Merge pull request #14 from STRML/feature/swagger1.2

Update to Swagger 1.2. Fixes #11, #8, #7.
This commit is contained in:
Raymond Feng 2014-07-14 13:44:25 -07:00
commit cd873e9ce1
32 changed files with 1177 additions and 5255 deletions

13
.jshintrc Normal file
View File

@ -0,0 +1,13 @@
{
"node": true,
"camelcase" : true,
"eqnull" : true,
"indent": 2,
"undef": true,
"quotmark": "single",
"maxlen": 80,
"trailing": true,
"newcap": true,
"nonew": true,
"undef": false
}

View File

@ -9,14 +9,80 @@ Below is a simple LoopBack application. The explorer is mounted at `/explorer`.
```js ```js
var loopback = require('loopback'); var loopback = require('loopback');
var app = loopback(); var app = loopback();
var explorer = require('loopback-explorer'); var explorer = require('../');
var port = 3000;
var Product = loopback.Model.extend('product'); var Product = loopback.Model.extend('product');
Product.attachTo(loopback.memory()); Product.attachTo(loopback.memory());
app.model(Product); app.model(Product);
app.use('/explorer', explorer(app, {basePath: '/api'}));
app.use('/api', loopback.rest()); app.use('/api', loopback.rest());
app.use('/explorer', explorer(app, { basePath: '/api' })); console.log("Explorer mounted at localhost:" + port + "/explorer");
app.listen(3000); app.listen(port);
``` ```
## Advanced Usage
Many aspects of the explorer are configurable.
See [options](#options) for a description of these options:
```js
// Mount middleware before calling `explorer()` to add custom headers, auth, etc.
app.use('/explorer', loopback.basicAuth('user', 'password'));
app.use('/explorer', explorer(app, {
basePath: '/custom-api-root',
swaggerDistRoot: '/swagger',
apiInfo: {
'title': 'My API',
'description': 'Explorer example app.'
},
resourcePath: 'swaggerResources',
version: '0.1-unreleasable'
}));
app.use('/custom-api-root', loopback.rest());
```
## Options
Options are passed to `explorer(app, options)`.
`basePath`: **String**
> Default: `app.get('restAPIRoot')` or `'/api'`.
> Sets the API's base path. This must be set if you are mounting your api
> to a path different than '/api', e.g. with
> `loopback.use('/custom-api-root', loopback.rest());
`swaggerDistRoot`: **String**
> Sets a path within your application for overriding Swagger UI files.
> If present, will search `swaggerDistRoot` first when attempting to load Swagger UI, allowing
> you to pick and choose overrides to the interface. Use this to style your explorer or
> add additional functionality.
> See [index.html](public/index.html), where you may want to begin your overrides.
> The rest of the UI is provided by [Swagger UI](https://github.com/wordnik/swagger-ui).
`apiInfo`: **Object**
> Additional information about your API. See the
> [spec](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#513-info-object).
`resourcePath`: **String**
> Default: `'resources'`
> Sets a different path for the
> [resource listing](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing).
> You generally shouldn't have to change this.
`version`: **String**
> Default: Read from package.json
> Sets your API version. If not present, will read from your app's package.json.

View File

@ -1,12 +1,19 @@
var loopback = require('loopback'); var loopback = require('loopback');
var app = loopback(); var app = loopback();
var explorer = require('../'); var explorer = require('../');
var port = 3000;
var Product = loopback.Model.extend('product'); var Product = loopback.Model.extend('product', {
foo: {type: 'string', required: true},
bar: 'string',
aNum: {type: 'number', min: 1, max: 10, required: true, default: 5}
});
Product.attachTo(loopback.memory()); Product.attachTo(loopback.memory());
app.model(Product); app.model(Product);
app.use(loopback.rest()); var apiPath = '/api';
app.use('/explorer', explorer(app)); app.use('/explorer', explorer(app, {basePath: apiPath}));
app.use(apiPath, loopback.rest());
console.log('Explorer mounted at localhost:' + port + '/explorer');
app.listen(3000); app.listen(port);

View File

@ -1,10 +1,15 @@
'use strict';
/*! /*!
* Adds dynamically-updated docs as /explorer * Adds dynamically-updated docs as /explorer
*/ */
var url = require('url');
var path = require('path'); var path = require('path');
var extend = require('util')._extend; var urlJoin = require('./lib/url-join');
var loopback = require('loopback'); var _defaults = require('lodash.defaults');
var express = requireLoopbackDependency('express'); var express = require('express');
var swagger = require('./lib/swagger');
var SWAGGER_UI_ROOT = path.join(__dirname, 'node_modules',
'swagger-ui', 'dist');
var STATIC_ROOT = path.join(__dirname, 'public'); var STATIC_ROOT = path.join(__dirname, 'public');
module.exports = explorer; module.exports = explorer;
@ -17,41 +22,43 @@ module.exports = explorer;
*/ */
function explorer(loopbackApplication, options) { function explorer(loopbackApplication, options) {
options = extend({}, options); options = _defaults({}, options, {
options.basePath = options.basePath || loopbackApplication.get('restApiRoot'); resourcePath: 'resources',
apiInfo: loopbackApplication.get('apiInfo') || {}
loopbackApplication.docs(options); });
var app = express(); var app = express();
swagger(loopbackApplication, app, options);
app.disable('x-powered-by'); app.disable('x-powered-by');
// config.json is loaded by swagger-ui. The server should respond
// with the relative URI of the resource doc.
app.get('/config.json', function(req, res) { app.get('/config.json', function(req, res) {
// Get the path we're mounted at. It's best to get this from the referer
// in case we're proxied at a deep path.
var source = url.parse(req.headers.referer || '').pathname;
// If no referer is available, use the incoming url.
if (!source) {
source = req.originalUrl.replace(/\/config.json(\?.*)?$/, '');
}
res.send({ res.send({
discoveryUrl: (options.basePath || '') + '/swagger/resources' url: urlJoin(source, '/' + options.resourcePath)
}); });
}); });
app.use(loopback.static(STATIC_ROOT));
// Allow specifying a static file root for swagger files. Any files in
// that folder will override those in the swagger-ui distribution.
// In this way one could e.g. make changes to index.html without having
// to worry about constantly pulling in JS updates.
if (options.swaggerDistRoot) {
app.use(express.static(options.swaggerDistRoot));
}
// File in node_modules are overridden by a few customizations
app.use(express.static(STATIC_ROOT));
// Swagger UI distribution
app.use(express.static(SWAGGER_UI_ROOT));
return app; return app;
} }
function requireLoopbackDependency(module) {
try {
return require('loopback/node_modules/' + module);
} catch (err) {
if (err.code !== 'MODULE_NOT_FOUND') throw err;
try {
// Dependencies may be installed outside the loopback module,
// e.g. as peer dependencies. Try to load the dependency from there.
return require(module);
} catch (errPeer) {
if (errPeer.code !== 'MODULE_NOT_FOUND') throw errPeer;
// Rethrow the initial error to make it clear that we were trying
// to load a module that should have been installed inside
// "loopback/node_modules". This should minimise end-user's confusion.
// However, such situation should never happen as `require('loopback')`
// would have failed before this function was even called.
throw err;
}
}
}

47
lib/class-helper.js Normal file
View File

@ -0,0 +1,47 @@
'use strict';
/**
* Module dependencies.
*/
var modelHelper = require('./model-helper');
var urlJoin = require('./url-join');
/**
* Export the classHelper singleton.
*/
var classHelper = module.exports = {
/**
* Given a remoting class, generate an API doc.
* See https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration
* @param {Class} aClass Strong Remoting class.
* @param {Object} opts Options (passed from Swagger(remotes, options))
* @param {String} opts.version API Version.
* @param {String} opts.swaggerVersion Swagger version.
* @param {String} opts.basePath Basepath (usually e.g. http://localhost:3000).
* @param {String} opts.resourcePath Resource path (usually /swagger/resources).
* @return {Object} API Declaration.
*/
generateAPIDoc: function(aClass, opts) {
return {
apiVersion: opts.version,
swaggerVersion: opts.swaggerVersion,
basePath: opts.basePath,
resourcePath: urlJoin('/', opts.resourcePath),
apis: [],
models: modelHelper.generateModelDefinition(aClass)
};
},
/**
* Given a remoting class, generate a reference to an API declaration.
* This is meant for insertion into the Resource declaration.
* See https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object
* @param {Class} aClass Strong Remoting class.
* @return {Object} API declaration reference.
*/
generateResourceDocAPIEntry: function(aClass) {
return {
path: aClass.http.path,
description: aClass.ctor.sharedCtor && aClass.ctor.sharedCtor.description
};
}
};

101
lib/model-helper.js Normal file
View File

@ -0,0 +1,101 @@
'use strict';
/**
* Module dependencies.
*/
var _cloneDeep = require('lodash.clonedeep');
var translateDataTypeKeys = require('./translate-data-type-keys');
/**
* Export the modelHelper singleton.
*/
var modelHelper = module.exports = {
/**
* Given a class (from remotes.classes()), generate a model definition.
* This is used to generate the schema at the top of many endpoints.
* @param {Class} class Remote class.
* @return {Object} Associated model definition.
*/
generateModelDefinition: function generateModelDefinition(aClass) {
var def = aClass.ctor.definition;
var name = def.name;
var required = [];
// Don't modify original properties.
var properties = _cloneDeep(def.properties);
// Iterate through each property in the model definition.
// Types may be defined as constructors (e.g. String, Date, etc.),
// or as strings; getPropType() will take care of the conversion.
// See more on types:
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#431-primitives
Object.keys(properties).forEach(function(key) {
var prop = properties[key];
// Eke a type out of the constructors we were passed.
prop = modelHelper.LDLPropToSwaggerDataType(prop);
// Required props sit in a per-model array.
if (prop.required || (prop.id && !prop.generated)) {
required.push(key);
}
// Change mismatched keys.
prop = translateDataTypeKeys(prop);
// Assign this back to the properties object.
properties[key] = prop;
});
var out = {};
out[name] = {
id: name,
properties: properties,
required: required
};
return out;
},
/**
* Given a propType (which may be a function, string, or array),
* get a string type.
* @param {*} propType Prop type description.
* @return {String} Prop type string.
*/
getPropType: function getPropType(propType) {
if (typeof propType === 'function') {
propType = propType.name.toLowerCase();
} else if(Array.isArray(propType)) {
propType = 'array';
}
return propType;
},
// Converts a prop defined with the LDL spec to one conforming to the
// Swagger spec.
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#431-primitives
LDLPropToSwaggerDataType: function LDLPropToSwaggerDataType(prop) {
var out = _cloneDeep(prop);
out.type = modelHelper.getPropType(out.type);
if (out.type === 'array') {
var arrayProp = prop.type[0];
if (!arrayProp.type) arrayProp = {type: arrayProp};
out.items = modelHelper.LDLPropToSwaggerDataType(arrayProp);
}
if (out.type === 'date') {
out.type = 'string';
out.format = 'date';
} else if (out.type === 'buffer') {
out.type = 'string';
out.format = 'byte';
} else if (out.type === 'number') {
out.format = 'double'; // Since all JS numbers are doubles
}
return out;
}
};

240
lib/route-helper.js Normal file
View File

@ -0,0 +1,240 @@
'use strict';
/**
* Module dependencies.
*/
var debug = require('debug')('loopback:explorer:routeHelpers');
var _cloneDeep = require('lodash.clonedeep');
var translateDataTypeKeys = require('./translate-data-type-keys');
var modelHelper = require('./model-helper');
/**
* Export the routeHelper singleton.
*/
var routeHelper = module.exports = {
/**
* Given a route, generate an API description and add it to the doc.
* If a route shares a path with another route (same path, different verb),
* add it as a new operation under that API description.
*
* Routes can be translated to API declaration 'operations',
* but they need a little massaging first. The `accepts` and
* `returns` declarations need some basic conversions to be compatible.
*
* This method will convert the route and add it to the doc.
* @param {Route} route Strong Remoting Route object.
* @param {Class} classDef Strong Remoting class.
* @param {Object} doc The class's backing API declaration doc.
*/
addRouteToAPIDeclaration: function (route, classDef, doc) {
var api = routeHelper.routeToAPIDoc(route, classDef);
var matchingAPIs = doc.apis.filter(function(existingAPI) {
return existingAPI.path === api.path;
});
if (matchingAPIs.length) {
matchingAPIs[0].operations.push(api.operations[0]);
} else {
doc.apis.push(api);
}
},
/**
* Massage route.accepts.
* @param {Object} route Strong Remoting Route object.
* @param {Class} classDef Strong Remoting class.
* @return {Array} Array of param docs.
*/
convertAcceptsToSwagger: function convertAcceptsToSwagger(route, classDef) {
var split = route.method.split('.');
var accepts = _cloneDeep(route.accepts) || [];
if (classDef && classDef.sharedCtor &&
classDef.sharedCtor.accepts && split.length > 2 /* HACK */) {
accepts = accepts.concat(classDef.sharedCtor.accepts);
}
// Filter out parameters that are generated from the incoming request,
// or generated by functions that use those resources.
accepts = accepts.filter(function(arg){
if (!arg.http) return true;
// Don't show derived arguments.
if (typeof arg.http === 'function') return false;
// Don't show arguments set to the incoming http request.
// Please note that body needs to be shown, such as User.create().
if (arg.http.source === 'req') return false;
return true;
});
// Translate LDL keys to Swagger keys.
accepts = accepts.map(translateDataTypeKeys);
// Turn accept definitions in to parameter docs.
accepts = accepts.map(routeHelper.acceptToParameter(route));
return accepts;
},
/**
* Massage route.returns.
* @param {Object} route Strong Remoting Route object.
* @param {Class} classDef Strong Remoting class.
* @return {Object} A single returns param doc.
*/
convertReturnsToSwagger: function convertReturnsToSwagger(route, classDef) {
var routeReturns = _cloneDeep(route.returns) || [];
// HACK: makes autogenerated REST routes return the correct model name.
var firstReturn = routeReturns && routeReturns[0];
if (firstReturn && firstReturn.arg === 'data') {
if (firstReturn.type === 'object') {
firstReturn.type = classDef.name;
} else if (firstReturn.type === 'array') {
firstReturn.type = 'array';
firstReturn.items = {
'$ref': classDef.name
};
}
}
// Translate LDL keys to Swagger keys.
var returns = routeReturns.map(translateDataTypeKeys);
// Convert `returns` into a single object for later conversion into an
// operation object.
if (returns && returns.length > 1) {
// TODO ad-hoc model definition in the case of multiple return values.
returns = {model: 'object'};
} else {
returns = returns[0] || {};
}
return returns;
},
/**
* Converts from an sl-remoting-formatted "Route" description to a
* Swagger-formatted "API" description.
* See https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object
*/
routeToAPIDoc: function routeToAPIDoc(route, classDef) {
var returnDesc;
// Some parameters need to be altered; eventually most of this should
// be removed.
var accepts = routeHelper.convertAcceptsToSwagger(route, classDef);
var returns = routeHelper.convertReturnsToSwagger(route, classDef);
debug('route %j', route);
var apiDoc = {
path: routeHelper.convertPathFragments(route.path),
operations: [{
method: routeHelper.convertVerb(route.verb),
// [rfeng] Swagger UI doesn't escape '.' for jQuery selector
nickname: route.method.replace(/\./g, '_'),
// Per the spec:
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object
// This is the only object that may have a type of 'void'.
type: returns.model || returns.type || 'void',
parameters: accepts,
// TODO(schoon) - We don't have descriptions for this yet.
responseMessages: [],
summary: route.description, // TODO(schoon) - Excerpt?
notes: '' // TODO(schoon) - `description` metadata?
}]
};
// Convert types and return.
return routeHelper.extendWithType(apiDoc);
},
convertPathFragments: function convertPathFragments(path) {
return path.split('/').map(function (fragment) {
if (fragment.charAt(0) === ':') {
return '{' + fragment.slice(1) + '}';
}
return fragment;
}).join('/');
},
convertVerb: function convertVerb(verb) {
if (verb.toLowerCase() === 'all') {
return 'POST';
}
if (verb.toLowerCase() === 'del') {
return 'DELETE';
}
return verb.toUpperCase();
},
/**
* A generator to convert from an sl-remoting-formatted "Accepts" description
* to a Swagger-formatted "Parameter" description.
*/
acceptToParameter: function acceptToParameter(route) {
var type = 'form';
if (route.verb.toLowerCase() === 'get') {
type = 'query';
}
return function (accepts) {
var name = accepts.name || accepts.arg;
var paramType = type;
// TODO: Regex. This is leaky.
if (route.path.indexOf(':' + name) !== -1) {
paramType = 'path';
}
// Check the http settings for the argument
if(accepts.http && accepts.http.source) {
paramType = accepts.http.source;
}
var out = {
paramType: paramType || type,
name: name,
description: accepts.description,
type: accepts.type,
required: !!accepts.required,
defaultValue: accepts.defaultValue,
minimum: accepts.minimum,
maximum: accepts.maximum,
allowMultiple: false
};
out = routeHelper.extendWithType(out);
// HACK: Derive the type from model
if(out.name === 'data' && out.type === 'object') {
out.type = route.method.split('.')[0];
}
return out;
};
},
/**
* Extends an Operation Object or Parameter object with
* a proper Swagger type and optional `format` and `items` fields.
* Does not modify original object.
* @param {Object} obj Object to extend.
* @return {Object} Extended object.
*/
extendWithType: function extendWithType(obj) {
obj = _cloneDeep(obj);
// Format the `type` property using our LDL converter.
var typeDesc = modelHelper
.LDLPropToSwaggerDataType({type: obj.model || obj.type});
// The `typeDesc` may have additional attributes, such as
// `format` for non-primitive types.
Object.keys(typeDesc).forEach(function(key){
obj[key] = typeDesc[key];
});
return obj;
}
};

141
lib/swagger.js Normal file
View File

@ -0,0 +1,141 @@
'use strict';
/**
* Expose the `Swagger` plugin.
*/
module.exports = Swagger;
/**
* Module dependencies.
*/
var debug = require('debug')('loopback:explorer:swagger');
var path = require('path');
var urlJoin = require('./url-join');
var _defaults = require('lodash.defaults');
var classHelper = require('./class-helper');
var modelHelper = require('./model-helper');
var routeHelper = require('./route-helper');
/**
* Create a remotable Swagger module for plugging into `RemoteObjects`.
*
* @param {Application} loopbackApplication Host loopback application.
* @param {Application} swaggerApp Swagger application used for hosting
* these files.
* @param {Object} opts Options.
*/
function Swagger(loopbackApplication, swaggerApp, opts) {
opts = _defaults({}, opts, {
swaggerVersion: '1.2',
basePath: loopbackApplication.get('restApiRoot') || '/api',
resourcePath: 'resources',
version: getVersion()
});
// We need a temporary REST adapter to discover our available routes.
var remotes = loopbackApplication.remotes();
var adapter = remotes.handler('rest').adapter;
var routes = adapter.allRoutes();
var classes = remotes.classes();
// These are the docs we will be sending from the /swagger endpoints.
var resourceDoc = generateResourceDoc(opts);
var apiDocs = {};
// A class is an endpoint root; e.g. /users, /products, and so on.
classes.forEach(function (aClass) {
var doc = apiDocs[aClass.name] = classHelper.generateAPIDoc(aClass, opts);
resourceDoc.apis.push(classHelper.generateResourceDocAPIEntry(aClass));
// Add the getter for this doc.
var docPath = urlJoin(opts.resourcePath, aClass.http.path);
addRoute(swaggerApp, docPath, doc);
});
// A route is an endpoint, such as /users/findOne.
routes.forEach(function(route) {
// Get the API doc matching this class name.
var className = route.method.split('.')[0];
var doc = apiDocs[className];
if (!doc) {
console.error('Route exists with no class: %j', route);
return;
}
// Get the class definition matching this route.
var classDef = classes.filter(function (item) {
return item.name === className;
})[0];
routeHelper.addRouteToAPIDeclaration(route, classDef, doc);
});
/**
* The topmost Swagger resource is a description of all (non-Swagger)
* resources available on the system, and where to find more
* information about them.
*/
addRoute(swaggerApp, opts.resourcePath, resourceDoc);
}
/**
* Add a route to this remoting extension.
* @param {Application} app Express application.
* @param {String} uri Path from which to serve the doc.
* @param {Object} doc Doc to serve.
*/
function addRoute(app, uri, doc) {
var hasBasePath = Object.keys(doc).indexOf('basePath') !== -1;
var initialPath = doc.basePath || '';
app.get(urlJoin('/', uri), function(req, res) {
// There's a few forces at play that require this "hack". The Swagger spec
// requires a `basePath` to be set in the API descriptions. However, we
// can't guarantee this path is either reachable or desirable if it's set
// as a part of the options.
//
// The simplest way around this is to reflect the value of the `Host` HTTP
// header as the `basePath`. Because we pre-build the Swagger data, we don't
// know that header at the time the data is built.
if (hasBasePath) {
var headers = req.headers;
var host = headers.Host || headers.host;
doc.basePath = req.protocol + '://' + host + initialPath;
}
res.send(200, doc);
});
}
/**
* Generate a top-level resource doc. This is the entry point for swagger UI
* and lists all of the available APIs.
* @param {Object} opts Swagger options.
* @return {Object} Resource doc.
*/
function generateResourceDoc(opts) {
return {
swaggerVersion: opts.swaggerVersion,
apiVersion: opts.version,
apis: [],
// See https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#513-info-object
info: opts.apiInfo
// TODO Authorizations
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#514-authorizations-object
// TODO Produces/Consumes
// https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration
};
}
/**
* Attempt to get the current API version from package.json.
* @return {String} API Version.
*/
function getVersion() {
var version;
try {
version = require(path.join(process.cwd(), 'package.json')).version;
} catch(e) {
version = '';
}
return version;
}

View File

@ -0,0 +1,38 @@
'use strict';
/**
* Module dependencies.
*/
var _cloneDeep = require('lodash.clonedeep');
// Keys that are different between LDL and Swagger
var KEY_TRANSLATIONS = {
// LDL : Swagger
'doc': 'description',
'default': 'defaultValue',
'min': 'minimum',
'max': 'maximum'
};
/**
* Correct key mismatches between LDL & Swagger.
* Does not modify original object.
* @param {Object} object Object on which to change keys.
* @return {Object} Translated object.
*/
module.exports = function translateDataTypeKeys(object) {
object = _cloneDeep(object);
Object.keys(KEY_TRANSLATIONS).forEach(function(LDLKey){
var val = object[LDLKey];
if (val) {
// Should change in Swagger 2.0
if (LDLKey === 'min' || LDLKey === 'max') {
val = String(val);
}
object[KEY_TRANSLATIONS[LDLKey]] = val;
}
delete object[LDLKey];
});
return object;
};

8
lib/url-join.js Normal file
View File

@ -0,0 +1,8 @@
'use strict';
// Simple url joiner. Ensure we don't have to care about whether or not
// we are fed paths with leading/trailing slashes.
module.exports = function urlJoin() {
var args = Array.prototype.slice.call(arguments);
return args.join('/').replace(/\/+/g, '/');
};

View File

@ -6,9 +6,6 @@
"scripts": { "scripts": {
"test": "mocha" "test": "mocha"
}, },
"peerDependencies": {
"loopback": "2.x || 1.x >=1.5"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/strongloop/loopback-explorer.git" "url": "git://github.com/strongloop/loopback-explorer.git"
@ -32,5 +29,12 @@
"license": { "license": {
"name": "Dual MIT/StrongLoop", "name": "Dual MIT/StrongLoop",
"url": "https://github.com/strongloop/loopback-explorer/blob/master/LICENSE" "url": "https://github.com/strongloop/loopback-explorer/blob/master/LICENSE"
},
"dependencies": {
"swagger-ui": "~2.0.17",
"debug": "~1.0.2",
"lodash.clonedeep": "^2.4.1",
"lodash.defaults": "^2.4.1",
"express": "3.x"
} }
} }

View File

@ -1,135 +0,0 @@
/*
Original style from softwaremaniacs.org (c) Ivan Sagalaev <Maniac@SoftwareManiacs.Org>
*/
pre code {
display: block; padding: 0.5em;
background: #F0F0F0;
}
pre code,
pre .subst,
pre .tag .title,
pre .lisp .title,
pre .clojure .built_in,
pre .nginx .title {
color: black;
}
pre .string,
pre .title,
pre .constant,
pre .parent,
pre .tag .value,
pre .rules .value,
pre .rules .value .number,
pre .preprocessor,
pre .ruby .symbol,
pre .ruby .symbol .string,
pre .aggregate,
pre .template_tag,
pre .django .variable,
pre .smalltalk .class,
pre .addition,
pre .flow,
pre .stream,
pre .bash .variable,
pre .apache .tag,
pre .apache .cbracket,
pre .tex .command,
pre .tex .special,
pre .erlang_repl .function_or_atom,
pre .markdown .header {
color: #800;
}
pre .comment,
pre .annotation,
pre .template_comment,
pre .diff .header,
pre .chunk,
pre .markdown .blockquote {
color: #888;
}
pre .number,
pre .date,
pre .regexp,
pre .literal,
pre .smalltalk .symbol,
pre .smalltalk .char,
pre .go .constant,
pre .change,
pre .markdown .bullet,
pre .markdown .link_url {
color: #080;
}
pre .label,
pre .javadoc,
pre .ruby .string,
pre .decorator,
pre .filter .argument,
pre .localvars,
pre .array,
pre .attr_selector,
pre .important,
pre .pseudo,
pre .pi,
pre .doctype,
pre .deletion,
pre .envvar,
pre .shebang,
pre .apache .sqbracket,
pre .nginx .built_in,
pre .tex .formula,
pre .erlang_repl .reserved,
pre .prompt,
pre .markdown .link_label,
pre .vhdl .attribute,
pre .clojure .attribute,
pre .coffeescript .property {
color: #88F
}
pre .keyword,
pre .id,
pre .phpdoc,
pre .title,
pre .built_in,
pre .aggregate,
pre .css .tag,
pre .javadoctag,
pre .phpdoc,
pre .yardoctag,
pre .smalltalk .class,
pre .winutils,
pre .bash .variable,
pre .apache .tag,
pre .go .typename,
pre .tex .command,
pre .markdown .strong,
pre .request,
pre .status {
font-weight: bold;
}
pre .markdown .emphasis {
font-style: italic;
}
pre .nginx .built_in {
font-weight: normal;
}
pre .coffeescript .javascript,
pre .javascript .xml,
pre .tex .formula,
pre .xml .javascript,
pre .xml .vbscript,
pre .xml .css,
pre .xml .cdata {
opacity: 0.5;
}

View File

@ -0,0 +1,27 @@
/* Styles used for loopback explorer customizations */
.accessTokenDisplay {
color: white;
margin-right: 10px;
}
.accessTokenDisplay.set {
border-bottom: 1px dotted #333; position: relative; cursor: pointer;
}
.accessTokenDisplay.set:hover:after {
content: attr(data-tooltip);
position: absolute;
white-space: nowrap;
font-size: 12px;
background: rgba(0, 0, 0, 0.85);
padding: 3px 7px;
color: #FFF;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
right: 0;
bottom: -30px;
}
/*
FIXME: Separate the overrides from the rest of the styles, rather than override screen.css entirely.
*/

View File

@ -1,3 +1,4 @@
/* FIXME move overrides only into loopbackStyles.css */
html, html,
body, body,
div, div,
@ -132,24 +133,7 @@ section,
summary { summary {
display: block; display: block;
} }
@font-face {
font-family: 'Ubuntu';
font-style: normal;
font-weight: 300;
src: local('Ubuntu Light'), local('Ubuntu-Light'), url(../fonts/_aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
}
@font-face {
font-family: 'Ubuntu';
font-style: normal;
font-weight: 500;
src: local('Ubuntu Medium'), local('Ubuntu-Medium'), url(../fonts/OsJ2DjdpjqFRVUSto6IffLO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
}
@font-face {
font-family: 'Ubuntu';
font-style: normal;
font-weight: 700;
src: local('Ubuntu Bold'), local('Ubuntu-Bold'), url(../fonts/0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
}
h1 a, h1 a,
h2 a, h2 a,
h3 a, h3 a,

View File

@ -1,82 +1,47 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>StrongLoop API Explorer</title> <title>StrongLoop API Explorer</title>
<link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/> <link href='https://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
<link href='css/hightlight.default.css' media='screen' rel='stylesheet' type='text/css'/> <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/> <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script> <link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script> <link href='css/screen.css' media='print' rel='stylesheet' type='text/css'/>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script> <link href='css/loopbackStyles.css' rel='stylesheet' type='text/css'/>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script> <script type="text/javascript" src="lib/shred.bundle.js"></script>
<script src='lib/handlebars-1.0.0.js' type='text/javascript'></script> <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script> <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script> <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/swagger.js' type='text/javascript'></script> <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script> <script src='lib/handlebars-1.0.0.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script> <script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='lib/swagger.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script type="text/javascript"> <!-- enabling this will enable oauth2 implicit scope support -->
$(function() { <script src='lib/swagger-oauth.js' type='text/javascript'></script>
$.getJSON('config.json', function(config) {
console.log(config);
loadSwaggerUi(config);
});
});
function loadSwaggerUi(config) { <!-- Init swagger UI. -->
window.swaggerUi = new SwaggerUi({ <script src='lib/loadSwaggerUI.js' type="text/javascript"></script>
discoveryUrl: config.discoveryUrl || "/swagger/resources",
apiKey: "",
dom_id: "swagger-ui-container",
supportHeaderParams: true,
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function(swaggerApi, swaggerUi) {
if (console) {
console.log("Loaded SwaggerUI")
console.log(swaggerApi);
console.log(swaggerUi);
}
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
},
onFailure: function(data) {
if (console) {
console.log("Unable to Load SwaggerUI");
console.log(data);
}
},
docExpansion: "none"
});
window.swaggerUi.load();
}
</script>
</head> </head>
<body> <body class="swagger-section">
<div id='header'> <div id='header'>
<div class="swagger-ui-wrap"> <div class="swagger-ui-wrap">
<a id="logo">StrongLoop API Explorer</a> <a id="logo">StrongLoop API Explorer</a>
<form id='api_selector'>
<form id='api_selector'> <div class='input'>
<div class='input'> <span class='accessTokenDisplay'>Token Not Set</span>
<input placeholder="http://example.com/api" <input placeholder="accessToken" id="input_accessToken" name="accessToken" type="text"/>
id="input_baseUrl" name="baseUrl" </div>
type="text"/> <div class='input'><a id="explore" type="submit">Set Access Token</a></div>
</div> </form>
<div class='input'><a id="explore" href="#">Explore</a></div> </div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">
&nbsp;
</div>
<div id="swagger-ui-container" class="swagger-ui-wrap">
</div> </div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body> </body>
</html> </html>

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,18 +0,0 @@
/*
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
* http://benalman.com/projects/jquery-bbq-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);

View File

@ -1 +0,0 @@
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);

View File

@ -1,8 +0,0 @@
/*
jQuery Wiggle
Author: WonderGroup, Jordan Thomas
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
*/
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};

View File

@ -0,0 +1,65 @@
'use strict';
// Refactoring of inline script from index.html.
/*global SwaggerUi, log, ApiKeyAuthorization, hljs, window, $ */
$(function() {
$.getJSON('config.json', function(config) {
log(config);
loadSwaggerUi(config);
});
var accessToken;
function loadSwaggerUi(config) {
window.swaggerUi = new SwaggerUi({
url: config.url || '/swagger/resources',
apiKey: '',
dom_id: 'swagger-ui-container',
supportHeaderParams: true,
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function(swaggerApi, swaggerUi) {
log('Loaded SwaggerUI');
log(swaggerApi);
log(swaggerUi);
$('pre code').each(function(i, e) {hljs.highlightBlock(e); });
},
onFailure: function(data) {
log('Unable to Load SwaggerUI');
log(data);
},
docExpansion: 'none'
});
$('#explore').click(setAccessToken);
$('#api_selector').submit(setAccessToken);
$('#input_accessToken').keyup(onInputChange);
window.swaggerUi.load();
}
function setAccessToken(e) {
e.stopPropagation(); // Don't let the default #explore handler fire
e.preventDefault();
var key = $('#input_accessToken')[0].value;
log('key: ' + key);
if(key && key.trim() !== '') {
log('added accessToken ' + key);
window.authorizations.add('key', new ApiKeyAuthorization('access_token', key, 'query'));
accessToken = key;
$('.accessTokenDisplay').text('Token Set.').addClass('set');
$('.accessTokenDisplay').attr('data-tooltip', 'Current Token: ' + key);
}
}
function onInputChange(e) {
var el = e.currentTarget;
var key = $(e.currentTarget)[0].value;
if (!key || key.trim === '') return;
if (accessToken !== key) {
$('.accessTokenDisplay').text('Token changed; submit to confirm.');
} else {
$('.accessTokenDisplay').text('Token Set.');
}
}
});

View File

@ -1,782 +0,0 @@
// Generated by CoffeeScript 1.4.0
(function() {
var SwaggerApi, SwaggerModel, SwaggerModelProperty, SwaggerOperation, SwaggerRequest, SwaggerResource,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
SwaggerApi = (function() {
SwaggerApi.prototype.discoveryUrl = "http://api.wordnik.com/v4/resources.json";
SwaggerApi.prototype.debug = false;
SwaggerApi.prototype.api_key = null;
SwaggerApi.prototype.basePath = null;
function SwaggerApi(options) {
if (options == null) {
options = {};
}
if (options.discoveryUrl != null) {
this.discoveryUrl = options.discoveryUrl;
}
if (options.debug != null) {
this.debug = options.debug;
}
this.apiKeyName = options.apiKeyName != null ? options.apiKeyName : 'api_key';
if (options.apiKey != null) {
this.api_key = options.apiKey;
}
if (options.api_key != null) {
this.api_key = options.api_key;
}
if (options.verbose != null) {
this.verbose = options.verbose;
}
this.supportHeaderParams = options.supportHeaderParams != null ? options.supportHeaderParams : false;
this.supportedSubmitMethods = options.supportedSubmitMethods != null ? options.supportedSubmitMethods : ['get'];
if (options.success != null) {
this.success = options.success;
}
this.failure = options.failure != null ? options.failure : function() {};
this.progress = options.progress != null ? options.progress : function() {};
this.headers = options.headers != null ? options.headers : {};
this.booleanValues = options.booleanValues != null ? options.booleanValues : new Array('true', 'false');
this.discoveryUrl = this.suffixApiKey(this.discoveryUrl);
if (options.success != null) {
this.build();
}
}
SwaggerApi.prototype.build = function() {
var _this = this;
this.progress('fetching resource list: ' + this.discoveryUrl);
return jQuery.getJSON(this.discoveryUrl, function(response) {
var res, resource, _i, _j, _len, _len1, _ref, _ref1;
if (response.apiVersion != null) {
_this.apiVersion = response.apiVersion;
}
if ((response.basePath != null) && jQuery.trim(response.basePath).length > 0) {
_this.basePath = response.basePath;
if (_this.basePath.match(/^HTTP/i) == null) {
_this.fail("discoveryUrl basePath must be a URL.");
}
_this.basePath = _this.basePath.replace(/\/$/, '');
} else {
_this.basePath = _this.discoveryUrl.substring(0, _this.discoveryUrl.lastIndexOf('/'));
log('derived basepath from discoveryUrl as ' + _this.basePath);
}
_this.apis = {};
_this.apisArray = [];
if (response.resourcePath != null) {
_this.resourcePath = response.resourcePath;
res = null;
_ref = response.apis;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
resource = _ref[_i];
if (res === null) {
res = new SwaggerResource(resource, _this);
} else {
res.addOperations(resource.path, resource.operations);
}
}
if (res != null) {
_this.apis[res.name] = res;
_this.apisArray.push(res);
res.ready = true;
_this.selfReflect();
}
} else {
_ref1 = response.apis;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
resource = _ref1[_j];
res = new SwaggerResource(resource, _this);
_this.apis[res.name] = res;
_this.apisArray.push(res);
}
}
return _this;
}).error(function(error) {
if (_this.discoveryUrl.substring(0, 4) !== 'http') {
return _this.fail('Please specify the protocol for ' + _this.discoveryUrl);
} else if (error.status === 0) {
return _this.fail('Can\'t read from server. It may not have the appropriate access-control-origin settings.');
} else if (error.status === 404) {
return _this.fail('Can\'t read swagger JSON from ' + _this.discoveryUrl);
} else {
return _this.fail(error.status + ' : ' + error.statusText + ' ' + _this.discoveryUrl);
}
});
};
SwaggerApi.prototype.selfReflect = function() {
var resource, resource_name, _ref;
if (this.apis == null) {
return false;
}
_ref = this.apis;
for (resource_name in _ref) {
resource = _ref[resource_name];
if (resource.ready == null) {
return false;
}
}
this.setConsolidatedModels();
this.ready = true;
if (this.success != null) {
return this.success();
}
};
SwaggerApi.prototype.fail = function(message) {
this.failure(message);
throw message;
};
SwaggerApi.prototype.setConsolidatedModels = function() {
var model, modelName, resource, resource_name, _i, _len, _ref, _ref1, _results;
this.modelsArray = [];
this.models = {};
_ref = this.apis;
for (resource_name in _ref) {
resource = _ref[resource_name];
for (modelName in resource.models) {
if (!(this.models[modelName] != null)) {
this.models[modelName] = resource.models[modelName];
this.modelsArray.push(resource.models[modelName]);
}
}
}
_ref1 = this.modelsArray;
_results = [];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
model = _ref1[_i];
_results.push(model.setReferencedModels(this.models));
}
return _results;
};
SwaggerApi.prototype.suffixApiKey = function(url) {
var sep;
if ((this.api_key != null) && jQuery.trim(this.api_key).length > 0 && (url != null)) {
sep = url.indexOf('?') > 0 ? '&' : '?';
return url + sep + this.apiKeyName + '=' + this.api_key;
} else {
return url;
}
};
SwaggerApi.prototype.help = function() {
var operation, operation_name, parameter, resource, resource_name, _i, _len, _ref, _ref1, _ref2;
_ref = this.apis;
for (resource_name in _ref) {
resource = _ref[resource_name];
console.log(resource_name);
_ref1 = resource.operations;
for (operation_name in _ref1) {
operation = _ref1[operation_name];
console.log(" " + operation.nickname);
_ref2 = operation.parameters;
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
parameter = _ref2[_i];
console.log(" " + parameter.name + (parameter.required ? ' (required)' : '') + " - " + parameter.description);
}
}
}
return this;
};
return SwaggerApi;
})();
SwaggerResource = (function() {
function SwaggerResource(resourceObj, api) {
var parts,
_this = this;
this.api = api;
this.path = this.api.resourcePath != null ? this.api.resourcePath : resourceObj.path;
this.description = resourceObj.description;
parts = this.path.split("/");
this.name = parts[parts.length - 1].replace('.{format}', '');
this.basePath = this.api.basePath;
this.operations = {};
this.operationsArray = [];
this.modelsArray = [];
this.models = {};
if ((resourceObj.operations != null) && (this.api.resourcePath != null)) {
this.api.progress('reading resource ' + this.name + ' models and operations');
this.addModels(resourceObj.models);
this.addOperations(resourceObj.path, resourceObj.operations);
this.api[this.name] = this;
} else {
if (this.path == null) {
this.api.fail("SwaggerResources must have a path.");
}
this.url = this.api.suffixApiKey(this.api.basePath + this.path.replace('{format}', 'json'));
this.api.progress('fetching resource ' + this.name + ': ' + this.url);
jQuery.getJSON(this.url, function(response) {
var endpoint, _i, _len, _ref;
if ((response.basePath != null) && jQuery.trim(response.basePath).length > 0) {
_this.basePath = response.basePath;
_this.basePath = _this.basePath.replace(/\/$/, '');
}
_this.addModels(response.models);
if (response.apis) {
_ref = response.apis;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
endpoint = _ref[_i];
_this.addOperations(endpoint.path, endpoint.operations);
}
}
_this.api[_this.name] = _this;
_this.ready = true;
return _this.api.selfReflect();
}).error(function(error) {
return _this.api.fail("Unable to read api '" + _this.name + "' from path " + _this.url + " (server returned " + error.statusText + ")");
});
}
}
SwaggerResource.prototype.addModels = function(models) {
var model, modelName, swaggerModel, _i, _len, _ref, _results;
if (models != null) {
for (modelName in models) {
if (!(this.models[modelName] != null)) {
swaggerModel = new SwaggerModel(modelName, models[modelName]);
this.modelsArray.push(swaggerModel);
this.models[modelName] = swaggerModel;
}
}
_ref = this.modelsArray;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
model = _ref[_i];
_results.push(model.setReferencedModels(this.models));
}
return _results;
}
};
SwaggerResource.prototype.addOperations = function(resource_path, ops) {
var consumes, err, errorResponses, o, op, _i, _j, _len, _len1, _results;
if (ops) {
_results = [];
for (_i = 0, _len = ops.length; _i < _len; _i++) {
o = ops[_i];
consumes = o.consumes;
if (o.supportedContentTypes) {
consumes = o.supportedContentTypes;
}
errorResponses = o.responseMessages;
if (errorResponses) {
for (_j = 0, _len1 = errorResponses.length; _j < _len1; _j++) {
err = errorResponses[_j];
err.reason = err.message;
}
}
if (o.errorResponses) {
errorResponses = o.errorResponses;
}
op = new SwaggerOperation(o.nickname, resource_path, o.httpMethod, o.parameters, o.summary, o.notes, o.responseClass, errorResponses, this, o.consumes, o.produces);
this.operations[op.nickname] = op;
_results.push(this.operationsArray.push(op));
}
return _results;
}
};
SwaggerResource.prototype.help = function() {
var operation, operation_name, parameter, _i, _len, _ref, _ref1;
_ref = this.operations;
for (operation_name in _ref) {
operation = _ref[operation_name];
console.log(" " + operation.nickname);
_ref1 = operation.parameters;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
parameter = _ref1[_i];
console.log(" " + parameter.name + (parameter.required ? ' (required)' : '') + " - " + parameter.description);
}
}
return this;
};
return SwaggerResource;
})();
SwaggerModel = (function() {
function SwaggerModel(modelName, obj) {
var propertyName;
this.name = obj.id != null ? obj.id : modelName;
this.properties = [];
for (propertyName in obj.properties) {
this.properties.push(new SwaggerModelProperty(propertyName, obj.properties[propertyName]));
}
}
SwaggerModel.prototype.setReferencedModels = function(allModels) {
var prop, _i, _len, _ref, _results;
_ref = this.properties;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
prop = _ref[_i];
if (allModels[prop.dataType] != null) {
_results.push(prop.refModel = allModels[prop.dataType]);
} else if ((prop.refDataType != null) && (allModels[prop.refDataType] != null)) {
_results.push(prop.refModel = allModels[prop.refDataType]);
} else {
_results.push(void 0);
}
}
return _results;
};
SwaggerModel.prototype.getMockSignature = function(modelsToIgnore) {
var classClose, classOpen, prop, propertiesStr, returnVal, strong, strongClose, stronger, _i, _j, _len, _len1, _ref, _ref1;
propertiesStr = [];
_ref = this.properties;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
prop = _ref[_i];
propertiesStr.push(prop.toString());
}
strong = '<span class="strong">';
stronger = '<span class="stronger">';
strongClose = '</span>';
classOpen = strong + this.name + ' {' + strongClose;
classClose = strong + '}' + strongClose;
returnVal = classOpen + '<div>' + propertiesStr.join(',</div><div>') + '</div>' + classClose;
if (!modelsToIgnore) {
modelsToIgnore = [];
}
modelsToIgnore.push(this);
_ref1 = this.properties;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
prop = _ref1[_j];
if ((prop.refModel != null) && (modelsToIgnore.indexOf(prop.refModel)) === -1) {
returnVal = returnVal + ('<br>' + prop.refModel.getMockSignature(modelsToIgnore));
}
}
return returnVal;
};
SwaggerModel.prototype.createJSONSample = function(modelsToIgnore) {
var prop, result, _i, _len, _ref;
result = {};
modelsToIgnore = modelsToIgnore || [];
modelsToIgnore.push(this.name);
_ref = this.properties;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
prop = _ref[_i];
result[prop.name] = prop.getSampleValue(modelsToIgnore);
}
return result;
};
return SwaggerModel;
})();
SwaggerModelProperty = (function() {
function SwaggerModelProperty(name, obj) {
this.name = name;
this.dataType = obj.type;
this.isCollection = this.dataType && (this.dataType.toLowerCase() === 'array' || this.dataType.toLowerCase() === 'list' || this.dataType.toLowerCase() === 'set');
this.descr = obj.description;
this.required = obj.required;
if (obj.items != null) {
if (obj.items.type != null) {
this.refDataType = obj.items.type;
}
if (obj.items.$ref != null) {
this.refDataType = obj.items.$ref;
}
}
this.dataTypeWithRef = this.refDataType != null ? this.dataType + '[' + this.refDataType + ']' : this.dataType;
if (obj.allowableValues != null) {
this.valueType = obj.allowableValues.valueType;
this.values = obj.allowableValues.values;
if (this.values != null) {
this.valuesString = "'" + this.values.join("' or '") + "'";
}
}
}
SwaggerModelProperty.prototype.getSampleValue = function(modelsToIgnore) {
var result;
if ((this.refModel != null) && (modelsToIgnore.indexOf(this.refModel.name) === -1)) {
result = this.refModel.createJSONSample(modelsToIgnore);
} else {
if (this.isCollection) {
result = this.refDataType;
} else {
result = this.dataType;
}
}
if (this.isCollection) {
return [result];
} else {
return result;
}
};
SwaggerModelProperty.prototype.toString = function() {
var req, str;
req = this.required ? 'propReq' : 'propOpt';
str = '<span class="propName ' + req + '">' + this.name + '</span> (<span class="propType">' + this.dataTypeWithRef + '</span>';
if (!this.required) {
str += ', <span class="propOptKey">optional</span>';
}
str += ')';
if (this.values != null) {
str += " = <span class='propVals'>['" + this.values.join("' or '") + "']</span>";
}
if (this.descr != null) {
str += ': <span class="propDesc">' + this.descr + '</span>';
}
return str;
};
return SwaggerModelProperty;
})();
SwaggerOperation = (function() {
function SwaggerOperation(nickname, path, httpMethod, parameters, summary, notes, responseClass, errorResponses, resource, consumes, produces) {
var parameter, v, _i, _j, _len, _len1, _ref, _ref1, _ref2,
_this = this;
this.nickname = nickname;
this.path = path;
this.httpMethod = httpMethod;
this.parameters = parameters != null ? parameters : [];
this.summary = summary;
this.notes = notes;
this.responseClass = responseClass;
this.errorResponses = errorResponses;
this.resource = resource;
this.consumes = consumes;
this.produces = produces;
this["do"] = __bind(this["do"], this);
if (this.nickname == null) {
this.resource.api.fail("SwaggerOperations must have a nickname.");
}
if (this.path == null) {
this.resource.api.fail("SwaggerOperation " + nickname + " is missing path.");
}
if (this.httpMethod == null) {
this.resource.api.fail("SwaggerOperation " + nickname + " is missing httpMethod.");
}
this.path = this.path.replace('{format}', 'json');
this.httpMethod = this.httpMethod.toLowerCase();
this.isGetMethod = this.httpMethod === "get";
this.resourceName = this.resource.name;
if (((_ref = this.responseClass) != null ? _ref.toLowerCase() : void 0) === 'void') {
this.responseClass = void 0;
}
if (this.responseClass != null) {
this.responseClassSignature = this.getSignature(this.responseClass, this.resource.models);
this.responseSampleJSON = this.getSampleJSON(this.responseClass, this.resource.models);
}
this.errorResponses = this.errorResponses || [];
_ref1 = this.parameters;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
parameter = _ref1[_i];
parameter.name = parameter.name || parameter.dataType;
if (parameter.dataType.toLowerCase() === 'boolean') {
parameter.allowableValues = {};
parameter.allowableValues.values = this.resource.api.booleanValues;
}
parameter.signature = this.getSignature(parameter.dataType, this.resource.models);
parameter.sampleJSON = this.getSampleJSON(parameter.dataType, this.resource.models);
if (parameter.allowableValues != null) {
if (parameter.allowableValues.valueType === "RANGE") {
parameter.isRange = true;
} else {
parameter.isList = true;
}
if (parameter.allowableValues.values != null) {
parameter.allowableValues.descriptiveValues = [];
_ref2 = parameter.allowableValues.values;
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
v = _ref2[_j];
if ((parameter.defaultValue != null) && parameter.defaultValue === v) {
parameter.allowableValues.descriptiveValues.push({
value: v,
isDefault: true
});
} else {
parameter.allowableValues.descriptiveValues.push({
value: v,
isDefault: false
});
}
}
}
}
}
this.resource[this.nickname] = function(args, callback, error) {
return _this["do"](args, callback, error);
};
}
SwaggerOperation.prototype.isListType = function(dataType) {
if (dataType.indexOf('[') >= 0) {
return dataType.substring(dataType.indexOf('[') + 1, dataType.indexOf(']'));
} else {
return void 0;
}
};
SwaggerOperation.prototype.getSignature = function(dataType, models) {
var isPrimitive, listType;
listType = this.isListType(dataType);
isPrimitive = ((listType != null) && models[listType]) || (models[dataType] != null) ? false : true;
if (isPrimitive) {
return dataType;
} else {
if (listType != null) {
return models[listType].getMockSignature();
} else {
return models[dataType].getMockSignature();
}
}
};
SwaggerOperation.prototype.getSampleJSON = function(dataType, models) {
var isPrimitive, listType, val;
listType = this.isListType(dataType);
isPrimitive = ((listType != null) && models[listType]) || (models[dataType] != null) ? false : true;
val = isPrimitive ? void 0 : (listType != null ? models[listType].createJSONSample() : models[dataType].createJSONSample());
if (val) {
val = listType ? [val] : val;
return JSON.stringify(val, null, 2);
}
};
SwaggerOperation.prototype["do"] = function(args, callback, error) {
var body, headers;
if (args == null) {
args = {};
}
if ((typeof args) === "function") {
error = callback;
callback = args;
args = {};
}
if (error == null) {
error = function(xhr, textStatus, error) {
return console.log(xhr, textStatus, error);
};
}
if (callback == null) {
callback = function(data) {
return console.log(data);
};
}
if (args.headers != null) {
headers = args.headers;
delete args.headers;
}
if (args.body != null) {
body = args.body;
delete args.body;
}
return new SwaggerRequest(this.httpMethod, this.urlify(args), headers, body, callback, error, this);
};
SwaggerOperation.prototype.pathJson = function() {
return this.path.replace("{format}", "json");
};
SwaggerOperation.prototype.pathXml = function() {
return this.path.replace("{format}", "xml");
};
SwaggerOperation.prototype.urlify = function(args, includeApiKey) {
var param, queryParams, reg, url, _i, _len, _ref;
if (includeApiKey == null) {
includeApiKey = true;
}
url = this.resource.basePath + this.pathJson();
_ref = this.parameters;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
param = _ref[_i];
if (param.paramType === 'path') {
if (args[param.name]) {
reg = new RegExp('\{' + param.name + '[^\}]*\}', 'gi');
url = url.replace(reg, encodeURIComponent(args[param.name]));
delete args[param.name];
} else {
throw "" + param.name + " is a required path param.";
}
}
}
if (includeApiKey && (this.resource.api.api_key != null) && this.resource.api.api_key.length > 0) {
args[this.apiKeyName] = this.resource.api.api_key;
}
if (this.supportHeaderParams()) {
queryParams = jQuery.param(this.getQueryParams(args, includeApiKey));
} else {
queryParams = jQuery.param(this.getQueryAndHeaderParams(args, includeApiKey));
}
if ((queryParams != null) && queryParams.length > 0) {
url += "?" + queryParams;
}
return url;
};
SwaggerOperation.prototype.supportHeaderParams = function() {
return this.resource.api.supportHeaderParams;
};
SwaggerOperation.prototype.supportedSubmitMethods = function() {
return this.resource.api.supportedSubmitMethods;
};
SwaggerOperation.prototype.getQueryAndHeaderParams = function(args, includeApiKey) {
if (includeApiKey == null) {
includeApiKey = true;
}
return this.getMatchingParams(['query', 'header'], args, includeApiKey);
};
SwaggerOperation.prototype.getQueryParams = function(args, includeApiKey) {
if (includeApiKey == null) {
includeApiKey = true;
}
return this.getMatchingParams(['query'], args, includeApiKey);
};
SwaggerOperation.prototype.getHeaderParams = function(args, includeApiKey) {
if (includeApiKey == null) {
includeApiKey = true;
}
return this.getMatchingParams(['header'], args, includeApiKey);
};
SwaggerOperation.prototype.getMatchingParams = function(paramTypes, args, includeApiKey) {
var matchingParams, name, param, value, _i, _len, _ref, _ref1;
matchingParams = {};
_ref = this.parameters;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
param = _ref[_i];
if ((jQuery.inArray(param.paramType, paramTypes) >= 0) && args[param.name]) {
matchingParams[param.name] = args[param.name];
}
}
if (includeApiKey && (this.resource.api.api_key != null) && this.resource.api.api_key.length > 0) {
matchingParams[this.resource.api.apiKeyName] = this.resource.api.api_key;
}
if (jQuery.inArray('header', paramTypes) >= 0) {
_ref1 = this.resource.api.headers;
for (name in _ref1) {
value = _ref1[name];
matchingParams[name] = value;
}
}
return matchingParams;
};
SwaggerOperation.prototype.help = function() {
var parameter, _i, _len, _ref;
_ref = this.parameters;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
parameter = _ref[_i];
console.log(" " + parameter.name + (parameter.required ? ' (required)' : '') + " - " + parameter.description);
}
return this;
};
return SwaggerOperation;
})();
SwaggerRequest = (function() {
function SwaggerRequest(type, url, headers, body, successCallback, errorCallback, operation) {
var obj,
_this = this;
this.type = type;
this.url = url;
this.headers = headers;
this.body = body;
this.successCallback = successCallback;
this.errorCallback = errorCallback;
this.operation = operation;
if (this.type == null) {
throw "SwaggerRequest type is required (get/post/put/delete).";
}
if (this.url == null) {
throw "SwaggerRequest url is required.";
}
if (this.successCallback == null) {
throw "SwaggerRequest successCallback is required.";
}
if (this.errorCallback == null) {
throw "SwaggerRequest error callback is required.";
}
if (this.operation == null) {
throw "SwaggerRequest operation is required.";
}
if (this.operation.resource.api.verbose) {
console.log(this.asCurl());
}
this.headers || (this.headers = {});
if (this.operation.resource.api.api_key != null) {
this.headers[this.apiKeyName] = this.operation.resource.api.api_key;
}
if (this.headers.mock == null) {
obj = {
type: this.type,
url: this.url,
data: JSON.stringify(this.body),
dataType: 'json',
error: function(xhr, textStatus, error) {
return _this.errorCallback(xhr, textStatus, error);
},
success: function(data) {
return _this.successCallback(data);
}
};
if (obj.type.toLowerCase() === "post" || obj.type.toLowerCase() === "put") {
obj.contentType = "application/json";
}
jQuery.ajax(obj);
}
}
SwaggerRequest.prototype.asCurl = function() {
var header_args, k, v;
header_args = (function() {
var _ref, _results;
_ref = this.headers;
_results = [];
for (k in _ref) {
v = _ref[k];
_results.push("--header \"" + k + ": " + v + "\"");
}
return _results;
}).call(this);
return "curl " + (header_args.join(" ")) + " " + this.url;
};
return SwaggerRequest;
})();
window.SwaggerApi = SwaggerApi;
window.SwaggerResource = SwaggerResource;
window.SwaggerOperation = SwaggerOperation;
window.SwaggerRequest = SwaggerRequest;
window.SwaggerModelProperty = SwaggerModelProperty;
}).call(this);

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

22
test/.jshintrc Normal file
View File

@ -0,0 +1,22 @@
{
"node": true,
"camelcase" : true,
"eqnull" : true,
"indent": 2,
"undef": true,
"quotmark": "single",
"maxlen": 80,
"trailing": true,
"newcap": true,
"nonew": true,
"undef": false,
"globals" : {
/* MOCHA */
"describe" : false,
"it" : false,
"before" : false,
"beforeEach" : false,
"after" : false,
"afterEach" : false
}
}

View File

@ -24,7 +24,8 @@ describe('explorer', function() {
.end(function(err, res) { .end(function(err, res) {
if (err) throw err; if (err) throw err;
assert(!!~res.text.indexOf('<title>StrongLoop API Explorer</title>'), 'text does not contain expected string'); assert(!!~res.text.indexOf('<title>StrongLoop API Explorer</title>'),
'text does not contain expected string');
done(); done();
}); });
}); });
@ -37,24 +38,24 @@ describe('explorer', function() {
.end(function(err, res) { .end(function(err, res) {
if (err) return done(err); if (err) return done(err);
expect(res.body).to expect(res.body).to
.have.property('discoveryUrl', '/swagger/resources'); .have.property('url', '/explorer/resources');
done(); done();
}); });
}); });
}); });
describe('with custom baseUrl', function() { describe('with custom explorer base', function() {
beforeEach(givenLoopBackAppWithExplorer('/api')); beforeEach(givenLoopBackAppWithExplorer('/swagger'));
it('should serve correct swagger-ui config', function(done) { it('should serve correct swagger-ui config', function(done) {
request(this.app) request(this.app)
.get('/explorer/config.json') .get('/swagger/config.json')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(function(err, res) { .end(function(err, res) {
if (err) return done(err); if (err) return done(err);
expect(res.body).to expect(res.body).to
.have.property('discoveryUrl', '/api/swagger/resources'); .have.property('url', '/swagger/resources');
done(); done();
}); });
}); });
@ -72,36 +73,26 @@ describe('explorer', function() {
.end(function(err, res) { .end(function(err, res) {
if (err) return done(err); if (err) return done(err);
expect(res.body).to expect(res.body).to
.have.property('discoveryUrl', '/rest-api-root/swagger/resources'); .have.property('url', '/explorer/resources');
done(); done();
}); });
}); });
}); });
function givenLoopBackAppWithExplorer(restUrlBase) { function givenLoopBackAppWithExplorer(explorerBase) {
return function(done) { return function(done) {
var app = this.app = loopback(); var app = this.app = loopback();
configureRestApiAndExplorer(app, restUrlBase); configureRestApiAndExplorer(app, explorerBase);
done(); done();
}; };
} }
function configureRestApiAndExplorer(app, restUrlBase) { function configureRestApiAndExplorer(app, explorerBase) {
var Product = loopback.Model.extend('product'); var Product = loopback.Model.extend('product');
Product.attachTo(loopback.memory()); Product.attachTo(loopback.memory());
app.model(Product); app.model(Product);
if (restUrlBase) { app.use(explorerBase || '/explorer', explorer(app));
app.use(restUrlBase, loopback.rest()); app.use(app.get('restApiRoot') || '/', loopback.rest());
app.use('/explorer', explorer(app, { basePath: restUrlBase }));
} else {
// LoopBack REST adapter owns the whole URL space and does not
// let other middleware handle same URLs.
// It's possible to circumvent this measure by installing
// the explorer middleware before the REST middleware.
// This way we can acess `/explorer` even when REST is mounted at `/`
app.use('/explorer', explorer(app));
app.use(app.get('restApiRoot') || '/', loopback.rest());
}
} }
}); });

101
test/model-helper.test.js Normal file
View File

@ -0,0 +1,101 @@
'use strict';
var modelHelper = require('../lib/model-helper');
var expect = require('chai').expect;
describe('model-helper', function() {
describe('properly converts LDL definitions to swagger types', function() {
it('converts constructor types', function() {
var def = getDefinition({
str: String, // 'string'
num: Number, // {type: 'number', format: 'double'}
date: Date, // {type: 'string', format: 'date'}
bool: Boolean, // 'boolean'
buf: Buffer // {type: 'string', format: 'byte'}
});
var props = def.properties;
expect(props.str).to.eql({ type: 'string' });
expect(props.num).to.eql({ type: 'number', format: 'double' });
expect(props.date).eql({ type: 'string', format: 'date' });
expect(props.bool).to.eql({ type: 'boolean' });
expect(props.buf).to.eql({ type: 'string', format: 'byte' });
});
it('converts string types', function() {
var def = getDefinition({
str: 'string', // 'string'
num: 'number', // {type: 'number', format: 'double'}
date: 'date', // {type: 'string', format: 'date'}
bool: 'boolean', // 'boolean'
buf: 'buffer' // {type: 'string', format: 'byte'}
});
var props = def.properties;
expect(props.str).to.eql({ type: 'string' });
expect(props.num).to.eql({ type: 'number', format: 'double' });
expect(props.date).eql({ type: 'string', format: 'date' });
expect(props.bool).to.eql({ type: 'boolean' });
expect(props.buf).to.eql({ type: 'string', format: 'byte' });
});
describe('array definitions', function() {
// There are three types we want to checK:
// [String]
// ["string"],
// [{type: String, ...}]
it('converts [Constructor] type', function() {
var def = getDefinition({
array: [String]
});
var props = def.properties;
expect(props.array).to.eql({ type: 'array', items: {
type: 'string'
}});
});
it('converts ["string"] type', function() {
var def = getDefinition({
array: ['string']
});
var props = def.properties;
expect(props.array).to.eql({ type: 'array', items: {
type: 'string'
}});
});
it('converts [{type: "string", length: 64}] type', function() {
var def = getDefinition({
array: [{type: 'string', length: 64}]
});
var props = def.properties;
expect(props.array).to.eql({ type: 'array', items: {
type: 'string',
length: 64
}});
});
it('converts [{type: "date"}] type (with `format`)', function() {
var def = getDefinition({
array: [{type: 'date'}]
});
var props = def.properties;
expect(props.array).to.eql({ type: 'array', items: {
type: 'string', format: 'date'
}});
});
});
});
});
// Simulates the format of a remoting class.
function getDefinition(model) {
Object.keys(model).forEach(function(name) {
model[name] = {type: model[name]};
});
var aClass = {
ctor: {
definition: {
name: 'testModel',
properties: model
}
}
};
return modelHelper.generateModelDefinition(aClass).testModel;
}

65
test/route-helper.test.js Normal file
View File

@ -0,0 +1,65 @@
'use strict';
var routeHelper = require('../lib/route-helper');
var expect = require('chai').expect;
var _defaults = require('lodash.defaults');
describe('route-helper', function() {
it('returns "object" when a route has multiple return values', function() {
var doc = createAPIDoc({
returns: [
{ arg: 'max', type: 'number' },
{ arg: 'min', type: 'number' },
{ arg: 'avg', type: 'number' }
]
});
expect(doc.operations[0].type).to.equal('object');
});
it('converts path params when they exist in the route name', function() {
var doc = createAPIDoc({
accepts: [
{arg: 'id', type: 'string'}
],
path: '/test/:id'
});
var paramDoc = doc.operations[0].parameters[0];
expect(paramDoc.paramType).to.equal('path');
expect(paramDoc.name).to.equal('id');
expect(paramDoc.required).to.equal(false);
});
// FIXME need regex in routeHelper.acceptToParameter
xit('won\'t convert path params when they don\'t exist in the route name', function() {
var doc = createAPIDoc({
accepts: [
{arg: 'id', type: 'string'}
],
path: '/test/:identifier'
});
var paramDoc = doc.operations[0].parameters[0];
expect(paramDoc.paramType).to.equal('query');
});
it('correctly coerces param types', function() {
var doc = createAPIDoc({
accepts: [
{arg: 'binaryData', type: 'buffer'}
]
});
var paramDoc = doc.operations[0].parameters[0];
expect(paramDoc.paramType).to.equal('query');
expect(paramDoc.type).to.equal('string');
expect(paramDoc.format).to.equal('byte');
});
});
// Easy wrapper around createRoute
function createAPIDoc(def) {
return routeHelper.routeToAPIDoc(_defaults(def, {
path: '/test',
verb: 'GET',
method: 'test.get'
}));
}

135
test/swagger.test.js Normal file
View File

@ -0,0 +1,135 @@
'use strict';
var url = require('url');
var urlJoin = require('../lib/url-join');
var loopback = require('loopback');
var express = require('express');
var swagger = require('../lib/swagger');
var request = require('supertest');
var expect = require('chai').expect;
describe('swagger definition', function() {
describe('basePath', function() {
// No basepath on resource doc in 1.2
it('no longer exists on resource doc', function(done) {
var app = mountSwagger();
var getReq = getSwaggerResources(app);
getReq.end(function(err, res) {
if (err) return done(err);
expect(res.body.basePath).to.equal(undefined);
done();
});
});
it('is "http://{host}/api" by default', function(done) {
var app = mountSwagger();
var getReq = getAPIDeclaration(app, 'products');
getReq.end(function(err, res) {
if (err) return done(err);
expect(res.body.basePath).to.equal(url.resolve(getReq.url, '/api'));
done();
});
});
it('is "http://{host}/{basePath}" when basePath is a path', function(done){
var app = mountSwagger({ basePath: '/api-root'});
var getReq = getAPIDeclaration(app, 'products');
getReq.end(function(err, res) {
if (err) return done(err);
var apiRoot = url.resolve(getReq.url, '/api-root');
expect(res.body.basePath).to.equal(apiRoot);
done();
});
});
it('infers API basePath from app', function(done){
var app = mountSwagger({}, {apiRoot: '/custom-api-root'});
var getReq = getAPIDeclaration(app, 'products');
getReq.end(function(err, res) {
if (err) return done(err);
var apiRoot = url.resolve(getReq.url, '/custom-api-root');
expect(res.body.basePath).to.equal(apiRoot);
done();
});
});
it('is reachable when explorer mounting location is changed', function(done){
var explorerRoot = '/erforscher';
var app = mountSwagger({}, {explorerRoot: explorerRoot});
var getReq = getSwaggerResources(app, explorerRoot, 'products');
getReq.end(function(err, res) {
if (err) return done(err);
expect(res.body.basePath).to.be.a('string');
done();
});
});
});
describe('Model definition attributes', function() {
it('Properly defines basic attributes', function(done) {
var app = mountSwagger();
var getReq = getAPIDeclaration(app, 'products');
getReq.end(function(err, res) {
if (err) return done(err);
var data = res.body.models.product;
expect(data.id).to.equal('product');
expect(data.required.sort()).to.eql(['aNum', 'foo'].sort());
expect(data.properties.foo.type).to.equal('string');
expect(data.properties.bar.type).to.equal('string');
expect(data.properties.aNum.type).to.equal('number');
// These will be Numbers for Swagger 2.0
expect(data.properties.aNum.minimum).to.equal('1');
expect(data.properties.aNum.maximum).to.equal('10');
// Should be Number even in 1.2
expect(data.properties.aNum.defaultValue).to.equal(5);
done();
});
});
});
function getSwaggerResources(app, restPath, classPath) {
return request(app)
.get(urlJoin(restPath || '/explorer', '/resources', classPath || ''))
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
}
function getAPIDeclaration(app, className) {
return getSwaggerResources(app, '', urlJoin('/', className));
}
function mountSwagger(options, addlOptions) {
addlOptions = addlOptions || {};
var app = createLoopbackAppWithModel(addlOptions.apiRoot);
var swaggerApp = express();
swagger(app, swaggerApp, options);
app.use(addlOptions.explorerRoot || '/explorer', swaggerApp);
return app;
}
function createLoopbackAppWithModel(apiRoot) {
var app = loopback();
var Product = loopback.Model.extend('product', {
foo: {type: 'string', required: true},
bar: 'string',
aNum: {type: 'number', min: 1, max: 10, required: true, default: 5}
});
Product.attachTo(loopback.memory());
app.model(Product);
// Simulate a restApiRoot set in config
app.set('restApiRoot', apiRoot || '/api');
app.use(app.get('restApiRoot'), loopback.rest());
return app;
}
});