update Catharsis so type unions without parens can be parsed (#644)

This commit is contained in:
Jeff Williams 2014-06-10 22:25:01 -07:00
parent bbd06c4ff8
commit b5a5d09e02
26 changed files with 2923 additions and 106 deletions

3
.gitignore vendored
View File

@ -11,3 +11,6 @@ conf.json
# Generated files
test/tutorials/out
out/
# Dotfile detritus
.DS_Store

3
node_modules/catharsis/LICENSE generated vendored
View File

@ -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,

65
node_modules/catharsis/catharsis.js generated vendored
View File

@ -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();

532
node_modules/catharsis/lib/describe.js generated vendored Normal file
View File

@ -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('<a href="%s"%s>%s</a>', 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
};
};

File diff suppressed because one or more lines are too long

View File

@ -4,21 +4,22 @@ var Types = require('./types');
function Stringifier(options) {
this._options = options || {};
this._options.linkClass = this._options.linkClass || this._options.cssClass;
// in a list of function signature params, repeatable params are stringified differently
this._inFunctionSignatureParams = false;
}
Stringifier.prototype.applications = function(applications) {
var result = '';
var strings = [];
if (!applications) {
return '';
return result;
}
var parsedApplications = [];
var result = '';
for (var i = 0, l = applications.length; i < l; i++) {
parsedApplications.push(this.type(applications[i]));
strings.push(this.type(applications[i]));
}
if (this._options.htmlSafe) {
@ -27,23 +28,26 @@ Stringifier.prototype.applications = function(applications) {
result = '.<';
}
result += parsedApplications.join(', ') + '>';
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 = '<a href="' + options.links[nameString] + '"' + linkClass + '>';
nameString = openTag + nameString + '</a>';
}
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 = '<a href="' + this._options.links[nameString] + '"' + cssClass + '>';
nameString = openTag + nameString + '</a>';
}
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;
};

View File

@ -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.

View File

@ -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');

View File

@ -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!"
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

26
node_modules/catharsis/package.json generated vendored

File diff suppressed because one or more lines are too long

91
node_modules/catharsis/res/en.json generated vendored Normal file
View File

@ -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"
}

View File

@ -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",