diff --git a/.gitignore b/.gitignore
index f53fcf5e..d54e8860 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,6 @@ conf.json
# Generated files
test/tutorials/out
out/
+
+# Dotfile detritus
+.DS_Store
diff --git a/node_modules/catharsis/LICENSE b/node_modules/catharsis/LICENSE
index 9a454a29..6fdbb085 100644
--- a/node_modules/catharsis/LICENSE
+++ b/node_modules/catharsis/LICENSE
@@ -1,4 +1,5 @@
-Copyright (c) 2012-2013 Jeff Williams
+Copyright (c) 2014 Google Inc.
+Copyright (c) 2012-2014 Jeff Williams
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,
diff --git a/node_modules/catharsis/catharsis.js b/node_modules/catharsis/catharsis.js
index bb28e1d1..7899f3c2 100644
--- a/node_modules/catharsis/catharsis.js
+++ b/node_modules/catharsis/catharsis.js
@@ -8,6 +8,7 @@
'use strict';
+var describe = require('./lib/describe');
var parse = require('./lib/parser').parse;
var stringify = require('./lib/stringify');
@@ -21,6 +22,10 @@ var parsedTypeCache = {
htmlSafe: {}
};
+var descriptionCache = {
+ normal: {}
+};
+
function getTypeExpressionCache(options) {
if (options.useCache === false) {
return null;
@@ -41,6 +46,14 @@ function getParsedTypeCache(options) {
}
}
+function getDescriptionCache(options) {
+ if (options.useCache === false || options.links !== null || options.links !== undefined) {
+ return null;
+ } else {
+ return descriptionCache.normal;
+ }
+}
+
// can't return the original if any of the following are true:
// 1. restringification was requested
// 2. htmlSafe option was requested
@@ -52,6 +65,21 @@ function canReturnOriginalExpression(parsedType, options) {
Object.prototype.hasOwnProperty.call(parsedType, 'typeExpression');
}
+// Add non-enumerable properties to a result object, then freeze it.
+function prepareFrozenObject(obj, expr, options) {
+ Object.defineProperty(obj, 'jsdoc', {
+ value: options.jsdoc === true ? true : false
+ });
+
+ if (expr) {
+ Object.defineProperty(obj, 'typeExpression', {
+ value: expr
+ });
+ }
+
+ return Object.freeze(obj);
+}
+
function cachedParse(expr, options) {
var cache = getTypeExpressionCache(options);
var parsedType;
@@ -60,16 +88,7 @@ function cachedParse(expr, options) {
return cache[expr];
} else {
parsedType = parse(expr, options);
-
- Object.defineProperties(parsedType, {
- typeExpression: {
- value: expr
- },
- jsdoc: {
- value: options.jsdoc === true ? true : false
- }
- });
- parsedType = Object.freeze(parsedType);
+ parsedType = prepareFrozenObject(parsedType, expr, options);
if (cache) {
cache[expr] = parsedType;
@@ -94,6 +113,23 @@ function cachedStringify(parsedType, options) {
}
}
+function cachedDescribe(parsedType, options) {
+ var cache = getDescriptionCache(options);
+ var json;
+ var result;
+
+ if (cache) {
+ json = JSON.stringify(parsedType);
+ cache[json] = cache[json] || describe(parsedType, options);
+ return cache[json];
+ } else {
+ result = describe(parsedType, options);
+ result = prepareFrozenObject(result, null, options);
+
+ return result;
+ }
+}
+
function Catharsis() {
this.Types = require('./lib/types');
}
@@ -109,9 +145,10 @@ Catharsis.prototype.parse = function(typeExpr, options) {
};
Catharsis.prototype.stringify = function(parsedType, options) {
- options = options || {};
var result;
+ options = options || {};
+
result = cachedStringify(parsedType, options);
if (options.validate) {
this.parse(result, options);
@@ -120,4 +157,10 @@ Catharsis.prototype.stringify = function(parsedType, options) {
return result;
};
+Catharsis.prototype.describe = function(parsedType, options) {
+ options = options || {};
+
+ return cachedDescribe(parsedType, options);
+};
+
module.exports = new Catharsis();
diff --git a/node_modules/catharsis/lib/describe.js b/node_modules/catharsis/lib/describe.js
new file mode 100644
index 00000000..eeb777b0
--- /dev/null
+++ b/node_modules/catharsis/lib/describe.js
@@ -0,0 +1,532 @@
+'use strict';
+
+var _ = require('underscore-contrib');
+var fs = require('fs');
+var path = require('path');
+var stringify = require('./stringify');
+var Types = require('./types');
+var util = require('util');
+
+var DEFAULT_OPTIONS = {
+ language: 'en',
+ resources: {
+ en: JSON.parse(fs.readFileSync(path.join(__dirname, '../res/en.json'), 'utf8'))
+ }
+};
+
+// order matters for these!
+var FUNCTION_DETAILS = ['new', 'this'];
+var FUNCTION_DETAILS_VARIABLES = ['functionNew', 'functionThis'];
+var MODIFIERS = ['optional', 'nullable', 'repeatable'];
+
+var TEMPLATE_VARIABLES = [
+ 'application',
+ 'codeTagClose',
+ 'codeTagOpen',
+ 'element',
+ 'field',
+ 'functionNew',
+ 'functionParams',
+ 'functionReturns',
+ 'functionThis',
+ 'keyApplication',
+ 'name',
+ 'nullable',
+ 'optional',
+ 'param',
+ 'prefix',
+ 'repeatable',
+ 'suffix',
+ 'type'
+];
+
+var FORMATS = {
+ EXTENDED: 'extended',
+ SIMPLE: 'simple'
+};
+
+function makeTagOpen(codeTag, codeClass) {
+ var tagOpen = '';
+ var tags = codeTag ? codeTag.split(' ') : [];
+
+ tags.forEach(function(tag) {
+ var tagClass = codeClass ? util.format(' class="%s"', codeClass) : '';
+ tagOpen += util.format('<%s%s>', tag, tagClass);
+ });
+
+ return tagOpen;
+}
+
+function makeTagClose(codeTag) {
+ var tagClose = '';
+ var tags = codeTag ? codeTag.split(' ') : [];
+
+ tags.reverse();
+ tags.forEach(function(tag) {
+ tagClose += util.format('%s>', tag);
+ });
+
+ return tagClose;
+}
+
+function Result() {
+ this.description = '';
+ this.modifiers = {
+ functionNew: '',
+ functionThis: '',
+ optional: '',
+ nullable: '',
+ repeatable: ''
+ };
+ this.returns = '';
+}
+
+function Context(props) {
+ var self = this;
+
+ props = props || {};
+
+ TEMPLATE_VARIABLES.forEach(function(variable) {
+ self[variable] = props[variable] || '';
+ });
+}
+
+function Describer(opts) {
+ var options;
+
+ this._useLongFormat = true;
+ options = this._options = _.defaults(opts || {}, DEFAULT_OPTIONS);
+ this._stringifyOptions = _.defaults(options, {_ignoreModifiers: true});
+
+ // use a dictionary, not a Context object, so we can more easily merge this into Context objects
+ this._i18nContext = {
+ codeTagClose: makeTagClose(options.codeTag),
+ codeTagOpen: makeTagOpen(options.codeTag, options.codeClass)
+ };
+
+ // templates start out as strings; we lazily replace them with template functions
+ this._templates = options.resources[options.language];
+ if (!this._templates) {
+ throw new Error('I18N resources are not available for the language ' + options.language);
+ }
+}
+
+function modifierKind(useLongFormat) {
+ return useLongFormat ? FORMATS.EXTENDED : FORMATS.SIMPLE;
+}
+
+function buildModifierStrings(describer, modifiers, type, useLongFormat) {
+ var modifierStrings = {};
+ var result = {};
+
+ modifiers.forEach(function(modifier) {
+ var key = modifierKind(useLongFormat);
+ var modifierStrings = describer[modifier](type[modifier]);
+
+ result[modifier] = modifierStrings[key];
+ });
+
+ return result;
+}
+
+function addModifiers(describer, context, result, type, useLongFormat) {
+ var keyPrefix = 'modifiers.' + modifierKind(useLongFormat);
+ var modifiers = buildModifierStrings(describer, MODIFIERS, type, useLongFormat);
+
+ MODIFIERS.forEach(function(modifier) {
+ var modifierText = modifiers[modifier] || '';
+
+ result.modifiers[modifier] = modifierText;
+ if (!useLongFormat) {
+ context[modifier] = modifierText;
+ }
+ });
+
+ context.prefix = describer._translate(keyPrefix + '.prefix', context);
+ context.suffix = describer._translate(keyPrefix + '.suffix', context);
+}
+
+function addFunctionModifiers(describer, context, result, type, useLongFormat) {
+ var functionDetails = buildModifierStrings(describer, FUNCTION_DETAILS, type, useLongFormat);
+ var kind = modifierKind(useLongFormat);
+ var strings = [];
+
+ FUNCTION_DETAILS.forEach(function(functionDetail, i) {
+ var functionExtraInfo = functionDetails[functionDetail] || '';
+ var functionDetailsVariable = FUNCTION_DETAILS_VARIABLES[i];
+
+ result.modifiers[functionDetailsVariable] = functionExtraInfo;
+ if (!useLongFormat) {
+ context[functionDetailsVariable] += functionExtraInfo;
+ }
+ });
+}
+
+// Replace 2+ whitespace characters with a single whitespace character.
+function collapseSpaces(string) {
+ return string.replace(/(\s)+/g, '$1');
+}
+
+Describer.prototype._stringify = function(type, typeString, useLongFormat) {
+ var context = new Context({
+ type: typeString || stringify(type, this._stringifyOptions)
+ });
+ var result = new Result();
+
+ addModifiers(this, context, result, type, useLongFormat);
+ result.description = this._translate('type', context).trim();
+
+ return result;
+};
+
+Describer.prototype._translate = function(key, context) {
+ var result;
+ var templateFunction = _.getPath(this._templates, key);
+
+ context = context || new Context();
+
+ if (templateFunction === undefined) {
+ throw new Error(util.format('The template %s does not exist for the language %s', key,
+ this._options.language));
+ }
+
+ // compile and cache the template function if necessary
+ if (typeof templateFunction === 'string') {
+ // force the templates to use the `context` object
+ templateFunction = templateFunction.replace(/\<\%\= /g, '<%= context.');
+ templateFunction = _.template(templateFunction, null, {variable: 'context'});
+ _.setPath(this._templates, templateFunction, key);
+ }
+
+ result = (templateFunction(_.extend(context, this._i18nContext)) || '')
+ // strip leading spaces
+ .replace(/^\s+/, '');
+ result = collapseSpaces(result);
+
+ return result;
+};
+
+Describer.prototype._modifierHelper = function(key, modifierPrefix, context) {
+ modifierPrefix = modifierPrefix || '';
+
+ return {
+ extended: key ?
+ this._translate(util.format('%s.%s.%s', modifierPrefix, FORMATS.EXTENDED, key),
+ context) :
+ '',
+ simple: key ?
+ this._translate(util.format('%s.%s.%s', modifierPrefix, FORMATS.SIMPLE, key), context) :
+ ''
+ };
+};
+
+Describer.prototype._translateModifier = function(key, context) {
+ return this._modifierHelper(key, 'modifiers', context);
+};
+
+Describer.prototype._translateFunctionModifier = function(key, context) {
+ return this._modifierHelper(key, 'function', context);
+};
+
+function getApplicationKey(applications) {
+ if (applications.length === 1) {
+ return 'array';
+ } else if (/[Ss]tring/.test(applications[0].name)) {
+ // object with string keys
+ return 'object';
+ } else {
+ // object with non-string keys
+ return 'objectNonString';
+ }
+}
+
+Describer.prototype.application = function(type, useLongFormat) {
+ var applications = type.applications.slice(0);
+ var context = new Context();
+ var key = 'application.' + getApplicationKey(applications);
+ var result = new Result();
+ var self = this;
+
+ addModifiers(this, context, result, type, useLongFormat);
+
+ context.application = this.type(applications.pop()).description;
+ context.keyApplication = applications.length ? this.type(applications.pop()).description : '';
+
+ result.description = this._translate(key, context).trim();
+
+ return result;
+};
+
+function reduceMultiple(context, keyName, contextName, translate, previous, current, index, items) {
+ var key =
+ index === 0 ? '.first.many' :
+ index === (items.length - 1) ? '.last.many' :
+ '.middle.many';
+
+ key = keyName + key;
+ context[contextName] = items[index];
+
+ return previous + translate(key, context);
+}
+
+Describer.prototype.elements = function(type, useLongFormat) {
+ var context = new Context();
+ var items = type.elements.slice(0);
+ var result = new Result();
+
+ addModifiers(this, context, result, type, useLongFormat);
+ result.description = this._combineMultiple(items, context, 'union', 'element', useLongFormat);
+
+ return result;
+};
+
+Describer.prototype['new'] = function(funcNew) {
+ var context = new Context({'functionNew': this.type(funcNew).description});
+ var key = funcNew ? 'new' : '';
+
+ return this._translateFunctionModifier(key, context);
+};
+
+Describer.prototype.nullable = function(nullable) {
+ var key = nullable === true ? 'nullable' :
+ nullable === false ? 'nonNullable' :
+ '';
+
+ return this._translateModifier(key);
+};
+
+Describer.prototype.optional = function(optional) {
+ var key = (optional === true) ? 'optional' : '';
+
+ return this._translateModifier(key);
+};
+
+Describer.prototype.repeatable = function(repeatable) {
+ var key = (repeatable === true) ? 'repeatable' : '';
+
+ return this._translateModifier(key);
+};
+
+Describer.prototype._combineMultiple = function(items, context, keyName, contextName,
+ useLongFormat) {
+ var result = new Result();
+ var self = this;
+ var strings;
+
+ strings = typeof items[0] === 'string' ?
+ items.slice(0) :
+ items.map(function(item) {
+ return self.type(item).description;
+ });
+
+ switch(strings.length) {
+ case 0:
+ // falls through
+ case 1:
+ context[contextName] = strings[0] || '';
+ result.description = this._translate(keyName + '.first.one', context);
+ break;
+ case 2:
+ strings.forEach(function(item, idx) {
+ var key = keyName + (idx === 0 ? '.first' : '.last' ) + '.two';
+
+ context[contextName] = item;
+ result.description += self._translate(key, context);
+ });
+ break;
+ default:
+ result.description = strings.reduce(reduceMultiple.bind(null, context, keyName,
+ contextName, this._translate.bind(this)), '');
+ }
+
+ return result.description.trim();
+};
+
+Describer.prototype.params = function(params, functionContext) {
+ var context = new Context();
+ var result = new Result();
+ var self = this;
+ var strings;
+
+ // TODO: this hardcodes the order and placement of functionNew and functionThis; need to move
+ // this to the template (and also track whether to put a comma after the last modifier)
+ functionContext = functionContext || {};
+ params = params || [];
+ strings = params.map(function(param) {
+ return self.type(param).description;
+ });
+
+ if (functionContext.functionThis) {
+ strings.unshift(functionContext.functionThis);
+ }
+ if (functionContext.functionNew) {
+ strings.unshift(functionContext.functionNew);
+ }
+ result.description = this._combineMultiple(strings, context, 'params', 'param', false);
+
+ return result;
+};
+
+Describer.prototype['this'] = function(funcThis) {
+ var context = new Context({'functionThis': this.type(funcThis).description});
+ var key = funcThis ? 'this' : '';
+
+ return this._translateFunctionModifier(key, context);
+};
+
+Describer.prototype.type = function(type, useLongFormat) {
+ var result = new Result();
+
+ if (useLongFormat === undefined) {
+ useLongFormat = this._useLongFormat;
+ }
+ // ensure we don't use the long format for inner types
+ this._useLongFormat = false;
+
+ if (!type) {
+ return result;
+ }
+
+ switch(type.type) {
+ case Types.AllLiteral:
+ result = this._stringify(type, this._translate('all'), useLongFormat);
+ break;
+ case Types.FunctionType:
+ result = this._signature(type, useLongFormat);
+ break;
+ case Types.NameExpression:
+ result = this._stringify(type, null, useLongFormat);
+ break;
+ case Types.NullLiteral:
+ result = this._stringify(type, this._translate('null'), useLongFormat);
+ break;
+ case Types.RecordType:
+ result = this._record(type, useLongFormat);
+ break;
+ case Types.TypeApplication:
+ result = this.application(type, useLongFormat);
+ break;
+ case Types.TypeUnion:
+ result = this.elements(type, useLongFormat);
+ break;
+ case Types.UndefinedLiteral:
+ result = this._stringify(type, this._translate('undefined'), useLongFormat);
+ break;
+ case Types.UnknownLiteral:
+ result = this._stringify(type, this._translate('unknown'), useLongFormat);
+ break;
+ default:
+ throw new Error('Unknown type: ' + JSON.stringify(type));
+ }
+
+ return result;
+};
+
+Describer.prototype._record = function(type, useLongFormat) {
+ var context = new Context();
+ var items;
+ var result = new Result();
+
+ items = this._recordFields(type.fields);
+
+ addModifiers(this, context, result, type, useLongFormat);
+ result.description = this._combineMultiple(items, context, 'record', 'field', useLongFormat);
+
+ return result;
+};
+
+Describer.prototype._recordFields = function(fields) {
+ var context = new Context();
+ var result = [];
+ var self = this;
+
+ if (!fields.length) {
+ return result;
+ }
+
+ result = fields.map(function(field) {
+ var key = 'field.' + (field.value ? 'typed' : 'untyped');
+
+ context.name = self.type(field.key).description;
+ if (field.value) {
+ context.type = self.type(field.value).description;
+ }
+
+ return self._translate(key, context);
+ });
+
+ return result;
+};
+
+Describer.prototype._addLinks = function(nameString) {
+ var linkClass = '';
+ var options = this._options;
+ var result = nameString;
+
+
+ if (options.links && Object.prototype.hasOwnProperty.call(options.links, nameString)) {
+ if (options.linkClass) {
+ linkClass = util.format(' class="%s"', options.linkClass);
+ }
+
+ nameString = util.format('%s', options.links[nameString], linkClass,
+ nameString);
+ }
+
+ return nameString;
+};
+
+Describer.prototype.result = function(type, useLongFormat) {
+ var context = new Context();
+ var description;
+ var key = 'function.' + modifierKind(useLongFormat) + '.returns';
+ var result = new Result();
+
+ context.type = this.type(type).description;
+
+ addModifiers(this, context, result, type, useLongFormat);
+ result.description = this._translate(key, context);
+
+ return result;
+};
+
+Describer.prototype._signature = function(type, useLongFormat) {
+ var context = new Context();
+ var functionModifiers;
+ var kind = modifierKind(useLongFormat);
+ var result = new Result();
+ var returns;
+ var self = this;
+
+ addModifiers(this, context, result, type, useLongFormat);
+ addFunctionModifiers(this, context, result, type, useLongFormat);
+
+ context.functionParams = this.params(type.params || [], context).description;
+
+ if (type.result) {
+ returns = this.result(type.result, useLongFormat);
+ if (useLongFormat) {
+ result.returns = returns.description;
+ } else {
+ context.functionReturns = returns.description;
+ }
+ }
+
+ result.description += this._translate('function.' + kind + '.signature', context).trim();
+
+ return result;
+};
+
+module.exports = function(type, options) {
+ var simple = new Describer(options).type(type, false);
+ var extended = new Describer(options).type(type);
+
+ [simple, extended].forEach(function(result) {
+ result.description = collapseSpaces(result.description.trim());
+ });
+
+ return {
+ simple: simple.description,
+ extended: extended
+ };
+};
diff --git a/node_modules/catharsis/lib/parser.js b/node_modules/catharsis/lib/parser.js
index ddf743fb..e7adee46 100644
--- a/node_modules/catharsis/lib/parser.js
+++ b/node_modules/catharsis/lib/parser.js
@@ -1,3 +1,4 @@
-module.exports=function(){function peg$subclass(child,parent){function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor}function SyntaxError(expected,found,offset,line,column){function buildMessage(expected,found){function stringEscape(s){function hex(ch){return ch.charCodeAt(0).toString(16).toUpperCase()}return s.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\x08/g,"\\b").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/[\x00-\x07\x0B\x0E\x0F]/g,function(ch){return"\\x0"+hex(ch)}).replace(/[\x10-\x1F\x80-\xFF]/g,function(ch){return"\\x"+hex(ch)}).replace(/[\u0180-\u0FFF]/g,function(ch){return"\\u0"+hex(ch)}).replace(/[\u1080-\uFFFF]/g,function(ch){return"\\u"+hex(ch)})}var expectedDesc,foundDesc;switch(expected.length){case 0:expectedDesc="end of input";break;case 1:expectedDesc=expected[0];break;default:expectedDesc=expected.slice(0,-1).join(", ")+" or "+expected[expected.length-1]}foundDesc=found?'"'+stringEscape(found)+'"':"end of input";return"Expected "+expectedDesc+" but "+foundDesc+" found."}this.expected=expected;this.found=found;this.offset=offset;this.line=line;this.column=column;this.name="SyntaxError";this.message=buildMessage(expected,found)}peg$subclass(SyntaxError,Error);function parse(input){var options=arguments.length>1?arguments[1]:{},peg$startRuleFunctions={TypeExpression:peg$parseTypeExpression},peg$startRuleFunction=peg$parseTypeExpression,peg$c0=null,peg$c1="",peg$c2=function(r,unk){var result=unk;if(r.repeatable){result=repeatable(result)}return result},peg$c3="?",peg$c4='"?"',peg$c5="!",peg$c6='"!"',peg$c7=function(r,prefix,expr){var result=expr;if(r.repeatable){result=repeatable(result)}return nullable(result,prefix)},peg$c8=function(expr,postfix){return nullable(expr,postfix)},peg$c9=function(lit,opt){var result=lit;if(opt.optional){result.optional=true}return result},peg$c10=function(lit){return repeatable(lit)},peg$c11="*",peg$c12='"*"',peg$c13=function(){return{type:Types.AllLiteral}},peg$c14=function(){return{type:Types.NullLiteral}},peg$c15=function(){return{type:Types.UndefinedLiteral}},peg$c16="...",peg$c17='"..."',peg$c18=function(){return{repeatable:true}},peg$c19="=",peg$c20='"="',peg$c21=function(){return{optional:true}},peg$c22="[]",peg$c23='"[]"',peg$c24=function(name){var result;if(!options.jsdoc){return null}result={type:Types.TypeApplication,expression:{type:Types.NameExpression,name:"Array"},applications:[name]};result.applications[0].type=Types.NameExpression;return result},peg$c25=function(exp,appl,opt){var result={};var nameExp={type:Types.NameExpression,name:exp.name};if(appl.length){result.type=Types.TypeApplication;result.expression=nameExp;result.applications=appl}else{result=nameExp}if(opt.optional){result.optional=true}return result},peg$c26=function(name){if(!options.jsdoc){return null}return name},peg$c27=function(t){return repeatable(t)},peg$c28=function(exp,opt){var result={type:Types.NameExpression,name:exp.name,reservedWord:true};if(opt.optional){result.optional=true}return result},peg$c29=".",peg$c30='"."',peg$c31="<",peg$c32='"<"',peg$c33=">",peg$c34='">"',peg$c35=function(sep,l){if(sep===""&&!options.jsdoc){return null}return l},peg$c36=[],peg$c37=",",peg$c38='","',peg$c39=function(expr,list){var result=[expr];for(var i=0,l=list.length;ipos){peg$cachedPos=0;peg$cachedPosDetails={line:1,column:1,seenCR:false}}peg$cachedPos=pos;advance(peg$cachedPosDetails,peg$cachedPos)}return peg$cachedPosDetails}function peg$fail(expected){if(peg$currPospeg$maxFailPos){peg$maxFailPos=peg$currPos;peg$maxFailExpected=[]}peg$maxFailExpected.push(expected)}function peg$cleanupExpected(expected){var i=0;expected.sort();while(ipeg$currPos){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c277)}}return s0}function peg$parseHexEscapeSequence(){var s0,s1,s2,s3,s4,s5;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===120){s1=peg$c275;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c276)}}if(s1!==null){s2=peg$currPos;s3=peg$currPos;s4=peg$parseHexDigit();if(s4!==null){s5=peg$parseHexDigit();if(s5!==null){s4=[s4,s5];s3=s4}else{peg$currPos=s3;s3=peg$c0}}else{peg$currPos=s3;s3=peg$c0}if(s3!==null){s3=input.substring(s2,peg$currPos)}s2=s3;if(s2!==null){peg$reportedPos=s0;s1=peg$c223(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parseLineContinuation(){var s0,s1,s2;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===92){s1=peg$c219;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c220)}}if(s1!==null){s2=peg$parseLineTerminatorSequence();if(s2!==null){peg$reportedPos=s0;s1=peg$c261(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parse_(){var s0,s1;peg$silentFails++;s0=[];s1=peg$parseWhitespace();while(s1!==null){s0.push(s1);s1=peg$parseWhitespace()}peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c278)}}return s0}function peg$parse__(){var s0,s1;peg$silentFails++;s0=peg$c1;peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c279)}}return s0}function peg$parseWhitespace(){var s0;if(peg$c280.test(input.charAt(peg$currPos))){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c281)}}if(s0===null){s0=peg$parseUnicodeZs()}return s0}var Types=require("./types");function repeatable(obj){obj.repeatable=true;return obj}function nullable(obj,modifier){if(modifier){obj.nullable=modifier==="?"?true:false}return obj}peg$result=peg$startRuleFunction();if(peg$result!==null&&peg$currPos===input.length){return peg$result}else{peg$cleanupExpected(peg$maxFailExpected);peg$reportedPos=Math.max(peg$currPos,peg$maxFailPos);throw new SyntaxError(peg$maxFailExpected,peg$reportedPos1?arguments[1]:{},peg$startRuleFunctions={TypeExpression:peg$parseTypeExpression},peg$startRuleFunction=peg$parseTypeExpression,peg$c0=null,peg$c1="",peg$c2=function(r,unk){var result=unk;if(r.repeatable){result=repeatable(result)}return result},peg$c3="?",peg$c4='"?"',peg$c5="!",peg$c6='"!"',peg$c7=function(r,prefix,expr){var result=expr;if(r.repeatable){result=repeatable(result)}return nullable(result,prefix)},peg$c8=function(expr,postfix){return nullable(expr,postfix)},peg$c9=function(prefix,expr){return nullable(expr,prefix)},peg$c10=function(lit,opt){var result=lit;if(opt.optional){result.optional=true}return result},peg$c11=function(lit){return repeatable(lit)},peg$c12="*",peg$c13='"*"',peg$c14=function(){return{type:Types.AllLiteral}},peg$c15=function(){return{type:Types.NullLiteral}},peg$c16=function(){return{type:Types.UndefinedLiteral}},peg$c17="...",peg$c18='"..."',peg$c19=function(){return{repeatable:true}},peg$c20="=",peg$c21='"="',peg$c22=function(){return{optional:true}},peg$c23="[]",peg$c24='"[]"',peg$c25=function(name){var result;if(!options.jsdoc){return null}result={type:Types.TypeApplication,expression:{type:Types.NameExpression,name:"Array"},applications:[name]};result.applications[0].type=Types.NameExpression;return result},peg$c26=function(exp,appl,opt){var result={};var nameExp={type:Types.NameExpression,name:exp.name};if(appl.length){result.type=Types.TypeApplication;result.expression=nameExp;result.applications=appl}else{result=nameExp}if(opt.optional){result.optional=true}return result},peg$c27=function(name){if(!options.jsdoc){return null}return name},peg$c28=function(t){return repeatable(t)},peg$c29=function(exp,opt){var result={type:Types.NameExpression,name:exp.name,reservedWord:true};if(opt.optional){result.optional=true}return result},peg$c30=".",peg$c31='"."',peg$c32="<",peg$c33='"<"',peg$c34=">",peg$c35='">"',peg$c36=function(sep,l){if(sep===""&&!options.jsdoc){return null}return l},peg$c37=[],peg$c38=",",peg$c39='","',peg$c40=function(expr,list){var result=[expr];for(var i=0,l=list.length;ipos){peg$cachedPos=0;peg$cachedPosDetails={line:1,column:1,seenCR:false}}peg$cachedPos=pos;advance(peg$cachedPosDetails,peg$cachedPos)}return peg$cachedPosDetails}function peg$fail(expected){if(peg$currPospeg$maxFailPos){peg$maxFailPos=peg$currPos;peg$maxFailExpected=[]}peg$maxFailExpected.push(expected)}function peg$cleanupExpected(expected){var i=0;expected.sort();while(ipeg$currPos){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c278)}}return s0}function peg$parseHexEscapeSequence(){var s0,s1,s2,s3,s4,s5;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===120){s1=peg$c276;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c277)}}if(s1!==null){s2=peg$currPos;s3=peg$currPos;s4=peg$parseHexDigit();if(s4!==null){s5=peg$parseHexDigit();if(s5!==null){s4=[s4,s5];s3=s4}else{peg$currPos=s3;s3=peg$c0}}else{peg$currPos=s3;s3=peg$c0}if(s3!==null){s3=input.substring(s2,peg$currPos)}s2=s3;if(s2!==null){peg$reportedPos=s0;s1=peg$c224(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parseLineContinuation(){var s0,s1,s2;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===92){s1=peg$c220;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c221)}}if(s1!==null){s2=peg$parseLineTerminatorSequence();if(s2!==null){peg$reportedPos=s0;s1=peg$c262(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parse_(){var s0,s1;peg$silentFails++;s0=[];s1=peg$parseWhitespace();while(s1!==null){s0.push(s1);s1=peg$parseWhitespace()}peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c279)}}return s0}function peg$parse__(){var s0,s1;peg$silentFails++;s0=peg$c1;peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c280)}}return s0}function peg$parseWhitespace(){var s0;if(peg$c281.test(input.charAt(peg$currPos))){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c282)}}if(s0===null){s0=peg$parseUnicodeZs()}return s0}var Types=require("./types");function repeatable(obj){obj.repeatable=true;return obj}function nullable(obj,modifier){if(modifier){obj.nullable=modifier==="?"?true:false}return obj}peg$result=peg$startRuleFunction();if(peg$result!==null&&peg$currPos===input.length){return peg$result
+}else{peg$cleanupExpected(peg$maxFailExpected);peg$reportedPos=Math.max(peg$currPos,peg$maxFailPos);throw new SyntaxError(peg$maxFailExpected,peg$reportedPos';
+ result += strings.join(', ') + '>';
return result;
};
Stringifier.prototype.elements = function(elements) {
- if (!elements) {
- return '';
- }
+ var result = '';
+ var strings = [];
- var result = [];
+ if (!elements) {
+ return result;
+ }
for (var i = 0, l = elements.length; i < l; i++) {
- result.push(this.type(elements[i]));
+ strings.push(this.type(elements[i]));
}
- return '(' + result.join('|') + ')';
+ result = '(' + strings.join('|') + ')';
+
+ return result;
};
Stringifier.prototype.name = function(name) {
@@ -74,30 +78,18 @@ Stringifier.prototype.optional = function(optional) {
};
Stringifier.prototype.params = function(params) {
+ var result = '';
+ var strings = [];
+
if (!params || params.length === 0) {
- return '';
+ return result;
}
- var result = [];
-
- var param;
for (var i = 0, l = params.length; i < l; i++) {
- result.push(this.type(params[i]));
+ strings.push(this.type(params[i]));
}
- return result.join(', ');
-};
-
-Stringifier.prototype.properties = function(props) {
- if (!props) {
- return '';
- }
-
- var result = [];
-
- for (var i = 0, l = props.length; i < l; i++) {
- result.push(this._formatNameAndType(props[i].name, props[i].type));
- }
+ result = strings.join(', ');
return result;
};
@@ -110,52 +102,48 @@ Stringifier.prototype['this'] = function(funcThis) {
return funcThis ? 'this:' + this.type(funcThis) : '';
};
-// TODO: refactor for clarity
Stringifier.prototype.type = function(type) {
- var result = '';
+ var typeString = '';
if (!type) {
- return result;
+ return typeString;
}
switch(type.type) {
case Types.AllLiteral:
- result += this._formatRepeatableAndNullable(type, '',
- this._formatNameAndType(type, '*'));
+ typeString = this._formatNameAndType(type, '*');
break;
case Types.FunctionType:
- result += this._signature(type);
+ typeString = this._signature(type);
break;
case Types.NullLiteral:
- result += this._formatRepeatableAndNullable(type, '',
- this._formatNameAndType(type, 'null'));
+ typeString = this._formatNameAndType(type, 'null');
break;
case Types.RecordType:
- result += this._formatRepeatableAndNullable(type, '', this._record(type));
+ typeString = this._record(type);
break;
case Types.TypeApplication:
- result += this._formatRepeatableAndNullable(type, '', this.type(type.expression));
- result += this.applications(type.applications);
+ typeString = this.type(type.expression) + this.applications(type.applications);
break;
case Types.UndefinedLiteral:
- result += this._formatRepeatableAndNullable(type, '',
- this._formatNameAndType(type, 'undefined'));
+ typeString = this._formatNameAndType(type, 'undefined');
break;
case Types.TypeUnion:
- result += this._formatRepeatableAndNullable(type, '', this.elements(type.elements));
+ typeString = this.elements(type.elements);
break;
case Types.UnknownLiteral:
- result += this._formatRepeatableAndNullable(type, '',
- this._formatNameAndType(type, '?'));
+ typeString = this._formatNameAndType(type, '?');
break;
default:
- result += this._formatRepeatableAndNullable(type, '', this._formatNameAndType(type));
+ typeString = this._formatNameAndType(type);
}
- // finally, optionality
- result += this.optional(type.optional);
+ // add optional/nullable/repeatable modifiers
+ if (!this._options._ignoreModifiers) {
+ typeString = this._addModifiers(type, typeString);
+ }
- return result;
+ return typeString;
};
Stringifier.prototype.stringify = Stringifier.prototype.type;
@@ -169,14 +157,14 @@ Stringifier.prototype._record = function(type) {
};
Stringifier.prototype._recordFields = function(fields) {
- if (!fields) {
- return '';
- }
+ var field;
+ var keyAndValue;
var result = [];
- var field;
- var keyAndValue;
+ if (!fields) {
+ return result;
+ }
for (var i = 0, l = fields.length; i < l; i++) {
field = fields[i];
@@ -196,52 +184,64 @@ function combineNameAndType(nameString, typeString) {
return nameString + separator + typeString;
}
-Stringifier.prototype._formatRepeatableAndNullable = function(type, nameString, typeString) {
+// Adds optional, nullable, and repeatable modifiers if necessary.
+Stringifier.prototype._addModifiers = function(type, typeString) {
+ var combined;
+
var open = '';
var close = '';
- var combined;
+ var optional = '';
if (type.repeatable) {
open = this._inFunctionSignatureParams ? '...[' : '...';
close = this._inFunctionSignatureParams ? ']' : '';
}
- combined = this.nullable(type.nullable) + combineNameAndType(nameString, typeString);
+ combined = this.nullable(type.nullable) + combineNameAndType('', typeString);
+ optional = this.optional(type.optional);
- return open + combined + close;
+ return open + combined + close + optional;
+};
+
+Stringifier.prototype._addLinks = function(nameString) {
+ var openTag;
+
+ var linkClass = '';
+ var options = this._options;
+
+ if (options.links && Object.prototype.hasOwnProperty.call(options.links, nameString)) {
+ if (options.linkClass) {
+ linkClass = ' class="' + options.linkClass + '"';
+ }
+
+ openTag = '';
+ nameString = openTag + nameString + '';
+ }
+
+ return nameString;
};
Stringifier.prototype._formatNameAndType = function(type, literal) {
var nameString = type.name || literal || '';
var typeString = type.type ? this.type(type.type) : '';
- var cssClass;
- var openTag;
- // replace the type with an HTML link if necessary
- if (this._options.links && Object.prototype.hasOwnProperty.call(this._options.links,
- nameString)) {
- cssClass = this._options.cssClass ? ' class="' + this._options.cssClass + '"' : '';
-
- openTag = '';
- nameString = openTag + nameString + '';
- }
+ nameString = this._addLinks(nameString);
return combineNameAndType(nameString, typeString);
};
Stringifier.prototype._signature = function(type) {
- var params = [];
var param;
- var result;
- var signatureBase;
+ var prop;
+ var signature;
+ var params = [];
// these go within the signature's parens, in this order
var props = [
'new',
'this',
'params'
];
- var prop;
this._inFunctionSignatureParams = true;
for (var i = 0, l = props.length; i < l; i++) {
@@ -253,11 +253,10 @@ Stringifier.prototype._signature = function(type) {
}
this._inFunctionSignatureParams = false;
- signatureBase = 'function(' + params.join(', ') + ')';
- result = this._formatRepeatableAndNullable(type, '', signatureBase);
- result += this.result(type.result);
+ signature = 'function(' + params.join(', ') + ')';
+ signature += this.result(type.result);
- return result;
+ return signature;
};
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/LICENSE b/node_modules/catharsis/node_modules/underscore-contrib/LICENSE
new file mode 100644
index 00000000..8e1f77b4
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013 Jeremy Ashkenas, Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+
+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.
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/index.js b/node_modules/catharsis/node_modules/underscore-contrib/index.js
new file mode 100644
index 00000000..5d4bc175
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/index.js
@@ -0,0 +1,16 @@
+require('./underscore.array.builders');
+require('./underscore.array.selectors');
+require('./underscore.collections.walk');
+require('./underscore.function.arity');
+require('./underscore.function.combinators');
+require('./underscore.function.dispatch');
+require('./underscore.function.iterators');
+require('./underscore.function.predicates');
+require('./underscore.object.builders');
+require('./underscore.object.selectors');
+require('./underscore.util.existential');
+require('./underscore.util.operators');
+require('./underscore.util.strings');
+require('./underscore.util.trampolines');
+
+module.exports = require('underscore');
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/package.json b/node_modules/catharsis/node_modules/underscore-contrib/package.json
new file mode 100644
index 00000000..41c958f5
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "underscore-contrib",
+ "version": "0.3.0",
+ "main": "index.js",
+ "dependencies": {
+ "underscore": "1.6.0"
+ },
+ "devDependencies": {
+ "grunt": "~0.4.1",
+ "grunt-contrib-concat": "0.3.0",
+ "grunt-contrib-uglify": "0.2.0",
+ "grunt-contrib-qunit": "~0.2.2",
+ "grunt-contrib-watch": "~0.5.3",
+ "grunt-contrib-jshint": "~0.6.4",
+ "grunt-docco": "~0.3.0",
+ "grunt-tocdoc": "~0.1.0",
+ "grunt-cli": "~0.1.11"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/documentcloud/underscore-contrib.git"
+ },
+ "license": "MIT",
+ "author": {
+ "name": "Fogus",
+ "email": "me@fogus.me",
+ "url": "http://www.fogus.me"
+ },
+ "scripts": {
+ "test": "node ./node_modules/grunt-cli/bin/grunt test"
+ },
+ "homepage": "https://github.com/documentcloud/underscore-contrib",
+ "description": "underscore-contrib ==================",
+ "bugs": {
+ "url": "https://github.com/documentcloud/underscore-contrib/issues"
+ },
+ "_id": "underscore-contrib@0.3.0",
+ "dist": {
+ "shasum": "665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7",
+ "tarball": "http://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz"
+ },
+ "_from": "underscore-contrib@~0.3.0",
+ "_npmVersion": "1.3.21",
+ "_npmUser": {
+ "name": "joshuacc",
+ "email": "joshua.clanton@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "fogus",
+ "email": "mefogus@gmail.com"
+ },
+ {
+ "name": "joshuacc",
+ "email": "joshua.clanton@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7",
+ "_resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.array.builders.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.array.builders.js
new file mode 100644
index 00000000..2ad8c562
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.array.builders.js
@@ -0,0 +1,197 @@
+// Underscore-contrib (underscore.array.builders.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ // Create quick reference variables for speed access to core prototypes.
+ var slice = Array.prototype.slice,
+ concat = Array.prototype.concat;
+
+ var existy = function(x) { return x != null; };
+
+ // Mixing in the array builders
+ // ----------------------------
+
+ _.mixin({
+ // Concatenates one or more arrays given as arguments. If given objects and
+ // scalars as arguments `cat` will plop them down in place in the result
+ // array. If given an `arguments` object, `cat` will treat it like an array
+ // and concatenate it likewise.
+ cat: function() {
+ return _.reduce(arguments, function(acc, elem) {
+ if (_.isArguments(elem)) {
+ return concat.call(acc, slice.call(elem));
+ }
+ else {
+ return concat.call(acc, elem);
+ }
+ }, []);
+ },
+
+ // 'Constructs' an array by putting an element at its front
+ cons: function(head, tail) {
+ return _.cat([head], tail);
+ },
+
+ // Takes an array and chunks it some number of times into
+ // sub-arrays of size n. Allows and optional padding array as
+ // the third argument to fill in the tail chunk when n is
+ // not sufficient to build chunks of the same size.
+ chunk: function(array, n, pad) {
+ var p = function(array) {
+ if (array == null) return [];
+
+ var part = _.take(array, n);
+
+ if (n === _.size(part)) {
+ return _.cons(part, p(_.drop(array, n)));
+ }
+ else {
+ return pad ? [_.take(_.cat(part, pad), n)] : [];
+ }
+ };
+
+ return p(array);
+ },
+
+ // Takes an array and chunks it some number of times into
+ // sub-arrays of size n. If the array given cannot fill the size
+ // needs of the final chunk then a smaller chunk is used
+ // for the last.
+ chunkAll: function(array, n, step) {
+ step = (step != null) ? step : n;
+
+ var p = function(array, n, step) {
+ if (_.isEmpty(array)) return [];
+
+ return _.cons(_.take(array, n),
+ p(_.drop(array, step), n, step));
+ };
+
+ return p(array, n, step);
+ },
+
+ // Maps a function over an array and concatenates all of the results.
+ mapcat: function(array, fun) {
+ return _.cat.apply(null, _.map(array, fun));
+ },
+
+ // Returns an array with some item between each element
+ // of a given array.
+ interpose: function(array, inter) {
+ if (!_.isArray(array)) throw new TypeError;
+ var sz = _.size(array);
+ if (sz === 0) return array;
+ if (sz === 1) return array;
+
+ return slice.call(_.mapcat(array, function(elem) {
+ return _.cons(elem, [inter]);
+ }), 0, -1);
+ },
+
+ // Weaves two or more arrays together
+ weave: function(/* args */) {
+ if (!_.some(arguments)) return [];
+ if (arguments.length == 1) return arguments[0];
+
+ return _.filter(_.flatten(_.zip.apply(null, arguments), true), function(elem) {
+ return elem != null;
+ });
+ },
+ interleave: _.weave,
+
+ // Returns an array of a value repeated a certain number of
+ // times.
+ repeat: function(t, elem) {
+ return _.times(t, function() { return elem; });
+ },
+
+ // Returns an array built from the contents of a given array repeated
+ // a certain number of times.
+ cycle: function(t, elems) {
+ return _.flatten(_.times(t, function() { return elems; }), true);
+ },
+
+ // Returns an array with two internal arrays built from
+ // taking an original array and spliting it at an index.
+ splitAt: function(array, index) {
+ return [_.take(array, index), _.drop(array, index)];
+ },
+
+ // Call a function recursively f(f(f(args))) until a second
+ // given function goes falsey. Expects a seed value to start.
+ iterateUntil: function(doit, checkit, seed) {
+ var ret = [];
+ var result = doit(seed);
+
+ while (checkit(result)) {
+ ret.push(result);
+ result = doit(result);
+ }
+
+ return ret;
+ },
+
+ // Takes every nth item from an array, returning an array of
+ // the results.
+ takeSkipping: function(array, n) {
+ var ret = [];
+ var sz = _.size(array);
+
+ if (n <= 0) return [];
+ if (n === 1) return array;
+
+ for(var index = 0; index < sz; index += n) {
+ ret.push(array[index]);
+ }
+
+ return ret;
+ },
+
+ // Returns an array of each intermediate stage of a call to
+ // a `reduce`-like function.
+ reductions: function(array, fun, init) {
+ var ret = [];
+ var acc = init;
+
+ _.each(array, function(v,k) {
+ acc = fun(acc, array[k]);
+ ret.push(acc);
+ });
+
+ return ret;
+ },
+
+ // Runs its given function on the index of the elements rather than
+ // the elements themselves, keeping all of the truthy values in the end.
+ keepIndexed: function(array, pred) {
+ return _.filter(_.map(_.range(_.size(array)), function(i) {
+ return pred(i, array[i]);
+ }),
+ existy);
+ },
+
+ // Accepts an array-like object (other than strings) as an argument and
+ // returns an array whose elements are in the reverse order. Unlike the
+ // built-in `Array.prototype.reverse` method, this does not mutate the
+ // original object. Note: attempting to use this method on a string will
+ // result in a `TypeError`, as it cannot properly reverse unicode strings.
+
+ reverseOrder: function(obj) {
+ if (typeof obj == 'string')
+ throw new TypeError('Strings cannot be reversed by _.reverseOrder');
+ return slice.call(obj).reverse();
+ }
+ });
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.array.selectors.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.array.selectors.js
new file mode 100644
index 00000000..53666648
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.array.selectors.js
@@ -0,0 +1,117 @@
+// Underscore-contrib (underscore.array.selectors.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ // Create quick reference variables for speed access to core prototypes.
+ var slice = Array.prototype.slice,
+ concat = Array.prototype.concat;
+
+ var existy = function(x) { return x != null; };
+ var truthy = function(x) { return (x !== false) && existy(x); };
+ var isSeq = function(x) { return (_.isArray(x)) || (_.isArguments(x)); };
+
+ // Mixing in the array selectors
+ // ----------------------------
+
+ _.mixin({
+ // Returns the second element of an array. Passing **n** will return all but
+ // the first of the head N values in the array. The **guard** check allows it
+ // to work with `_.map`.
+ second: function(array, n, guard) {
+ if (array == null) return void 0;
+ return (n != null) && !guard ? slice.call(array, 1, n) : array[1];
+ },
+
+ // Returns the third element of an array. Passing **n** will return all but
+ // the first two of the head N values in the array. The **guard** check allows it
+ // to work with `_.map`.
+ third: function(array, n, guard) {
+ if (array == null) return void 0;
+ return (n != null) && !guard ? slice.call(array, 2, n) : array[2];
+ },
+
+ // A function to get at an index into an array
+ nth: function(array, index, guard) {
+ if ((index != null) && !guard) return array[index];
+ },
+
+ // Takes all items in an array while a given predicate returns truthy.
+ takeWhile: function(array, pred) {
+ if (!isSeq(array)) throw new TypeError;
+
+ var sz = _.size(array);
+
+ for (var index = 0; index < sz; index++) {
+ if(!truthy(pred(array[index]))) {
+ break;
+ }
+ }
+
+ return _.take(array, index);
+ },
+
+ // Drops all items from an array while a given predicate returns truthy.
+ dropWhile: function(array, pred) {
+ if (!isSeq(array)) throw new TypeError;
+
+ var sz = _.size(array);
+
+ for (var index = 0; index < sz; index++) {
+ if(!truthy(pred(array[index])))
+ break;
+ }
+
+ return _.drop(array, index);
+ },
+
+ // Returns an array with two internal arrays built from
+ // taking an original array and spliting it at the index
+ // where a given function goes falsey.
+ splitWith: function(array, pred) {
+ return [_.takeWhile(array, pred), _.dropWhile(array, pred)];
+ },
+
+ // Takes an array and partitions it as the given predicate changes
+ // truth sense.
+ partitionBy: function(array, fun){
+ if (_.isEmpty(array) || !existy(array)) return [];
+
+ var fst = _.first(array);
+ var fstVal = fun(fst);
+ var run = concat.call([fst], _.takeWhile(_.rest(array), function(e) {
+ return _.isEqual(fstVal, fun(e));
+ }));
+
+ return concat.call([run], _.partitionBy(_.drop(array, _.size(run)), fun));
+ },
+
+ // Returns the 'best' value in an array based on the result of a
+ // given function.
+ best: function(array, fun) {
+ return _.reduce(array, function(x, y) {
+ return fun(x, y) ? x : y;
+ });
+ },
+
+ // Returns an array of existy results of a function over an source array.
+ keep: function(array, fun) {
+ if (!isSeq(array)) throw new TypeError("expected an array as the first argument");
+
+ return _.filter(_.map(array, function(e) {
+ return fun(e);
+ }), existy);
+ }
+ });
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.collections.walk.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.collections.walk.js
new file mode 100644
index 00000000..963b146f
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.collections.walk.js
@@ -0,0 +1,196 @@
+// Underscore-contrib (underscore.collections.walk.js 0.3.0)
+// (c) 2013 Patrick Dubroy
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ // An internal object that can be returned from a visitor function to
+ // prevent a top-down walk from walking subtrees of a node.
+ var stopRecursion = {};
+
+ // An internal object that can be returned from a visitor function to
+ // cause the walk to immediately stop.
+ var stopWalk = {};
+
+ var notTreeError = 'Not a tree: same object found in two different branches';
+
+ // Implements the default traversal strategy: if `obj` is a DOM node, walk
+ // its DOM children; otherwise, walk all the objects it references.
+ function defaultTraversal(obj) {
+ return _.isElement(obj) ? obj.children : obj;
+ }
+
+ // Walk the tree recursively beginning with `root`, calling `beforeFunc`
+ // before visiting an objects descendents, and `afterFunc` afterwards.
+ // If `collectResults` is true, the last argument to `afterFunc` will be a
+ // collection of the results of walking the node's subtrees.
+ function walkImpl(root, traversalStrategy, beforeFunc, afterFunc, context, collectResults) {
+ var visited = [];
+ return (function _walk(value, key, parent) {
+ // Keep track of objects that have been visited, and throw an exception
+ // when trying to visit the same object twice.
+ if (_.isObject(value)) {
+ if (visited.indexOf(value) >= 0) throw new TypeError(notTreeError);
+ visited.push(value);
+ }
+
+ if (beforeFunc) {
+ var result = beforeFunc.call(context, value, key, parent);
+ if (result === stopWalk) return stopWalk;
+ if (result === stopRecursion) return;
+ }
+
+ var subResults;
+ var target = traversalStrategy(value);
+ if (_.isObject(target) && !_.isEmpty(target)) {
+ // If collecting results from subtrees, collect them in the same shape
+ // as the parent node.
+ if (collectResults) subResults = _.isArray(value) ? [] : {};
+
+ var stop = _.any(target, function(obj, key) {
+ var result = _walk(obj, key, value);
+ if (result === stopWalk) return true;
+ if (subResults) subResults[key] = result;
+ });
+ if (stop) return stopWalk;
+ }
+ if (afterFunc) return afterFunc.call(context, value, key, parent, subResults);
+ })(root);
+ }
+
+ // Internal helper providing the implementation for `pluck` and `pluckRec`.
+ function pluck(obj, propertyName, recursive) {
+ var results = [];
+ this.preorder(obj, function(value, key) {
+ if (!recursive && key == propertyName)
+ return stopRecursion;
+ if (_.has(value, propertyName))
+ results[results.length] = value[propertyName];
+ });
+ return results;
+ }
+
+ var exports = {
+ // Performs a preorder traversal of `obj` and returns the first value
+ // which passes a truth test.
+ find: function(obj, visitor, context) {
+ var result;
+ this.preorder(obj, function(value, key, parent) {
+ if (visitor.call(context, value, key, parent)) {
+ result = value;
+ return stopWalk;
+ }
+ }, context);
+ return result;
+ },
+
+ // Recursively traverses `obj` and returns all the elements that pass a
+ // truth test. `strategy` is the traversal function to use, e.g. `preorder`
+ // or `postorder`.
+ filter: function(obj, strategy, visitor, context) {
+ var results = [];
+ if (obj == null) return results;
+ strategy(obj, function(value, key, parent) {
+ if (visitor.call(context, value, key, parent)) results.push(value);
+ }, null, this._traversalStrategy);
+ return results;
+ },
+
+ // Recursively traverses `obj` and returns all the elements for which a
+ // truth test fails.
+ reject: function(obj, strategy, visitor, context) {
+ return this.filter(obj, strategy, function(value, key, parent) {
+ return !visitor.call(context, value, key, parent);
+ });
+ },
+
+ // Produces a new array of values by recursively traversing `obj` and
+ // mapping each value through the transformation function `visitor`.
+ // `strategy` is the traversal function to use, e.g. `preorder` or
+ // `postorder`.
+ map: function(obj, strategy, visitor, context) {
+ var results = [];
+ strategy(obj, function(value, key, parent) {
+ results[results.length] = visitor.call(context, value, key, parent);
+ }, null, this._traversalStrategy);
+ return results;
+ },
+
+ // Return the value of properties named `propertyName` reachable from the
+ // tree rooted at `obj`. Results are not recursively searched; use
+ // `pluckRec` for that.
+ pluck: function(obj, propertyName) {
+ return pluck.call(this, obj, propertyName, false);
+ },
+
+ // Version of `pluck` which recursively searches results for nested objects
+ // with a property named `propertyName`.
+ pluckRec: function(obj, propertyName) {
+ return pluck.call(this, obj, propertyName, true);
+ },
+
+ // Recursively traverses `obj` in a depth-first fashion, invoking the
+ // `visitor` function for each object only after traversing its children.
+ // `traversalStrategy` is intended for internal callers, and is not part
+ // of the public API.
+ postorder: function(obj, visitor, context, traversalStrategy) {
+ traversalStrategy = traversalStrategy || this._traversalStrategy;
+ walkImpl(obj, traversalStrategy, null, visitor, context);
+ },
+
+ // Recursively traverses `obj` in a depth-first fashion, invoking the
+ // `visitor` function for each object before traversing its children.
+ // `traversalStrategy` is intended for internal callers, and is not part
+ // of the public API.
+ preorder: function(obj, visitor, context, traversalStrategy) {
+ traversalStrategy = traversalStrategy || this._traversalStrategy;
+ walkImpl(obj, traversalStrategy, visitor, null, context);
+ },
+
+ // Builds up a single value by doing a post-order traversal of `obj` and
+ // calling the `visitor` function on each object in the tree. For leaf
+ // objects, the `memo` argument to `visitor` is the value of the `leafMemo`
+ // argument to `reduce`. For non-leaf objects, `memo` is a collection of
+ // the results of calling `reduce` on the object's children.
+ reduce: function(obj, visitor, leafMemo, context) {
+ var reducer = function(value, key, parent, subResults) {
+ return visitor(subResults || leafMemo, value, key, parent);
+ };
+ return walkImpl(obj, this._traversalStrategy, null, reducer, context, true);
+ }
+ };
+
+ // Set up aliases to match those in underscore.js.
+ exports.collect = exports.map;
+ exports.detect = exports.find;
+ exports.select = exports.filter;
+
+ // Returns an object containing the walk functions. If `traversalStrategy`
+ // is specified, it is a function determining how objects should be
+ // traversed. Given an object, it returns the object to be recursively
+ // walked. The default strategy is equivalent to `_.identity` for regular
+ // objects, and for DOM nodes it returns the node's DOM children.
+ _.walk = function(traversalStrategy) {
+ var walker = _.clone(exports);
+
+ // Bind all of the public functions in the walker to itself. This allows
+ // the traversal strategy to be dynamically scoped.
+ _.bindAll.apply(null, [walker].concat(_.keys(walker)));
+
+ walker._traversalStrategy = traversalStrategy || defaultTraversal;
+ return walker;
+ };
+
+ // Use `_.walk` as a namespace to hold versions of the walk functions which
+ // use the default traversal strategy.
+ _.extend(_.walk, _.walk());
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.arity.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.arity.js
new file mode 100644
index 00000000..5f123d28
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.arity.js
@@ -0,0 +1,200 @@
+// Underscore-contrib (underscore.function.arity.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ function enforcesUnary (fn) {
+ return function mustBeUnary () {
+ if (arguments.length === 1) {
+ return fn.apply(this, arguments);
+ }
+ else throw new RangeError('Only a single argument may be accepted.');
+
+ };
+ }
+
+ // Curry
+ // -------
+ var curry = (function () {
+ function collectArgs(func, that, argCount, args, newArg, reverse) {
+ if (reverse === true) {
+ args.unshift(newArg);
+ } else {
+ args.push(newArg);
+ }
+ if (args.length == argCount) {
+ return func.apply(that, args);
+ } else {
+ return enforcesUnary(function () {
+ return collectArgs(func, that, argCount, args.slice(0), arguments[0], reverse);
+ });
+ }
+ }
+ return function curry (func, reverse) {
+ var that = this;
+ return enforcesUnary(function () {
+ return collectArgs(func, that, func.length, [], arguments[0], reverse);
+ });
+ };
+ }());
+
+ // Enforce Arity
+ // --------------------
+ var enforce = (function () {
+ var CACHE = [];
+ return function enforce (func) {
+ if (typeof func !== 'function') {
+ throw new Error('Argument 1 must be a function.');
+ }
+ var funcLength = func.length;
+ if (CACHE[funcLength] === undefined) {
+ CACHE[funcLength] = function (enforceFunc) {
+ return function () {
+ if (arguments.length !== funcLength) {
+ throw new RangeError(funcLength + ' arguments must be applied.');
+ }
+ return enforceFunc.apply(this, arguments);
+ };
+ };
+ }
+ return CACHE[funcLength](func);
+ };
+ }());
+
+ // Mixing in the arity functions
+ // -----------------------------
+
+ _.mixin({
+ // ### Fixed arguments
+
+ // Fixes the arguments to a function based on the parameter template defined by
+ // the presence of values and the `_` placeholder.
+ fix: function(fun) {
+ var fixArgs = _.rest(arguments);
+
+ var f = function() {
+ var args = fixArgs.slice();
+ var arg = 0;
+
+ for ( var i = 0; i < (args.length || arg < arguments.length); i++ ) {
+ if ( args[i] === _ ) {
+ args[i] = arguments[arg++];
+ }
+ }
+
+ return fun.apply(null, args);
+ };
+
+ f._original = fun;
+
+ return f;
+ },
+
+ unary: function (fun) {
+ return function unary (a) {
+ return fun.call(this, a);
+ };
+ },
+
+ binary: function (fun) {
+ return function binary (a, b) {
+ return fun.call(this, a, b);
+ };
+ },
+
+ ternary: function (fun) {
+ return function ternary (a, b, c) {
+ return fun.call(this, a, b, c);
+ };
+ },
+
+ quaternary: function (fun) {
+ return function quaternary (a, b, c, d) {
+ return fun.call(this, a, b, c, d);
+ };
+ },
+
+ // Flexible curry function with strict arity.
+ // Argument application left to right.
+ // source: https://github.com/eborden/js-curry
+ curry: curry,
+
+ // Flexible right to left curry with strict arity.
+ rCurry: function (func) {
+ return curry.call(this, func, true);
+ },
+
+
+ curry2: function (fun) {
+ return enforcesUnary(function curried (first) {
+ return enforcesUnary(function (last) {
+ return fun.call(this, first, last);
+ });
+ });
+ },
+
+ curry3: function (fun) {
+ return enforcesUnary(function (first) {
+ return enforcesUnary(function (second) {
+ return enforcesUnary(function (last) {
+ return fun.call(this, first, second, last);
+ });
+ });
+ });
+ },
+
+ // reverse currying for functions taking two arguments.
+ rcurry2: function (fun) {
+ return enforcesUnary(function (last) {
+ return enforcesUnary(function (first) {
+ return fun.call(this, first, last);
+ });
+ });
+ },
+
+ rcurry3: function (fun) {
+ return enforcesUnary(function (last) {
+ return enforcesUnary(function (second) {
+ return enforcesUnary(function (first) {
+ return fun.call(this, first, second, last);
+ });
+ });
+ });
+ },
+ // Dynamic decorator to enforce function arity and defeat varargs.
+ enforce: enforce
+ });
+
+ _.arity = (function () {
+ // Allow 'new Function', as that is currently the only reliable way
+ // to manipulate function.length
+ /* jshint -W054 */
+ var FUNCTIONS = {};
+ return function arity (numberOfArgs, fun) {
+ if (FUNCTIONS[numberOfArgs] == null) {
+ var parameters = new Array(numberOfArgs);
+ for (var i = 0; i < numberOfArgs; ++i) {
+ parameters[i] = "__" + i;
+ }
+ var pstr = parameters.join();
+ var code = "return function ("+pstr+") { return fun.apply(this, arguments); };";
+ FUNCTIONS[numberOfArgs] = new Function(['fun'], code);
+ }
+ if (fun == null) {
+ return function (fun) { return arity(numberOfArgs, fun); };
+ }
+ else return FUNCTIONS[numberOfArgs](fun);
+ };
+ })();
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.combinators.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.combinators.js
new file mode 100644
index 00000000..b4608996
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.combinators.js
@@ -0,0 +1,266 @@
+// Underscore-contrib (underscore.function.combinators.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ var existy = function(x) { return x != null; };
+ var truthy = function(x) { return (x !== false) && existy(x); };
+ var __reverse = [].reverse;
+ var __slice = [].slice;
+ var __map = [].map;
+ var curry2 = function (fun) {
+ return function curried (first, optionalLast) {
+ if (arguments.length === 1) {
+ return function (last) {
+ return fun(first, last);
+ };
+ }
+ else return fun(first, optionalLast);
+ };
+ };
+
+ // n.b. depends on underscore.function.arity.js
+
+ // Takes a target function and a mapping function. Returns a function
+ // that applies the mapper to its arguments before evaluating the body.
+ function baseMapArgs (fun, mapFun) {
+ return _.arity(fun.length, function () {
+ return fun.apply(this, __map.call(arguments, mapFun));
+ });
+ }
+
+ // Mixing in the combinator functions
+ // ----------------------------------
+
+ _.mixin({
+ // Provide "always" alias for backwards compatibility
+ always: _.constant,
+
+ // Takes some number of functions, either as an array or variadically
+ // and returns a function that takes some value as its first argument
+ // and runs it through a pipeline of the original functions given.
+ pipeline: function(/*, funs */){
+ var funs = (_.isArray(arguments[0])) ? arguments[0] : arguments;
+
+ return function(seed) {
+ return _.reduce(funs,
+ function(l,r) { return r(l); },
+ seed);
+ };
+ },
+
+ // Composes a bunch of predicates into a single predicate that
+ // checks all elements of an array for conformance to all of the
+ // original predicates.
+ conjoin: function(/* preds */) {
+ var preds = arguments;
+
+ return function(array) {
+ return _.every(array, function(e) {
+ return _.every(preds, function(p) {
+ return p(e);
+ });
+ });
+ };
+ },
+
+ // Composes a bunch of predicates into a single predicate that
+ // checks all elements of an array for conformance to any of the
+ // original predicates.
+ disjoin: function(/* preds */) {
+ var preds = arguments;
+
+ return function(array) {
+ return _.some(array, function(e) {
+ return _.some(preds, function(p) {
+ return p(e);
+ });
+ });
+ };
+ },
+
+ // Takes a predicate-like and returns a comparator (-1,0,1).
+ comparator: function(fun) {
+ return function(x, y) {
+ if (truthy(fun(x, y)))
+ return -1;
+ else if (truthy(fun(y, x)))
+ return 1;
+ else
+ return 0;
+ };
+ },
+
+ // Returns a function that reverses the sense of a given predicate-like.
+ complement: function(pred) {
+ return function() {
+ return !pred.apply(this, arguments);
+ };
+ },
+
+ // Takes a function expecting varargs and
+ // returns a function that takes an array and
+ // uses its elements as the args to the original
+ // function
+ splat: function(fun) {
+ return function(array) {
+ return fun.apply(this, array);
+ };
+ },
+
+ // Takes a function expecting an array and returns
+ // a function that takes varargs and wraps all
+ // in an array that is passed to the original function.
+ unsplat: function(fun) {
+ var funLength = fun.length;
+
+ if (funLength < 1) {
+ return fun;
+ }
+ else if (funLength === 1) {
+ return function () {
+ return fun.call(this, __slice.call(arguments, 0));
+ };
+ }
+ else {
+ return function () {
+ var numberOfArgs = arguments.length,
+ namedArgs = __slice.call(arguments, 0, funLength - 1),
+ numberOfMissingNamedArgs = Math.max(funLength - numberOfArgs - 1, 0),
+ argPadding = new Array(numberOfMissingNamedArgs),
+ variadicArgs = __slice.call(arguments, fun.length - 1);
+
+ return fun.apply(this, namedArgs.concat(argPadding).concat([variadicArgs]));
+ };
+ }
+ },
+
+ // Same as unsplat, but the rest of the arguments are collected in the
+ // first parameter, e.g. unsplatl( function (args, callback) { ... ]})
+ unsplatl: function(fun) {
+ var funLength = fun.length;
+
+ if (funLength < 1) {
+ return fun;
+ }
+ else if (funLength === 1) {
+ return function () {
+ return fun.call(this, __slice.call(arguments, 0));
+ };
+ }
+ else {
+ return function () {
+ var numberOfArgs = arguments.length,
+ namedArgs = __slice.call(arguments, Math.max(numberOfArgs - funLength + 1, 0)),
+ variadicArgs = __slice.call(arguments, 0, Math.max(numberOfArgs - funLength + 1, 0));
+
+ return fun.apply(this, [variadicArgs].concat(namedArgs));
+ };
+ }
+ },
+
+ // map the arguments of a function
+ mapArgs: curry2(baseMapArgs),
+
+ // Returns a function that returns an array of the calls to each
+ // given function for some arguments.
+ juxt: function(/* funs */) {
+ var funs = arguments;
+
+ return function(/* args */) {
+ var args = arguments;
+ return _.map(funs, function(f) {
+ return f.apply(this, args);
+ }, this);
+ };
+ },
+
+ // Returns a function that protects a given function from receiving
+ // non-existy values. Each subsequent value provided to `fnull` acts
+ // as the default to the original function should a call receive non-existy
+ // values in the defaulted arg slots.
+ fnull: function(fun /*, defaults */) {
+ var defaults = _.rest(arguments);
+
+ return function(/*args*/) {
+ var args = _.toArray(arguments);
+ var sz = _.size(defaults);
+
+ for(var i = 0; i < sz; i++) {
+ if (!existy(args[i]))
+ args[i] = defaults[i];
+ }
+
+ return fun.apply(this, args);
+ };
+ },
+
+ // Flips the first two args of a function
+ flip2: function(fun) {
+ return function(/* args */) {
+ var flipped = __slice.call(arguments);
+ flipped[0] = arguments[1];
+ flipped[1] = arguments[0];
+
+ return fun.apply(this, flipped);
+ };
+ },
+
+ // Flips an arbitrary number of args of a function
+ flip: function(fun) {
+ return function(/* args */) {
+ var reversed = __reverse.call(arguments);
+
+ return fun.apply(this, reversed);
+ };
+ },
+
+ // Takes a method-style function (one which uses `this`) and pushes
+ // `this` into the argument list. The returned function uses its first
+ // argument as the receiver/context of the original function, and the rest
+ // of the arguments are used as the original's entire argument list.
+ functionalize: function(method) {
+ return function(ctx /*, args */) {
+ return method.apply(ctx, _.rest(arguments));
+ };
+ },
+
+ // Takes a function and pulls the first argument out of the argument
+ // list and into `this` position. The returned function calls the original
+ // with its receiver (`this`) prepending the argument list. The original
+ // is called with a receiver of `null`.
+ methodize: function(func) {
+ return function(/* args */) {
+ return func.apply(null, _.cons(this, arguments));
+ };
+ },
+
+ k: _.always,
+ t: _.pipeline
+ });
+
+ _.unsplatr = _.unsplat;
+
+ // map the arguments of a function, takes the mapping function
+ // first so it can be used as a combinator
+ _.mapArgsWith = curry2(_.flip(baseMapArgs));
+
+ // Returns function property of object by name, bound to object
+ _.bound = function(obj, fname) {
+ var fn = obj[fname];
+ if (!_.isFunction(fn))
+ throw new TypeError("Expected property to be a function");
+ return _.bind(fn, obj);
+ };
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.dispatch.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.dispatch.js
new file mode 100644
index 00000000..9e0eab21
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.dispatch.js
@@ -0,0 +1,33 @@
+// Underscore-contrib (underscore.function.dispatch.js 0.3.0)
+// (c) 2013 Justin Ridgewell
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ // Create quick reference variable for speed.
+ var slice = Array.prototype.slice;
+
+ // Mixing in the attempt function
+ // ------------------------
+
+ _.mixin({
+ // If object is not undefined or null then invoke the named `method` function
+ // with `object` as context and arguments; otherwise, return undefined.
+ attempt: function(object, method) {
+ if (object == null) return void 0;
+ var func = object[method];
+ var args = slice.call(arguments, 2);
+ return _.isFunction(func) ? func.apply(object, args) : void 0;
+ }
+ });
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.iterators.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.iterators.js
new file mode 100644
index 00000000..9b5aed62
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.iterators.js
@@ -0,0 +1,334 @@
+// Underscore-contrib (underscore.function.iterators.js 0.3.0)
+// (c) 2013 Michael Fogus and DocumentCloud Inc.
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root, undefined) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ var HASNTBEENRUN = {};
+
+ function unary (fun) {
+ return function (first) {
+ return fun.call(this, first);
+ };
+ }
+
+ function binary (fun) {
+ return function (first, second) {
+ return fun.call(this, first, second);
+ };
+ }
+
+ // Mixing in the iterator functions
+ // --------------------------------
+
+ function foldl (iter, binaryFn, seed) {
+ var state, element;
+ if (seed !== void 0) {
+ state = seed;
+ }
+ else {
+ state = iter();
+ }
+ element = iter();
+ while (element != null) {
+ state = binaryFn.call(element, state, element);
+ element = iter();
+ }
+ return state;
+ }
+
+ function unfold (seed, unaryFn) {
+ var state = HASNTBEENRUN;
+ return function () {
+ if (state === HASNTBEENRUN) {
+ state = seed;
+ } else if (state != null) {
+ state = unaryFn.call(state, state);
+ }
+
+ return state;
+ };
+ }
+
+ // note that the unfoldWithReturn behaves differently than
+ // unfold with respect to the first value returned
+ function unfoldWithReturn (seed, unaryFn) {
+ var state = seed,
+ pair,
+ value;
+ return function () {
+ if (state != null) {
+ pair = unaryFn.call(state, state);
+ value = pair[1];
+ state = value != null ? pair[0] : void 0;
+ return value;
+ }
+ else return void 0;
+ };
+ }
+
+ function accumulate (iter, binaryFn, initial) {
+ var state = initial;
+ return function () {
+ var element = iter();
+ if (element == null) {
+ return element;
+ }
+ else {
+ if (state === void 0) {
+ state = element;
+ } else {
+ state = binaryFn.call(element, state, element);
+ }
+
+ return state;
+ }
+ };
+ }
+
+ function accumulateWithReturn (iter, binaryFn, initial) {
+ var state = initial,
+ stateAndReturnValue,
+ element;
+ return function () {
+ element = iter();
+ if (element == null) {
+ return element;
+ }
+ else {
+ if (state === void 0) {
+ state = element;
+ return state;
+ }
+ else {
+ stateAndReturnValue = binaryFn.call(element, state, element);
+ state = stateAndReturnValue[0];
+ return stateAndReturnValue[1];
+ }
+ }
+ };
+ }
+
+ function map (iter, unaryFn) {
+ return function() {
+ var element;
+ element = iter();
+ if (element != null) {
+ return unaryFn.call(element, element);
+ } else {
+ return void 0;
+ }
+ };
+ }
+
+ function mapcat(iter, unaryFn) {
+ var lastIter = null;
+ return function() {
+ var element;
+ var gen;
+ if (lastIter == null) {
+ gen = iter();
+ if (gen == null) {
+ lastIter = null;
+ return void 0;
+ }
+ lastIter = unaryFn.call(gen, gen);
+ }
+ while (element == null) {
+ element = lastIter();
+ if (element == null) {
+ gen = iter();
+ if (gen == null) {
+ lastIter = null;
+ return void 0;
+ }
+ else {
+ lastIter = unaryFn.call(gen, gen);
+ }
+ }
+ }
+ return element;
+ };
+ }
+
+ function select (iter, unaryPredicateFn) {
+ return function() {
+ var element;
+ element = iter();
+ while (element != null) {
+ if (unaryPredicateFn.call(element, element)) {
+ return element;
+ }
+ element = iter();
+ }
+ return void 0;
+ };
+ }
+
+ function reject (iter, unaryPredicateFn) {
+ return select(iter, function (something) {
+ return !unaryPredicateFn(something);
+ });
+ }
+
+ function find (iter, unaryPredicateFn) {
+ return select(iter, unaryPredicateFn)();
+ }
+
+ function slice (iter, numberToDrop, numberToTake) {
+ var count = 0;
+ while (numberToDrop-- > 0) {
+ iter();
+ }
+ if (numberToTake != null) {
+ return function() {
+ if (++count <= numberToTake) {
+ return iter();
+ } else {
+ return void 0;
+ }
+ };
+ }
+ else return iter;
+ }
+
+ function drop (iter, numberToDrop) {
+ return slice(iter, numberToDrop == null ? 1 : numberToDrop);
+ }
+
+ function take (iter, numberToTake) {
+ return slice(iter, 0, numberToTake == null ? 1 : numberToTake);
+ }
+
+ function List (array) {
+ var index = 0;
+ return function() {
+ return array[index++];
+ };
+ }
+
+ function Tree (array) {
+ var index, myself, state;
+ index = 0;
+ state = [];
+ myself = function() {
+ var element, tempState;
+ element = array[index++];
+ if (element instanceof Array) {
+ state.push({
+ array: array,
+ index: index
+ });
+ array = element;
+ index = 0;
+ return myself();
+ } else if (element === void 0) {
+ if (state.length > 0) {
+ tempState = state.pop();
+ array = tempState.array;
+ index = tempState.index;
+ return myself();
+ } else {
+ return void 0;
+ }
+ } else {
+ return element;
+ }
+ };
+ return myself;
+ }
+
+ function K (value) {
+ return function () {
+ return value;
+ };
+ }
+
+ function upRange (from, to, by) {
+ return function () {
+ var was;
+
+ if (from > to) {
+ return void 0;
+ }
+ else {
+ was = from;
+ from = from + by;
+ return was;
+ }
+ };
+ }
+
+ function downRange (from, to, by) {
+ return function () {
+ var was;
+
+ if (from < to) {
+ return void 0;
+ }
+ else {
+ was = from;
+ from = from - by;
+ return was;
+ }
+ };
+ }
+
+ function range (from, to, by) {
+ if (from == null) {
+ return upRange(1, Infinity, 1);
+ }
+ else if (to == null) {
+ return upRange(from, Infinity, 1);
+ }
+ else if (by == null) {
+ if (from <= to) {
+ return upRange(from, to, 1);
+ }
+ else return downRange(from, to, 1);
+ }
+ else if (by > 0) {
+ return upRange(from, to, by);
+ }
+ else if (by < 0) {
+ return downRange(from, to, Math.abs(by));
+ }
+ else return k(from);
+ }
+
+ var numbers = unary(range);
+
+ _.iterators = {
+ accumulate: accumulate,
+ accumulateWithReturn: accumulateWithReturn,
+ foldl: foldl,
+ reduce: foldl,
+ unfold: unfold,
+ unfoldWithReturn: unfoldWithReturn,
+ map: map,
+ mapcat: mapcat,
+ select: select,
+ reject: reject,
+ filter: select,
+ find: find,
+ slice: slice,
+ drop: drop,
+ take: take,
+ List: List,
+ Tree: Tree,
+ constant: K,
+ K: K,
+ numbers: numbers,
+ range: range
+ };
+
+})(this, void 0);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.predicates.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.predicates.js
new file mode 100644
index 00000000..11e60ce3
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.function.predicates.js
@@ -0,0 +1,112 @@
+// Underscore-contrib (underscore.function.predicates.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+
+ // Mixing in the predicate functions
+ // ---------------------------------
+
+ _.mixin({
+ // A wrapper around instanceof
+ isInstanceOf: function(x, t) { return (x instanceof t); },
+
+ // An associative object is one where its elements are
+ // accessed via a key or index. (i.e. array and object)
+ isAssociative: function(x) { return _.isArray(x) || _.isObject(x) || _.isArguments(x); },
+
+ // An indexed object is anything that allows numerical index for
+ // accessing its elements (e.g. arrays and strings). NOTE: Underscore
+ // does not support cross-browser consistent use of strings as array-like
+ // objects, so be wary in IE 8 when using String objects and IE<8.
+ // on string literals & objects.
+ isIndexed: function(x) { return _.isArray(x) || _.isString(x) || _.isArguments(x); },
+
+ // A seq is something considered a sequential composite type (i.e. arrays and `arguments`).
+ isSequential: function(x) { return (_.isArray(x)) || (_.isArguments(x)); },
+
+ // Check if an object is an object literal, since _.isObject(function() {}) === _.isObject([]) === true
+ isPlainObject: function(x) { return _.isObject(x) && x.constructor === root.Object; },
+
+ // These do what you think that they do
+ isZero: function(x) { return 0 === x; },
+ isEven: function(x) { return _.isFinite(x) && (x & 1) === 0; },
+ isOdd: function(x) { return _.isFinite(x) && !_.isEven(x); },
+ isPositive: function(x) { return x > 0; },
+ isNegative: function(x) { return x < 0; },
+ isValidDate: function(x) { return _.isDate(x) && !_.isNaN(x.getTime()); },
+
+ // A numeric is a variable that contains a numeric value, regardless its type
+ // It can be a String containing a numeric value, exponential notation, or a Number object
+ // See here for more discussion: http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric/1830844#1830844
+ isNumeric: function(n) {
+ return !isNaN(parseFloat(n)) && isFinite(n);
+ },
+
+ // An integer contains an optional minus sign to begin and only the digits 0-9
+ // Objects that can be parsed that way are also considered ints, e.g. "123"
+ // Floats that are mathematically equal to integers are considered integers, e.g. 1.0
+ // See here for more discussion: http://stackoverflow.com/questions/1019515/javascript-test-for-an-integer
+ isInteger: function(i) {
+ return _.isNumeric(i) && i % 1 === 0;
+ },
+
+ // A float is a numbr that is not an integer.
+ isFloat: function(n) {
+ return _.isNumeric(n) && !_.isInteger(n);
+ },
+
+ // checks if a string is a valid JSON
+ isJSON: function(str) {
+ try {
+ JSON.parse(str);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ },
+
+ // Returns true if its arguments are monotonically
+ // increaing values; false otherwise.
+ isIncreasing: function() {
+ var count = _.size(arguments);
+ if (count === 1) return true;
+ if (count === 2) return arguments[0] < arguments[1];
+
+ for (var i = 1; i < count; i++) {
+ if (arguments[i-1] >= arguments[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ // Returns true if its arguments are monotonically
+ // decreaing values; false otherwise.
+ isDecreasing: function() {
+ var count = _.size(arguments);
+ if (count === 1) return true;
+ if (count === 2) return arguments[0] > arguments[1];
+
+ for (var i = 1; i < count; i++) {
+ if (arguments[i-1] <= arguments[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ });
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.object.builders.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.object.builders.js
new file mode 100644
index 00000000..278f0978
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.object.builders.js
@@ -0,0 +1,120 @@
+// Underscore-contrib (underscore.object.builders.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ // Create quick reference variables for speed access to core prototypes.
+ var slice = Array.prototype.slice,
+ concat = Array.prototype.concat;
+
+ var existy = function(x) { return x != null; };
+ var truthy = function(x) { return (x !== false) && existy(x); };
+ var isAssociative = function(x) { return _.isArray(x) || _.isObject(x); };
+ var curry2 = function(fun) {
+ return function(last) {
+ return function(first) {
+ return fun(first, last);
+ };
+ };
+ };
+
+ // Mixing in the object builders
+ // ----------------------------
+
+ _.mixin({
+ // Merges two or more objects starting with the left-most and
+ // applying the keys right-word
+ // {any:any}* -> {any:any}
+ merge: function(/* objs */){
+ var dest = _.some(arguments) ? {} : null;
+
+ if (truthy(dest)) {
+ _.extend.apply(null, concat.call([dest], _.toArray(arguments)));
+ }
+
+ return dest;
+ },
+
+ // Takes an object and another object of strings to strings where the second
+ // object describes the key renaming to occur in the first object.
+ renameKeys: function(obj, kobj) {
+ return _.reduce(kobj, function(o, nu, old) {
+ if (existy(obj[old])) {
+ o[nu] = obj[old];
+ return o;
+ }
+ else
+ return o;
+ },
+ _.omit.apply(null, concat.call([obj], _.keys(kobj))));
+ },
+
+ // Snapshots an object deeply. Based on the version by
+ // [Keith Devens](http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone)
+ // until we can find a more efficient and robust way to do it.
+ snapshot: function(obj) {
+ if(obj == null || typeof(obj) != 'object') {
+ return obj;
+ }
+
+ var temp = new obj.constructor();
+
+ for(var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ temp[key] = _.snapshot(obj[key]);
+ }
+ }
+
+ return temp;
+ },
+
+ // Updates the value at any depth in a nested object based on the
+ // path described by the keys given. The function provided is supplied
+ // the current value and is expected to return a value for use as the
+ // new value. If no keys are provided, then the object itself is presented
+ // to the given function.
+ updatePath: function(obj, fun, ks, defaultValue) {
+ if (!isAssociative(obj)) throw new TypeError("Attempted to update a non-associative object.");
+ if (!existy(ks)) return fun(obj);
+
+ var deepness = _.isArray(ks);
+ var keys = deepness ? ks : [ks];
+ var ret = deepness ? _.snapshot(obj) : _.clone(obj);
+ var lastKey = _.last(keys);
+ var target = ret;
+
+ _.each(_.initial(keys), function(key) {
+ if (defaultValue && !_.has(target, key)) {
+ target[key] = _.clone(defaultValue);
+ }
+ target = target[key];
+ });
+
+ target[lastKey] = fun(target[lastKey]);
+ return ret;
+ },
+
+ // Sets the value at any depth in a nested object based on the
+ // path described by the keys given.
+ setPath: function(obj, value, ks, defaultValue) {
+ if (!existy(ks)) throw new TypeError("Attempted to set a property at a null path.");
+
+ return _.updatePath(obj, function() { return value; }, ks, defaultValue);
+ },
+
+ // Returns an object where each element of an array is keyed to
+ // the number of times that it occurred in said array.
+ frequencies: curry2(_.countBy)(_.identity)
+ });
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.object.selectors.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.object.selectors.js
new file mode 100644
index 00000000..17308eec
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.object.selectors.js
@@ -0,0 +1,108 @@
+// Underscore-contrib (underscore.object.selectors.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ // Create quick reference variables for speed access to core prototypes.
+ var concat = Array.prototype.concat;
+ var ArrayProto = Array.prototype;
+ var slice = ArrayProto.slice;
+
+ // Mixing in the object selectors
+ // ------------------------------
+
+ _.mixin({
+ // Returns a function that will attempt to look up a named field
+ // in any object that it's given.
+ accessor: function(field) {
+ return function(obj) {
+ return (obj && obj[field]);
+ };
+ },
+
+ // Given an object, returns a function that will attempt to look up a field
+ // that it's given.
+ dictionary: function (obj) {
+ return function(field) {
+ return (obj && field && obj[field]);
+ };
+ },
+
+ // Like `_.pick` except that it takes an array of keys to pick.
+ selectKeys: function (obj, ks) {
+ return _.pick.apply(null, concat.call([obj], ks));
+ },
+
+ // Returns the key/value pair for a given property in an object, undefined if not found.
+ kv: function(obj, key) {
+ if (_.has(obj, key)) {
+ return [key, obj[key]];
+ }
+
+ return void 0;
+ },
+
+ // Gets the value at any depth in a nested object based on the
+ // path described by the keys given. Keys may be given as an array
+ // or as a dot-separated string.
+ getPath: function getPath (obj, ks) {
+ if (typeof ks == "string") ks = ks.split(".");
+
+ // If we have reached an undefined property
+ // then stop executing and return undefined
+ if (obj === undefined) return void 0;
+
+ // If the path array has no more elements, we've reached
+ // the intended property and return its value
+ if (ks.length === 0) return obj;
+
+ // If we still have elements in the path array and the current
+ // value is null, stop executing and return undefined
+ if (obj === null) return void 0;
+
+ return getPath(obj[_.first(ks)], _.rest(ks));
+ },
+
+ // Returns a boolean indicating whether there is a property
+ // at the path described by the keys given
+ hasPath: function hasPath (obj, ks) {
+ if (typeof ks == "string") ks = ks.split(".");
+
+ var numKeys = ks.length;
+
+ if (obj == null && numKeys > 0) return false;
+
+ if (!(ks[0] in obj)) return false;
+
+ if (numKeys === 1) return true;
+
+ return hasPath(obj[_.first(ks)], _.rest(ks));
+ },
+
+ pickWhen: function(obj, pred) {
+ var copy = {};
+
+ _.each(obj, function(value, key) {
+ if (pred(obj[key])) copy[key] = obj[key];
+ });
+
+ return copy;
+ },
+
+ omitWhen: function(obj, pred) {
+ return _.pickWhen(obj, function(e) { return !pred(e); });
+ }
+
+ });
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.existential.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.existential.js
new file mode 100644
index 00000000..72b7f000
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.existential.js
@@ -0,0 +1,32 @@
+// Underscore-contrib (underscore.util.existential.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+
+ // Mixing in the truthiness
+ // ------------------------
+
+ _.mixin({
+ exists: function(x) { return x != null; },
+ truthy: function(x) { return (x !== false) && _.exists(x); },
+ falsey: function(x) { return !_.truthy(x); },
+ not: function(b) { return !b; },
+ firstExisting: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ if (arguments[i] != null) return arguments[i];
+ }
+ }
+ });
+
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.operators.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.operators.js
new file mode 100644
index 00000000..23057165
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.operators.js
@@ -0,0 +1,164 @@
+// Underscore-contrib (underscore.function.arity.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Setup for variadic operators
+ // ----------------------------
+
+ // Turn a binary math operator into a variadic operator
+ function variadicMath(operator) {
+ return function() {
+ return _.reduce(arguments, operator);
+ };
+ }
+
+ // Turn a binary comparator into a variadic comparator
+ function variadicComparator(comparator) {
+ return function() {
+ var result;
+ for (var i = 0; i < arguments.length - 1; i++) {
+ result = comparator(arguments[i], arguments[i + 1]);
+ if (result === false) return result;
+ }
+ return result;
+ };
+ }
+
+ // Turn a boolean-returning function into one with the opposite meaning
+ function invert(fn) {
+ return function() {
+ return !fn.apply(this, arguments);
+ };
+ }
+
+ // Basic math operators
+ function add(x, y) {
+ return x + y;
+ }
+
+ function sub(x, y) {
+ return x - y;
+ }
+
+ function mul(x, y) {
+ return x * y;
+ }
+
+ function div(x, y) {
+ return x / y;
+ }
+
+ function mod(x, y) {
+ return x % y;
+ }
+
+ function inc(x) {
+ return ++x;
+ }
+
+ function dec(x) {
+ return --x;
+ }
+
+ function neg(x) {
+ return -x;
+ }
+
+ // Bitwise operators
+ function bitwiseAnd(x, y) {
+ return x & y;
+ }
+
+ function bitwiseOr(x, y) {
+ return x | y;
+ }
+
+ function bitwiseXor(x, y) {
+ return x ^ y;
+ }
+
+ function bitwiseLeft(x, y) {
+ return x << y;
+ }
+
+ function bitwiseRight(x, y) {
+ return x >> y;
+ }
+
+ function bitwiseZ(x, y) {
+ return x >>> y;
+ }
+
+ function bitwiseNot(x) {
+ return ~x;
+ }
+
+ // Basic comparators
+ function eq(x, y) {
+ return x == y;
+ }
+
+ function seq(x, y) {
+ return x === y;
+ }
+
+ // Not
+ function not(x) {
+ return !x;
+ }
+
+ // Relative comparators
+ function gt(x, y) {
+ return x > y;
+ }
+
+ function lt(x, y) {
+ return x < y;
+ }
+
+ function gte(x, y) {
+ return x >= y;
+ }
+
+ function lte(x, y) {
+ return x <= y;
+ }
+
+ // Mixing in the operator functions
+ // -----------------------------
+
+ _.mixin({
+ add: variadicMath(add),
+ sub: variadicMath(sub),
+ mul: variadicMath(mul),
+ div: variadicMath(div),
+ mod: mod,
+ inc: inc,
+ dec: dec,
+ neg: neg,
+ eq: variadicComparator(eq),
+ seq: variadicComparator(seq),
+ neq: invert(variadicComparator(eq)),
+ sneq: invert(variadicComparator(seq)),
+ not: not,
+ gt: variadicComparator(gt),
+ lt: variadicComparator(lt),
+ gte: variadicComparator(gte),
+ lte: variadicComparator(lte),
+ bitwiseAnd: variadicMath(bitwiseAnd),
+ bitwiseOr: variadicMath(bitwiseOr),
+ bitwiseXor: variadicMath(bitwiseXor),
+ bitwiseNot: bitwiseNot,
+ bitwiseLeft: variadicMath(bitwiseLeft),
+ bitwiseRight: variadicMath(bitwiseRight),
+ bitwiseZ: variadicMath(bitwiseZ)
+ });
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.strings.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.strings.js
new file mode 100644
index 00000000..15c6eaf1
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.strings.js
@@ -0,0 +1,129 @@
+// Underscore-contrib (underscore.util.strings.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+ // No reason to create regex more than once
+ var plusRegex = /\+/g;
+ var spaceRegex = /\%20/g;
+ var bracketRegex = /(?:([^\[]+))|(?:\[(.*?)\])/g;
+
+ var urlDecode = function(s) {
+ return decodeURIComponent(s.replace(plusRegex, '%20'));
+ };
+ var urlEncode = function(s) {
+ return encodeURIComponent(s).replace(spaceRegex, '+');
+ };
+
+ var buildParams = function(prefix, val, top) {
+ if (_.isUndefined(top)) top = true;
+ if (_.isArray(val)) {
+ return _.map(val, function(value, key) {
+ return buildParams(top ? key : prefix + '[]', value, false);
+ }).join('&');
+ } else if (_.isObject(val)) {
+ return _.map(val, function(value, key) {
+ return buildParams(top ? key : prefix + '[' + key + ']', value, false);
+ }).join('&');
+ } else {
+ return urlEncode(prefix) + '=' + urlEncode(val);
+ }
+ };
+
+ // Mixing in the string utils
+ // ----------------------------
+
+ _.mixin({
+ // Explodes a string into an array of chars
+ explode: function(s) {
+ return s.split('');
+ },
+
+ // Parses a query string into a hash
+ fromQuery: function(str) {
+ var parameters = str.split('&'),
+ obj = {},
+ parameter,
+ key,
+ match,
+ lastKey,
+ subKey,
+ depth;
+
+ // Iterate over key/value pairs
+ _.each(parameters, function(parameter) {
+ parameter = parameter.split('=');
+ key = urlDecode(parameter[0]);
+ lastKey = key;
+ depth = obj;
+
+ // Reset so we don't have issues when matching the same string
+ bracketRegex.lastIndex = 0;
+
+ // Attempt to extract nested values
+ while ((match = bracketRegex.exec(key)) !== null) {
+ if (!_.isUndefined(match[1])) {
+
+ // If we're at the top nested level, no new object needed
+ subKey = match[1];
+
+ } else {
+
+ // If we're at a lower nested level, we need to step down, and make
+ // sure that there is an object to place the value into
+ subKey = match[2];
+ depth[lastKey] = depth[lastKey] || (subKey ? {} : []);
+ depth = depth[lastKey];
+ }
+
+ // Save the correct key as a hash or an array
+ lastKey = subKey || _.size(depth);
+ }
+
+ // Assign value to nested object
+ depth[lastKey] = urlDecode(parameter[1]);
+ });
+
+ return obj;
+ },
+
+ // Implodes and array of chars into a string
+ implode: function(a) {
+ return a.join('');
+ },
+
+ // Converts a string to camel case
+ camelCase : function( string ){
+ return string.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
+ },
+
+ // Converts camel case to dashed (opposite of _.camelCase)
+ toDash : function( string ){
+ string = string.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
+ // remove first dash
+ return ( string.charAt( 0 ) == '-' ) ? string.substr(1) : string;
+ },
+
+ // Creates a query string from a hash
+ toQuery: function(obj) {
+ return buildParams('', obj);
+ },
+
+ // Reports whether a string contains a search string.
+ strContains: function(str, search) {
+ if (typeof str != 'string') throw new TypeError;
+ return (str.indexOf(search) != -1);
+ }
+
+ });
+})(this);
diff --git a/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.trampolines.js b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.trampolines.js
new file mode 100644
index 00000000..ce2a1bc7
--- /dev/null
+++ b/node_modules/catharsis/node_modules/underscore-contrib/underscore.util.trampolines.js
@@ -0,0 +1,39 @@
+// Underscore-contrib (underscore.util.trampolines.js 0.3.0)
+// (c) 2013 Michael Fogus, DocumentCloud and Investigative Reporters & Editors
+// Underscore-contrib may be freely distributed under the MIT license.
+
+(function(root) {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var _ = root._ || require('underscore');
+
+ // Helpers
+ // -------
+
+
+ // Mixing in the truthiness
+ // ------------------------
+
+ _.mixin({
+ done: function(value) {
+ var ret = _(value);
+ ret.stopTrampoline = true;
+ return ret;
+ },
+
+ trampoline: function(fun /*, args */) {
+ var result = fun.apply(fun, _.rest(arguments));
+
+ while (_.isFunction(result)) {
+ result = result();
+ if ((result instanceof _) && (result.stopTrampoline)) break;
+ }
+
+ return result.value();
+ }
+ });
+
+})(this);
diff --git a/node_modules/catharsis/package.json b/node_modules/catharsis/package.json
index 3d857e23..523de49d 100644
--- a/node_modules/catharsis/package.json
+++ b/node_modules/catharsis/package.json
@@ -1,5 +1,5 @@
{
- "version": "0.7.1",
+ "version": "0.8.2",
"name": "catharsis",
"description": "A JavaScript parser for Google Closure Compiler and JSDoc type expressions.",
"author": {
@@ -14,15 +14,17 @@
"url": "https://github.com/hegemonic/catharsis/issues"
},
"main": "catharsis.js",
+ "dependencies": {
+ "underscore-contrib": "~0.3.0"
+ },
"devDependencies": {
- "mocha": "1.13.0",
+ "mocha": "~1.18.2",
"pegjs": "git+ssh://git@github.com:dmajda/pegjs.git#76cc5d55",
- "should": "1.3.0",
- "uglify-js": "2.4.0",
- "underscore": "1.5.2"
+ "should": "~3.3.1",
+ "uglify-js": "~2.4.13"
},
"engines": {
- "node": ">= 0.6"
+ "node": ">= 0.8"
},
"scripts": {
"build": "pegjs ./lib/parser.pegjs",
@@ -35,13 +37,11 @@
"url": "http://github.com/hegemonic/catharsis/raw/master/LICENSE"
}
],
- "readme": "# Catharsis #\n\nA JavaScript parser for\n[Google Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types)\nand [JSDoc](https://github.com/jsdoc3/jsdoc) type expressions.\n\nCatharsis is designed to be:\n\n+ **Accurate**. Catharsis is based on a [PEG.js](http://pegjs.majda.cz/) grammar that's designed to\nhandle any valid type expression. It uses a [Mocha](http://visionmedia.github.com/mocha/) test suite\nto verify the parser's accuracy.\n+ **Fast**. Parse results are cached, so the parser is invoked only when necessary.\n+ **Flexible**. Catharsis can convert parse results back into type expressions. In addition, it can\nparse [JSDoc](https://github.com/jsdoc3/jsdoc)-style type expressions.\n\n\n## Example ##\n\n```js\nvar catharsis = require('catharsis');\n\n// Google Closure Compiler parsing\nvar type = '!Object';\nvar parsedType;\ntry {\n parsedType = catharsis.parse(type); // {\"type\":\"NameExpression,\"name\":\"Object\",\"nullable\":false}\n} catch(e) {\n console.error('unable to parse %s: %s', type, e);\n}\n\n// JSDoc-style type expressions enabled\nvar jsdocType = 'string[]'; // Closure Compiler expects Array.\nvar parsedJsdocType;\ntry {\n parsedJsdocType = catharsis.parse(jsdocType, {jsdoc: true});\n} catch (e) {\n console.error('unable to parse %s: %s', jsdocType, e);\n}\n\ncatharsis.stringify(parsedType); // !Object\ncatharsis.stringify(parsedJsdocType); // string[]\ncatharsis.stringify(parsedJsdocType, {restringify: true}); // Array.\n```\n\nSee the `test/specs/` directory for more examples of Catharsis' parse results.\n\n\n## Methods ##\n\n### parse(typeExpression, options) ###\nParse `typeExpression`, and return the parse results. Throws an error if the type expression cannot\nbe parsed.\n\nWhen called without options, Catharsis attempts to parse type expressions in the same way as\nClosure Compiler. When the `jsdoc` option is enabled, Catharsis can also parse several kinds of\ntype expressions that are permitted in [JSDoc](https://github.com/jsdoc3/jsdoc):\n\n+ The string `function` is treated as a function type with no parameters.\n+ In a function type with repeatable parameters, the names of repeatable parameters are not required\nto be enclosed in square brackets (for example, `function(...foo)` is allowed).\n+ The period may be omitted from type applications. For example, `Array.` and\n`Array` will be parsed in the same way.\n+ You may append `[]` to a name expression (for example, `string[]`) to interpret it as a type\napplication with the expression `Array` (for example, `Array.`).\n+ Name expressions may contain the characters `#`, `~`, `:`, and `/`.\n+ Name expressions may contain a suffix that is similar to a function signature (for example,\n`MyClass(foo, bar)`).\n+ Name expressions may contain a reserved word.\n+ Record types may use types other than name expressions for keys.\n\n#### Parameters ####\n+ `type`: A string containing a Closure Compiler type expression.\n+ `options`: Options for parsing the type expression.\n + `options.jsdoc`: Specifies whether to enable parsing of JSDoc-style type expressions. Defaults\n to `false`.\n + `options.useCache`: Specifies whether to use the cache of parsed types. Defaults to `true`.\n\n#### Returns ####\nAn object containing the parse results. See the `test/specs/` directory for examples of the parse\nresults for different type expressions.\n\nThe object also includes two non-enumerable properties:\n\n+ `jsdoc`: A boolean indicating whether the type expression was parsed with JSDoc support enabled.\n+ `typeExpression`: A string containing the type expression that was parsed.\n\n### stringify(parsedType, options) ###\nStringify `parsedType`, and return the type expression. If validation is enabled, throws an error if\nthe stringified type expression cannot be parsed.\n\n#### Parameters ####\n+ `parsedType`: An object containing a parsed Closure Compiler type expression.\n+ `options`: Options for stringifying the parse results.\n + `options.cssClass`: A CSS class to add to HTML links. Used only if `options.links` is\n provided. By default, no CSS class is added.\n + `options.htmlSafe`: Specifies whether to return an HTML-safe string that replaces left angle\n brackets (`<`) with the corresponding entity (`<`). **Note**: Characters in name expressions\n are not escaped.\n + `options.links`: An object whose keys are name expressions and whose values are URIs. If a\n name expression matches a key in `options.links`, the name expression will be wrapped in an\n HTML `` tag that links to the URI. If `options.cssClass` is specified, the `` tag will\n include a `class` attribute. **Note**: When using this option, parsed types are always\n restringified, and the resulting string is not cached.\n + `options.restringify`: Forces Catharsis to restringify the parsed type. If this option is not\n specified, and the parsed type object includes a `typeExpression` property, Catharsis will\n return the `typeExpression` property without modification when possible. Defaults to `false`.\n + `options.useCache`: Specifies whether to use the cache of stringified parse results. Defaults\n to `true`.\n + `options.validate`: Specifies whether to validate the stringified parse results by attempting\n to parse them as a type expression. If the stringified results are not parsable by default, you\n must also provide the appropriate options to pass to the `parse()` method. Defaults to `false`.\n\n#### Returns ####\nA string containing the type expression.\n\n\n## Installation ##\n\nWith [npm](http://npmjs.org):\n\n npm install catharsis\n\nOr without:\n\n git clone git://github.com/hegemonic/catharsis.git\n\n\n## Roadmap and known issues ##\n\nTake a look at the [issue tracker](https://github.com/hegemonic/catharsis/issues) to see what's in\nstore for Catharsis.\n\nBug reports, feature requests, and pull requests are always welcome! If you're working on a large\npull request, please contact me in advance so I can help things go smoothly.\n\n**Note**: The parse tree's format should not be considered final until Catharsis reaches version\n1.0. I'll do my best to provide release notes for any changes.\n\n\n## Changelog ##\n\n+ 0.7.1 (April 2014): In record types, property names that begin with a keyword (for example,\n`undefinedHTML`) are now parsed correctly when JSDoc-style type expressions are enabled.\n+ 0.7.0 (October 2013):\n + Repeatable type expressions other than name expressions (for example, `...function()`) are now\n parsed and stringified correctly.\n + Type expressions that are both repeatable and either nullable or non-nullable (for example,\n `...!number`) are now parsed and stringified correctly.\n + Name expressions are now parsed correctly when they match a property name in an object\n instance (for example, `constructor`).\n+ 0.6.0 (September 2013): Added support for the type expression `function[]` when JSDoc-style type\nexpressions are enabled.\n+ 0.5.6 (April 2013):\n + For consistency with Google Closure Library, parentheses are no longer required around type\n unions. (In previous versions, the parentheses could be omitted when JSDoc support was enabled.)\n + For consistency with Google Closure Library, you can now use postfix notation for the `?`\n (nullable) and `!` (non-nullable) modifiers. For example, `?string` and `string?` are now\n treated as equivalent.\n + String literals and numeric literals are now allowed as property names within name\n expressions. For example, the name expression `Foo.\"bar\"` is now parsed correctly.\n+ 0.5.5 (April 2013): Corrected a parsing issue with name expressions that end with a value enclosed\nin parentheses.\n+ 0.5.4 (April 2013):\n + Repeatable literals (for example, `...*`) are now parsed correctly.\n + When JSDoc-style type expressions are enabled, a name expression can now contain a value\n enclosed in parentheses at the end of the name expression (for example, `MyClass(2)`).\n+ 0.5.3 (March 2013): The `parse()` method now correctly parses name expressions that contain\nhyphens.\n+ 0.5.2 (March 2013): The `parse()` method now correctly parses function types when JSDoc-style type\nexpressions are enabled.\n+ 0.5.1 (March 2013): Newlines and extra spaces are now removed from type expressions before they\nare parsed.\n+ 0.5.0 (March 2013):\n + The `parse()` method's `lenient` option has been renamed to `jsdoc`. **Note**: This change is\n not backwards-compatible with previous versions.\n + The `stringify()` method now accepts `cssClass` and `links` options, which you can use to\n add HTML links to a type expression.\n+ 0.4.3 (March 2013):\n + The `stringify()` method no longer caches HTML-safe type expressions as if they were normal\n type expressions.\n + The `stringify()` method's options parameter may now include an `options.restringify`\n property, and the behavior of the `options.useCache` property has changed.\n+ 0.4.2 (March 2013):\n + When lenient parsing is enabled, name expressions can now contain the characters `:` and `/`.\n + When lenient parsing is enabled, a name expression followed by `[]` (for example, `string[]`)\n will be interpreted as a type application with the expression `Array` (for example,\n `Array.`).\n+ 0.4.1 (March 2013):\n + The `parse()` and `stringify()` methods now honor all of the specified options.\n + When lenient parsing is enabled, name expressions can now contain a reserved word.\n+ 0.4.0 (March 2013):\n + Catharsis now supports a lenient parsing option that can parse several kinds of malformed type\n expressions. See the documentation for details.\n + The objects containing parse results are now frozen.\n + The objects containing parse results now have two non-enumerable properties:\n + `lenient`: A boolean indicating whether the type expression was parsed in lenient mode.\n + `typeExpression`: A string containing the original type expression.\n + The `stringify()` method now honors the `useCache` option. If a parsed type includes a\n `typeExpression` property, and `useCache` is not set to `false`, the stringified type will be\n identical to the original type expression.\n+ 0.3.1 (March 2013): Type expressions that begin with a reserved word, such as `integer`, are now\nparsed correctly.\n+ 0.3.0 (March 2013):\n + The `parse()` and `stringify()` methods are now synchronous, and the `parseSync()` and\n `stringifySync()` methods have been removed. **Note**: This change is not backwards-compatible\n with previous versions.\n + The parse results now use a significantly different format from previous versions. The new\n format is more expressive and is similar, but not identical, to the format used by the\n [doctrine](https://github.com/Constellation/doctrine) parser. **Note**: This change is not\n backwards-compatible with previous versions.\n + Name expressions that contain a reserved word now include a `reservedWord: true` property.\n + Union types that are optional or nullable, or that can be passed a variable number of times,\n are now parsed and stringified correctly.\n + Optional function types and record types are now parsed and stringified correctly.\n + Function types now longer include `new` or `this` properties unless the properties are defined\n in the type expression. In addition, the `new` and `this` properties can now use any type\n expression.\n + In record types, the key for a field type can now use any type expression.\n + Standalone single-character literals, such as ALL (`*`), are now parsed and stringified\n correctly.\n + `null` and `undefined` literals with additional properties, such as `repeatable`, are now\n stringified correctly.\n+ 0.2.0 (November 2012):\n + Added `stringify()` and `stringifySync()` methods, which convert a parsed type to a type\n expression.\n + Simplified the parse results for function signatures. **Note**: This change is not\n backwards-compatible with previous versions.\n + Corrected minor errors in README.md.\n+ 0.1.1 (November 2012): Added `opts` argument to `parse()` and `parseSync()` methods. **Note**: The\nchange to `parse()` is not backwards-compatible with previous versions.\n+ 0.1.0 (November 2012): Initial release.\n\n## License ##\n\n[MIT license](https://github.com/hegemonic/catharsis/blob/master/LICENSE).\n",
+ "readme": "# Catharsis #\n\nA JavaScript parser for\n[Google Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types)\nand [JSDoc](https://github.com/jsdoc3/jsdoc) type expressions.\n\nCatharsis is designed to be:\n\n+ **Accurate**. Catharsis is based on a [PEG.js](http://pegjs.majda.cz/) grammar that's designed to\nhandle any valid type expression. It uses a [Mocha](http://visionmedia.github.com/mocha/) test suite\nto verify the parser's accuracy.\n+ **Fast**. Parse results are cached, so the parser is invoked only when necessary.\n+ **Flexible**. Catharsis can convert a parse result back into a type expression, or into a\ndescription of the type expression. In addition, Catharsis can parse\n[JSDoc](https://github.com/jsdoc3/jsdoc)-style type expressions.\n\n\n## Example ##\n\n```js\nvar catharsis = require('catharsis');\n\n// Google Closure Compiler parsing\nvar type = '!Object';\nvar parsedType;\ntry {\n parsedType = catharsis.parse(type); // {\"type\":\"NameExpression,\"name\":\"Object\",\"nullable\":false}\n} catch(e) {\n console.error('unable to parse %s: %s', type, e);\n}\n\n// JSDoc-style type expressions enabled\nvar jsdocType = 'string[]'; // Closure Compiler expects Array.\nvar parsedJsdocType;\ntry {\n parsedJsdocType = catharsis.parse(jsdocType, {jsdoc: true});\n} catch (e) {\n console.error('unable to parse %s: %s', jsdocType, e);\n}\n\n// Converting parse results back to type expressions\ncatharsis.stringify(parsedType); // !Object\ncatharsis.stringify(parsedJsdocType); // string[]\ncatharsis.stringify(parsedJsdocType, {restringify: true}); // Array.\n\n// Converting parse results to descriptions of the type expression\ncatharsis.describe(parsedType).simple; // non-null Object\ncatharsis.describe(parsedJsdocType).simple; // Array of string\n```\n\nSee the [test/specs directory](test/specs) for more examples of Catharsis' parse results.\n\n\n## Methods ##\n\n### parse(typeExpression, options) ###\nParse a type expression, and return the parse results. Throws an error if the type expression cannot\nbe parsed.\n\nWhen called without options, Catharsis attempts to parse type expressions in the same way as\nClosure Compiler. When the `jsdoc` option is enabled, Catharsis can also parse several kinds of\ntype expressions that are permitted in [JSDoc](https://github.com/jsdoc3/jsdoc):\n\n+ The string `function` is treated as a function type with no parameters.\n+ In a function type with repeatable parameters, the names of repeatable parameters are not required\nto be enclosed in square brackets (for example, `function(...foo)` is allowed).\n+ The period may be omitted from type applications. For example, `Array.` and\n`Array` will be parsed in the same way.\n+ You may append `[]` to a name expression (for example, `string[]`) to interpret it as a type\napplication with the expression `Array` (for example, `Array.`).\n+ Name expressions may contain the characters `#`, `~`, `:`, and `/`.\n+ Name expressions may contain a suffix that is similar to a function signature (for example,\n`MyClass(foo, bar)`).\n+ Name expressions may contain a reserved word.\n+ Record types may use types other than name expressions for keys.\n\n#### Parameters ####\n+ `type`: A string containing a Closure Compiler type expression.\n+ `options`: Options for parsing the type expression.\n + `options.jsdoc`: Specifies whether to enable parsing of JSDoc-style type expressions. Defaults\n to `false`.\n + `options.useCache`: Specifies whether to use the cache of parsed types. Defaults to `true`.\n\n#### Returns ####\nAn object containing the parse results. See the [test/specs directory](test/specs) for examples of\nthe parse results for different type expressions.\n\nThe object also includes two non-enumerable properties:\n\n+ `jsdoc`: A boolean indicating whether the type expression was parsed with JSDoc support enabled.\n+ `typeExpression`: A string containing the type expression that was parsed.\n\n### stringify(parsedType, options) ###\nStringify `parsedType`, and return the type expression. If validation is enabled, throws an error if\nthe stringified type expression cannot be parsed.\n\n#### Parameters ####\n+ `parsedType`: An object containing a parsed Closure Compiler type expression.\n+ `options`: Options for stringifying the parse results.\n + `options.cssClass`: Synonym for `options.linkClass`. Deprecated in version 0.8.0; will be\n removed in a future version.\n + `options.htmlSafe`: Specifies whether to return an HTML-safe string that replaces left angle\n brackets (`<`) with the corresponding entity (`<`). **Note**: Characters in name expressions\n are not escaped.\n + `options.linkClass`: A CSS class to add to HTML links. Used only if `options.links` is\n provided. By default, no CSS class is added.\n + `options.links`: An object whose keys are name expressions and whose values are URIs. If a\n name expression matches a key in `options.links`, the name expression will be wrapped in an\n HTML `` tag that links to the URI. If `options.linkClass` is specified, the `` tag will\n include a `class` attribute. **Note**: When using this option, parsed types are always\n restringified, and the resulting string is not cached.\n + `options.restringify`: Forces Catharsis to restringify the parsed type. If this option is not\n specified, and the parsed type object includes a `typeExpression` property, Catharsis will\n return the `typeExpression` property without modification when possible. Defaults to `false`.\n + `options.useCache`: Specifies whether to use the cache of stringified type expressions.\n Defaults to `true`.\n + `options.validate`: Specifies whether to validate the stringified parse results by attempting\n to parse them as a type expression. If the stringified results are not parsable by default, you\n must also provide the appropriate options to pass to the `parse()` method. Defaults to `false`.\n\n#### Returns ####\nA string containing the type expression.\n\n### describe(parsedType, options) ###\nConvert a parsed type to a description of the type expression. This method is especially useful if\nyour users are not familiar with the syntax for type expressions.\n\nThe `describe()` method returns the description in two formats:\n\n+ **Simple format**. A string that provides a complete description of the type expression.\n+ **Extended format**. An object that separates out some of the details about the outermost type\nexpression, such as whether the type is optional, nullable, or repeatable.\n\nFor example, if you call `describe('?function(new:MyObject, string)=')`, it returns the following\nobject:\n\n```js\n{\n simple: 'optional nullable function(constructs MyObject, string)',\n extended: {\n description: 'function(string)',\n modifiers: {\n functionNew: 'Returns MyObject when called with new.',\n functionThis: '',\n optional: 'Optional.',\n nullable: 'May be null.',\n repeatable: ''\n },\n returns: ''\n }\n}\n```\n\n#### Parameters ####\n+ `parsedType`: An object containing a parsed Closure Compiler type expression.\n+ `options`: Options for creating the description.\n + `options.codeClass`: A CSS class to add to the tag that is wrapped around type names. Used\n only if `options.codeTag` is provided. By default, no CSS class is added.\n + `options.codeTag`: The name of an HTML tag (for example, `code`) to wrap around type names.\n For example, if this option is set to `code`, the type expression `Array.` would have\n the simple description `Array of string`.\n + `options.language`: A string identifying the language in which to generate the description.\n The identifier should be an\n [ISO 639-1 language code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (for example,\n `en`). It can optionally be followed by a hyphen and an\n [ISO 3166-1 alpha-2 country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) (for example,\n `en-US`). If you use values other than `en`, you must provide translation resources in\n `options.resources`. Defaults to `en`.\n + `options.linkClass`: A CSS class to add to HTML links. Used only if `options.links` is\n provided. By default, no CSS class is added.\n + `options.links`: An object whose keys are name expressions and whose values are URIs. If a\n name expression matches a key in `options.links`, the name expression will be wrapped in an\n HTML `` tag that links to the URI. If `options.linkClass` is specified, the `` tag will\n include a `class` attribute. **Note**: When using this option, the description is not cached.\n + `options.resources`: An object that specifies how to describe type expressions for a given\n language. The object's property names should use the same format as `options.language`. Each\n property should contain an object in the same format as the translation resources in\n [res/en.json](res/en.json). If you specify a value for `options.resources.en`, it will override\n the defaults in [res/en.json](res/en.json).\n + `options.useCache`: Specifies whether to use the cache of descriptions. Defaults to `true`.\n\n### Returns ###\nAn object with the following properties:\n\n+ `simple`: A string that provides a complete description of the type expression.\n+ `extended`: An object containing details about the outermost type expression.\n + `extended.description`: A string that provides a basic description of the type expression,\n excluding the information contained in other properties.\n + `extended.modifiers`: Information about modifiers that apply to the type expression.\n + `extended.modifiers.functionNew`: A string describing what a function returns when called\n with `new`. Used only for function types.\n + `extended.modifiers.functionThis`: A string describing what the keyword `this` refers to\n within a function. Used only for function types.\n + `extended.modifiers.nullable`: A string indicating whether the type is nullable or\n non-nullable.\n + `extended.modifiers.optional`: A string indicating whether the type is optional.\n + `extended.modifiers.repeatable`: A string indicating whether the type can be provided\n + `extended.returns`: A string describing the function's return value. Used only for function\n types.\n\n\n## Installation ##\n\nWith [npm](http://npmjs.org):\n\n npm install catharsis\n\nOr without:\n\n git clone git://github.com/hegemonic/catharsis.git\n cd catharsis\n npm install\n\n\n## Roadmap and known issues ##\n\nTake a look at the [issue tracker](https://github.com/hegemonic/catharsis/issues) to see what's in\nstore for Catharsis.\n\nBug reports, feature requests, and pull requests are always welcome! If you're working on a large\npull request, please contact me in advance so I can help things go smoothly.\n\n**Note**: The parse tree's format should not be considered final until Catharsis reaches version\n1.0. I'll do my best to provide release notes for any changes.\n\n\n## Changelog ##\n\n+ 0.8.2 (June 2014): Fixed a compatibility issue with the JSDoc fork of Mozilla Rhino.\n+ 0.8.1 (June 2014): Added support for type unions that are not enclosed in parentheses, and that\ncontain nullable or non-nullable modifiers (for example, `!string|!number`).\n+ 0.8.0 (May 2014):\n + Added a `describe()` method, which converts a parsed type to a description of the type.\n + Added a `linkClass` option to the `stringify()` method, and deprecated the existing `cssClass`\n option. The `cssClass` option will be removed in a future release.\n + Clarified and corrected several sections in the `README`.\n+ 0.7.1 (April 2014): In record types, property names that begin with a keyword (for example,\n`undefinedHTML`) are now parsed correctly when JSDoc-style type expressions are enabled.\n+ 0.7.0 (October 2013):\n + Repeatable type expressions other than name expressions (for example, `...function()`) are now\n parsed and stringified correctly.\n + Type expressions that are both repeatable and either nullable or non-nullable (for example,\n `...!number`) are now parsed and stringified correctly.\n + Name expressions are now parsed correctly when they match a property name in an object\n instance (for example, `constructor`).\n+ 0.6.0 (September 2013): Added support for the type expression `function[]` when JSDoc-style type\nexpressions are enabled.\n+ 0.5.6 (April 2013):\n + For consistency with Google Closure Library, parentheses are no longer required around type\n unions. (In previous versions, the parentheses could be omitted when JSDoc support was enabled.)\n + For consistency with Google Closure Library, you can now use postfix notation for the `?`\n (nullable) and `!` (non-nullable) modifiers. For example, `?string` and `string?` are now\n treated as equivalent.\n + String literals and numeric literals are now allowed as property names within name\n expressions. For example, the name expression `Foo.\"bar\"` is now parsed correctly.\n+ 0.5.5 (April 2013): Corrected a parsing issue with name expressions that end with a value enclosed\nin parentheses.\n+ 0.5.4 (April 2013):\n + Repeatable literals (for example, `...*`) are now parsed correctly.\n + When JSDoc-style type expressions are enabled, a name expression can now contain a value\n enclosed in parentheses at the end of the name expression (for example, `MyClass(2)`).\n+ 0.5.3 (March 2013): The `parse()` method now correctly parses name expressions that contain\nhyphens.\n+ 0.5.2 (March 2013): The `parse()` method now correctly parses function types when JSDoc-style type\nexpressions are enabled.\n+ 0.5.1 (March 2013): Newlines and extra spaces are now removed from type expressions before they\nare parsed.\n+ 0.5.0 (March 2013):\n + The `parse()` method's `lenient` option has been renamed to `jsdoc`. **Note**: This change is\n not backwards-compatible with previous versions.\n + The `stringify()` method now accepts `cssClass` and `links` options, which you can use to\n add HTML links to a type expression.\n+ 0.4.3 (March 2013):\n + The `stringify()` method no longer caches HTML-safe type expressions as if they were normal\n type expressions.\n + The `stringify()` method's options parameter may now include an `options.restringify`\n property, and the behavior of the `options.useCache` property has changed.\n+ 0.4.2 (March 2013):\n + When lenient parsing is enabled, name expressions can now contain the characters `:` and `/`.\n + When lenient parsing is enabled, a name expression followed by `[]` (for example, `string[]`)\n will be interpreted as a type application with the expression `Array` (for example,\n `Array.`).\n+ 0.4.1 (March 2013):\n + The `parse()` and `stringify()` methods now honor all of the specified options.\n + When lenient parsing is enabled, name expressions can now contain a reserved word.\n+ 0.4.0 (March 2013):\n + Catharsis now supports a lenient parsing option that can parse several kinds of malformed type\n expressions. See the documentation for details.\n + The objects containing parse results are now frozen.\n + The objects containing parse results now have two non-enumerable properties:\n + `lenient`: A boolean indicating whether the type expression was parsed in lenient mode.\n + `typeExpression`: A string containing the original type expression.\n + The `stringify()` method now honors the `useCache` option. If a parsed type includes a\n `typeExpression` property, and `useCache` is not set to `false`, the stringified type will be\n identical to the original type expression.\n+ 0.3.1 (March 2013): Type expressions that begin with a reserved word, such as `integer`, are now\nparsed correctly.\n+ 0.3.0 (March 2013):\n + The `parse()` and `stringify()` methods are now synchronous, and the `parseSync()` and\n `stringifySync()` methods have been removed. **Note**: This change is not backwards-compatible\n with previous versions.\n + The parse results now use a significantly different format from previous versions. The new\n format is more expressive and is similar, but not identical, to the format used by the\n [doctrine](https://github.com/Constellation/doctrine) parser. **Note**: This change is not\n backwards-compatible with previous versions.\n + Name expressions that contain a reserved word now include a `reservedWord: true` property.\n + Union types that are optional or nullable, or that can be passed a variable number of times,\n are now parsed and stringified correctly.\n + Optional function types and record types are now parsed and stringified correctly.\n + Function types now longer include `new` or `this` properties unless the properties are defined\n in the type expression. In addition, the `new` and `this` properties can now use any type\n expression.\n + In record types, the key for a field type can now use any type expression.\n + Standalone single-character literals, such as ALL (`*`), are now parsed and stringified\n correctly.\n + `null` and `undefined` literals with additional properties, such as `repeatable`, are now\n stringified correctly.\n+ 0.2.0 (November 2012):\n + Added `stringify()` and `stringifySync()` methods, which convert a parsed type to a type\n expression.\n + Simplified the parse results for function signatures. **Note**: This change is not\n backwards-compatible with previous versions.\n + Corrected minor errors in README.md.\n+ 0.1.1 (November 2012): Added `opts` argument to `parse()` and `parseSync()` methods. **Note**: The\nchange to `parse()` is not backwards-compatible with previous versions.\n+ 0.1.0 (November 2012): Initial release.\n\n## License ##\n\n[MIT license](https://github.com/hegemonic/catharsis/blob/master/LICENSE).\n",
"readmeFilename": "README.md",
+ "gitHead": "7c1a64875003fb599dba8d0c37752ca8b382dd64",
"homepage": "https://github.com/hegemonic/catharsis",
- "_id": "catharsis@0.7.1",
- "dist": {
- "shasum": "34f84dcd2254962f1a8df3f8756c18c361429791"
- },
- "_from": "catharsis@0.7.1",
- "_resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.7.1.tgz"
+ "_id": "catharsis@0.8.2",
+ "_shasum": "8e85e06b564a659add861c8abf64bd91958f2160",
+ "_from": "catharsis@~0.8.2"
}
diff --git a/node_modules/catharsis/res/en.json b/node_modules/catharsis/res/en.json
new file mode 100644
index 00000000..9afb501b
--- /dev/null
+++ b/node_modules/catharsis/res/en.json
@@ -0,0 +1,91 @@
+{
+ "all": "any type",
+ "application": {
+ "array": "<%= prefix %> <%= codeTagOpen %>Array<%= codeTagClose %> of <%= application %> <%= suffix %>",
+ "object": "<%= prefix %> <%= codeTagOpen %>Object<%= codeTagClose %> with <%= application %> properties <%= suffix %>",
+ "objectNonString": "<%= prefix %> <%= codeTagOpen %>Object<%= codeTagClose %> with <%= keyApplication %> keys and <%= application %> properties <%= suffix %>"
+ },
+ "function": {
+ "extended": {
+ "new": "Returns <%= functionNew %> when called with <%= codeTagOpen %>new<%= codeTagClose %>.",
+ "returns": "Returns <%= type %>.",
+ "signature": "function(<%= functionParams %>)",
+ "this": "Within the function, <%= codeTagOpen %>this<%= codeTagClose %> refers to <%= functionThis %>."
+ },
+ "simple": {
+ "new": "constructs <%= functionNew %>",
+ "returns": "returns <%= type %>",
+ "signature": "<%= prefix %> function(<%= functionParams %>) <%= functionReturns %>",
+ "this": "<%= codeTagOpen %>this<%= codeTagClose %> = <%= functionThis %>"
+ }
+ },
+ "modifiers": {
+ "extended": {
+ "nonNullable": "Must not be null.",
+ "nullable": "May be null.",
+ "optional": "Optional.",
+ "prefix": "",
+ "repeatable": "May be provided more than once.",
+ "suffix": ""
+ },
+ "simple": {
+ "nonNullable": "non-null",
+ "nullable": "nullable",
+ "optional": "optional",
+ "prefix": "<%= optional %> <%= nullable %> <%= repeatable %>",
+ "repeatable": "repeatable",
+ "suffix": ""
+ }
+ },
+ "name": "<%= codeTagOpen %>{{ name }}<%= codeTagClose %> <%= suffix %>",
+ "null": "null",
+ "params": {
+ "first": {
+ "one": "<%= param %>",
+ "two": "<%= param %>, ",
+ "many": "<%= param %>, "
+ },
+ "middle": {
+ "many": "<%= param %>, "
+ },
+ "last": {
+ "two": "<%= param %>",
+ "many": "<%= param %>"
+ }
+ },
+ "record": {
+ "first": {
+ "one": "<%= prefix %> {<%= field %>} <%= suffix %>",
+ "two": "<%= prefix %> {<%= field %>, ",
+ "many": "<%= prefix %> {<%= field %>, "
+ },
+ "middle": {
+ "many": "<%= field %>, "
+ },
+ "last": {
+ "two": "<%= field %>} <%= suffix %>",
+ "many": "<%= field %>} <%= suffix %>"
+ }
+ },
+ "field": {
+ "typed": "<%= name %>: <%= type %>",
+ "untyped": "<%= name %>"
+ },
+ "type": "<%= prefix %> <%= codeTagOpen %><%= type %><%= codeTagClose %> <%= suffix %>",
+ "undefined": "undefined",
+ "union": {
+ "first": {
+ "one": "<%= prefix %> <%= element %> <%= suffix %>",
+ "two": "<%= prefix %> (<%= element %> ",
+ "many": "<%= prefix %> (<%= element %>, "
+ },
+ "middle": {
+ "many": "<%= element %>, "
+ },
+ "last": {
+ "two": "or <%= element %>) <%= suffix %>",
+ "many": "or <%= element %>) <%= suffix %>"
+ }
+ },
+ "unknown": "unknown type"
+}
diff --git a/package.json b/package.json
index 0d3f6a31..a7ed4e2e 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
},
"dependencies": {
"async": "~0.1.22",
- "catharsis": "~0.7.1",
+ "catharsis": "~0.8.2",
"esprima": "https://github.com/ariya/esprima/tarball/49a2eccb243f29bd653b11e9419241a9d726af7c",
"js2xmlparser": "~0.1.0",
"marked": "~0.3.1",