Initial commit

This commit is contained in:
Kam 2016-01-12 20:38:19 +10:00
commit 48905fae4a
82 changed files with 7696 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
~*
*~
bak
node_modules
*cache
npm-debug.log

78
README.md Normal file
View File

@ -0,0 +1,78 @@
# Spectacle
[Spectacle](http://sourcey.com/spectacle) is a static documentation generator that lets you autogenerate your documentation from a OpenAPI/Swagger 2.0 JSON specification file. With Spectacle you can have beautiful HTML5 documentation for your API ready to be deployed in a matter of minutes.
Here's what Spectacle features:
* OpenAPI/Swagger 2.0 support
* Mobile friendly responsive HTML5 and CSS3 grid layout
* Modern and readable design
* Embedded option to generate docs without a layout for convenient integration with your own website
* Development mode with file watcher and live reload for regenerating docs when your spec is updated
* Simple and extendable Handlebars templates and SCSS styles
## Getting Started
Simply install Spectacle from `npm` like so:
```bash
npm install spectacle-docs
```
Next pass your `swagger.json` document use the CLI to generate your documentation.
```bash
spectacle your_swagger_api.json
```
Your documentation will be located in the `/public` directory. You can either copy the generated HTML to your web server, or view your docs by starting the internal web server like so:
```bash
spectacle -s
```
Now point your browser to [http://localhost:4400/](http://localhost:4400/) in order to view your docs.
## Configuration Options
The basic CLI options are detailed below:
```bash
$ spectacle -h
Usage: cli spactacle [options] <specfile>
Options:
-h, --help output usage information
-V, --version output the version number
-A, --skip-assets omit CSS and JavaScript generation (default: false)
-e, --embeddable omit the HTML <body/> and generate the documentation content only (default: false)
-d, --development-mode start HTTP server with the file watcher and live reload (default: false)
-s, --start-server start the HTTP server without any development features
-p, --port <dir> the port number for the HTTP server to listen on (default: 4400)
-a, --app-dir <dir> the application source directory (default: ./app)
-t, --target-dir <dir> the target build directory (default: ./public)
```
Most options are self explanatory, but the following options warrant some further explanation:
* **--development-mode**: This option starts a development server with a file watcher and live reload, and will automatically regenerate your docs when any of your spec or app files change.
* **--start-server **: This option starts a production server without any development options enabled that serves the contents of your `--target-dir`.
* **--embeddable**: This option lets you build a minimal version of the documentation without the HTML `<body>` tags, so you can embed Spectacle into your own website template. More info on [custom builds](#custom-builds) here.
* **--app-dir**: This option overrides the default directory which contains all the Handlebars templates, SCSS, and JavaScript source files. This option is useful for development because you can copy the contents of `app` to a remote location or a separate repo for custom builds.
* **--target-dir**: This option specifies where the generated documentation HTML files will be output.
## Custom Builds
The best option for building your own custom functionality into Spectacle is to [fork Spectacle on GitHub](https://help.github.com/articles/fork-a-repo/), and make your own modifications in source. This way you can keep up to date by merging changes from the `master` branch, and your can also contribute your updates back to `master` by creating a [Pull Request](https://help.github.com/articles/creating-a-pull-request/) if you think they improve Spectacle somehow.
To fork Spectacle go to `https://github.com/sourcey/spectacle`, and press the 'Fork' button. Now you can `git clone git@github.com:<yourname>/spectacle.git` to make your own changes.
Alternatively, you can just copy the contents of `app` from the main repo which contains all the source files such as templates, stylesheets and JavaScripts. Now just pass the path to your custom `app` path to the CLI like so: `spectacle -a /path/to/your/app your_swagger_api.json`
Good luck and enjoy Spectacle!

55
app/helpers/eachSorted.js Normal file
View File

@ -0,0 +1,55 @@
var Handlebars = require("handlebars");
/**
* This block-helper can be used to iterate objects sorted by key. It behaves like the built-in
* `{{#each ...}}`-helper except that it can only be used for objects and the output is in a
* deterministic order (i.e. sorted).
*
* Example template:
*
* ```handlebars
* {{#eachSorted obj}}
* {{@index}} of {{@length}}: {{@key}}={{.}}
* {{/eachSorted}}
* ```
*
* With the data `{ b: 'another one', a: 'first' }`, ignoring newlines and indents, this will output
*
* ```text
* 1 of 2: a=first
* 2 of 2: b=another one
* ```
*
* The helper will set the following @-values according to the Handlebars documentation:
* `@first`, `@index`, `@key`, `@last`, `@length`
* @name eachSorted
* @returns {string}
* @api public
*/
module.exports = function(context, options) {
var ret = "";
var data;
if (typeof context !== "object") {
return ret;
}
var keys = Object.keys(context);
keys.sort(function(a,b) {
// http://stackoverflow.com/questions/8996963/how-to-perform-case-insensitive-sorting-in-javascript
a = String(a).toLowerCase();
b = String(b).toLowerCase();
if( a == b) return 0;
if( a > b) return 1;
return -1;
}).forEach(function(key, index) {
if (options.data) {
data = Handlebars.createFrame(options.data || {});
data.index = index;
data.key = key;
data.length = keys.length;
data.first = index === 0;
data.last = index === keys.length - 1;
}
ret = ret + options.fn(context[key], {data: data})
});
return ret
};

9
app/helpers/equal.js Normal file
View File

@ -0,0 +1,9 @@
/**
* Checks whether two values a equal as in (==)
* @param value1
* @param value2
* @returns {boolean}
*/
module.exports = function(value1, value2) {
return value1 == value2;
};

8
app/helpers/htmlId.js Normal file
View File

@ -0,0 +1,8 @@
/**
* Replace all characters that may not be used in HTML id-attributes by '-'.
* There is still the restriction that IDs may only start with letters, which
* is not addressed by this helper.
*/
module.exports = function(value) {
return value.replace(/[^A-Za-z0-9-_:.]/g, "-");
};

View File

@ -0,0 +1,64 @@
module.exports = function(code) {
// Comments refer to the section number in rfc2616
// If an rfc number is specified, the code is
// documented in the specified rfc.
return {
'100': 'Continue', // 10.1.1
'101': 'Switching Protocols', // 10.1.2
'200': 'OK', // 10.2.1
'201': 'Created', // 10.2.2
'202': 'Accepted', // 10.2.3
'203': 'Non-Authoritative Information', // 10.2.4
'204': 'No Content', // 10.2.5
'205': 'Reset Content', // 10.2.6
'206': 'Partial Content', // 10.2.7
'207': 'Multi-status', // rfc4918, 11.1
'208': 'Already Reported', // rfc5842, 7.1
'226': 'IM Used', // rfc3229, 10.4.1
'300': 'Multiple Choices', // 10.3.1
'301': 'Moved Permanently', // 10.3.2
'302': 'Found', // 10.3.3
'303': 'See Other', // 10.3.4
'304': 'Not Modified', // 10.3.5
'305': 'Use Proxy', // 10.3.6
'306': '(Unused)', // 10.3.7
'307': 'Temporary Redirect', // 10.3.8
'400': 'Bad Request', // 10.4.1
'401': 'Unauthorized', // 10.4.2
'402': 'Payment Required', // 10.4.3
'403': 'Forbidden', // 10.4.4
'404': 'Not Found', // 10.4.5
'405': 'Method Not Allowed', // 10.4.6
'406': 'Not Acceptable', // 10.4.7
'407': 'Proxy Authentication Required', // 10.4.8
'408': 'Request Timeout', // 10.4.9
'409': 'Conflict', // 10.4.10
'410': 'Gone', // 10.4.11
'411': 'Length Required', // 10.4.12
'412': 'Precondition Failed', // 10.4.13
'413': 'Request Entity Too Large', // 10.4.14
'414': 'Request-URI Too Long', // 10.4.15
'415': 'Unsupported Media Type', // 10.4.16
'416': 'Requested Range Not Satisfiable', // 10.4.17
'417': 'Expectation Failed', // 10.4.18
'421': 'Misdirected Request', // rfc7540, 9.1.2
'422': 'Unprocessable Entity', // rfc4918, 11.2
'423': 'Locked', // rfc4918, 11.3
'424': 'Failed Dependency', // rfc4918, 11.4
'426': 'Upgrade Required', // rfc2817, 6
'428': 'Precondition Required', // rfc6585, 3
'429': 'Too Many Requests', // rfc6585, 4
'431': 'Request Header Fields Too Large', // rfc6585, 5
'500': 'Internal Server Error', // 10.5.1
'501': 'Not Implemented', // 10.5.2
'502': 'Bad Gateway', // 10.5.3
'503': 'Service Unavailable', // 10.5.4
'504': 'Gateway Timeout', // 10.5.5
'505': 'HTTP Version Not Supported', // 10.5.6
'506': 'Variant Also Negotiates',
'507': 'Insufficient Storage', // rfc4918, 11.5
'508': 'Loop Detected', // rfc5842, 7.2
'510': 'Not Extended', // rfc2774, 7
'511': 'Network Authentication Required' // rfc6585, 6
}[code]
};

View File

@ -0,0 +1,6 @@
module.exports = function(array, object, options) {
if (array && array.indexOf(object) >= 0) {
return options.fn(this);
}
return options.inverse(this);
};

22
app/helpers/ifeq.js Normal file
View File

@ -0,0 +1,22 @@
/**
* Block helper that compares to values. The body is executed if both value equal.
* Example:
*
* ```hbs
* {{#ifeq value 10}}
* Value is 10
* {{else}}
* Value is not 10
* {{/ifeq}}
* ```
*
* @param {object} `v1` the first value
* @param {object} `v2` the second value
*/
module.exports = function(v1, v2, options) {
// http://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional
if (v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
};

15
app/helpers/md.js Normal file
View File

@ -0,0 +1,15 @@
var Handlebars = require("handlebars");
var common = require("../lib/common");
/**
* Render a markdown formatted text as HTML.
* @param {string} `value` the markdown-formatted text
* @param {boolean} `options.hash.stripParagraph` the marked-md-renderer wraps generated HTML in a <p>-tag by default.
* If this options is set to true, the <p>-tag is stripped.
* @returns {Handlebars.SafeString} a Handlebars-SafeString containing the provieded
* markdown, rendered as HTML.
*/
module.exports = function(value, options) {
var html = common.markdown(value, options.hash ? options.hash.stripParagraph : false);
return new Handlebars.SafeString(html);
};

View File

@ -0,0 +1,8 @@
var Handlebars = require("handlebars");
var common = require("../lib/common");
// var entities = require("entities");
module.exports = function(value, options) {
var html = common.printSchema(value);
return new Handlebars.SafeString(html)
};

View File

@ -0,0 +1,28 @@
var Handlebars = require("handlebars");
var common = require("../lib/common");
// var entities = require("entities");
module.exports = function(reference, options) {
if (!reference) {
console.error("Cannot print null reference.");
return '';
}
var model = common.resolveSchemaReference(reference, options.data.root);
if (typeof model === 'object' && typeof model.properties === 'object')
model = model.properties;
Object.keys(model).forEach(function(propName) {
var prop = model[propName];
if (prop.type) {
model[propName] = prop.type;
if (prop.format) {
model[propName] += ('(' + prop.format + ')');
}
}
})
if (options.hash.type == 'array')
model = [model];
var html = common.printSchema(model);
// html = common.highlight(html, 'json')
// html = entities.decodeHTML(html); html;
return new Handlebars.SafeString(html);
};

View File

@ -0,0 +1,29 @@
/**
* Returns a descriptive string for a datatype
* @param value
* @returns {String} a string like <code>string[]</code> or <code>object[][]</code>
*/
module.exports = function (value) {
return dataType(value);
};
function dataType(value) {
if (!value) return null;
if (value['anyOf'] || value['allOf'] || value['oneOf']) {
return '';
}
if (!value.type) {
return 'object';
}
if (value.type === 'array') {
if (!value.items) {
return 'array';
}
if (value.items.type) {
return dataType(value.items) + '[]';
} else {
return 'object[]';
}
}
return value.type;
}

View File

@ -0,0 +1,45 @@
/**
*
* @param range a json-schema object with minimum, maximum, exclusiveMinimum, exclusiveMaximum
* @param {number} [range.minimum]
* @param {number} [range.maximum]
* @param {boolean} [range.minimumExclusive]
* @param {boolean} [range.maximumExclusive]
* @param {Handlebars} engine the current handlebars engine
*/
module.exports = function(range, options) {
var hasMinimum = range.minimum || range.minimum === 0;
var hasMaximum = range.maximum || range.maximum === 0;
if (!hasMinimum && !hasMaximum) {
// There is no range
return "";
}
var numberSet = "";
if (range.type === "integer") {
numberSet = "\u2208 \u2124" // ELEMENT OF - DOUBLE-STRUCK CAPITAL Z
} else if (range.type === "number") {
numberSet = "\u2208 \u211D" // ELEMENT OF - DOUBLE-STRUCK CAPITAL R
}
if (hasMinimum && !hasMaximum) {
return util.format(", { x %s | x %s %d }",
numberSet,
range.minimumExclusive ? ">" : "\u2265",
range.minimum);
} else if (hasMaximum && !hasMinimum) {
return util.format(", { x %s | x %s %d }",
numberSet,
range.maximumExclusive ? "<" : "\u2264",
range.maximum);
} else {
// if (hasMaxmium && hasMinimum)
return util.format(", { x %s | %d %s x %s %d }",
numberSet,
range.minimum,
range.minimumExclusive ? "<" : "\u2264",
range.maximumExclusive ? "<" : "\u2264",
range.maximum);
}
};

View File

@ -0,0 +1,12 @@
var common = require("../lib/common");
/**
* Resolve a (local) json schema $ref and return the referenced object.
* @param reference
*/
module.exports = function(reference, options) {
var model = common.resolveSchemaReference(reference, options.data.root);
if (typeof model === 'object' && typeof model.properties === 'object')
model = model.properties;
return model;
};

View File

@ -0,0 +1,8 @@
/**
* Extract then name of a subschema from a $ref property
* @param url
* @returns {*}
*/
module.exports = function(url) {
return url.replace('#/definitions/', '')
};

View File

@ -0,0 +1,9 @@
module.exports = function(value, paramName) {
return {
'csv': 'comma separated (`' + paramName + '=aaa,bbb`)',
'ssv': 'space separated (`' + paramName + '=aaa bbb`)',
'tsv': 'tab separated (`' + paramName + '=aaa\\tbbb`)',
'pipes': 'pipe separated (`' + paramName + '=aaa|bbb`)',
'multi': 'multiple parameters (`' + paramName + '=aaa&' + paramName + '=bbb`)'
}[value]
};

View File

@ -0,0 +1,19 @@
module.exports = function(options) {
var data = options.data.root;
var endpoint = 'http';
data.schemes.every(function (scheme) {
if (scheme == 'https' || scheme == 'HTTPS') {
endpoint = 'https';
return false;
}
endpoint = scheme;
return true;
})
endpoint += '://';
endpoint += data.host;
if (data.basePath) {
// endpoint += '/'
endpoint += data.basePath;
}
return endpoint;
};

View File

@ -0,0 +1,10 @@
/**
* Converts a string to uppercase
* @name toUpperCase
* @param {string} value the input string
* @returns {string} the uppercase string
* @api public
*/
module.exports = function(value, options) {
return value ? value.toUpperCase() : '';
};

4
app/javascripts/jquery-2.1.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

16
app/javascripts/main.js Normal file
View File

@ -0,0 +1,16 @@
$(function() {
var $sidebar = $('#sidebar');
var $nav = $sidebar.find('nav');
var traverse = new Traverse($nav, {
threshold: 10,
barOffset: $sidebar.position().top
});
$nav.on('update.traverse', function(event, element) {
$nav.find('section').removeClass('expand');
var $section = element.parents('section:first');
if ($section.length) {
$section.addClass('expand');
}
});
});

189
app/javascripts/traverse.js Normal file
View File

@ -0,0 +1,189 @@
/**
* Creates a new instance of Traverse.
* @class
* @fires Traverse#init
* @param {Object} element - jQuery object to add the trigger to.
* @param {Object} options - Overrides to the default plugin settings.
*/
function Traverse(element, options) {
this.$element = element;
this.options = $.extend({}, Traverse.defaults, this.$element.data(), options);
this._init();
}
/**
* Default settings for plugin
*/
Traverse.defaults = {
/**
* Amount of time, in ms, the animated scrolling should take between locations.
* @option
* @example 500
*/
animationDuration: 500,
/**
* Animation style to use when scrolling between locations.
* @option
* @example 'ease-in-out'
*/
animationEasing: 'linear',
/**
* Number of pixels to use as a marker for location changes.
* @option
* @example 50
*/
threshold: 50,
/**
* Class applied to the active locations link on the traverse container.
* @option
* @example 'active'
*/
activeClass: 'active',
/**
* Allows the script to manipulate the url of the current page, and if supported, alter the history.
* @option
* @example true
*/
deepLinking: false,
/**
* Number of pixels to offset the scroll of the page on item click if using a sticky nav bar.
* @option
* @example 25
*/
barOffset: 0
};
/**
* Initializes the Traverse plugin and calls functions to get equalizer functioning on load.
* @private
*/
Traverse.prototype._init = function() {
var id = this.$element[0].id, // || Foundation.GetYoDigits(6, 'traverse'),
_this = this;
this.$targets = $('[data-traverse-target]');
this.$links = this.$element.find('a');
this.$element.attr({
'data-resize': id,
'data-scroll': id,
'id': id
});
this.$active = $();
this.scrollPos = parseInt(window.pageYOffset, 10);
this._events();
};
/**
* Calculates an array of pixel values that are the demarcation lines between locations on the page.
* Can be invoked if new elements are added or the size of a location changes.
* @function
*/
Traverse.prototype.calcPoints = function(){
var _this = this,
body = document.body,
html = document.documentElement;
this.points = [];
this.winHeight = Math.round(Math.max(window.innerHeight, html.clientHeight));
this.docHeight = Math.round(Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight));
this.$targets.each(function(){
var $tar = $(this),
pt = $tar.offset().top; // Math.round($tar.offset().top - _this.options.threshold);
$tar.targetPoint = pt;
_this.points.push(pt);
});
};
/**
* Initializes events for Traverse.
* @private
*/
Traverse.prototype._events = function() {
var _this = this,
$body = $('html, body'),
opts = {
duration: _this.options.animationDuration,
easing: _this.options.animationEasing
};
$(window).one('load', function(){
_this.calcPoints();
_this._updateActive();
$(this).resize(function(e) {
_this.reflow();
}).scroll(function(e) {
_this._updateActive();
});
})
this.$element.on('click', 'a[href^="#"]', function(e) { //'click.zf.traverse'
e.preventDefault();
var arrival = this.getAttribute('href'),
scrollPos = $(arrival).offset().top - _this.options.barOffset; // - _this.options.threshold / 2 - _this.options.barOffset;
$body.stop(true).animate({
scrollTop: scrollPos
}, opts);
});
};
/**
* Calls necessary functions to update Traverse upon DOM change
* @function
*/
Traverse.prototype.reflow = function(){
this.calcPoints();
this._updateActive();
};
/**
* Updates the visibility of an active location link,
* and updates the url hash for the page, if deepLinking enabled.
* @private
* @function
* @fires Traverse#update
*/
Traverse.prototype._updateActive = function(){
var winPos = parseInt(window.pageYOffset, 10),
curIdx;
if(winPos + this.winHeight === this.docHeight){ curIdx = this.points.length - 1; }
else if(winPos < this.points[0]){ curIdx = 0; }
else{
var isDown = this.scrollPos < winPos,
_this = this,
curVisible = this.points.filter(function(p, i){
return isDown ?
p <= (winPos + _this.options.barOffset + _this.options.threshold) :
(p - (_this.options.barOffset + _this.options.threshold)) <= winPos;
// p <= (winPos - (offset - _this.options.threshold)) :
// (p - (-offset + _this.options.threshold)) <= winPos;
});
curIdx = curVisible.length ? curVisible.length - 1 : 0;
}
var $prev = this.$active;
var $next = this.$links.eq(curIdx);
this.$active.removeClass(this.options.activeClass);
this.$active = $next.addClass(this.options.activeClass);
if(this.options.deepLinking){
var hash = this.$active[0].getAttribute('href');
if(window.history.pushState){
window.history.pushState(null, null, hash);
}else{
window.location.hash = hash;
}
}
this.scrollPos = winPos;
// Fire event if the active element was changed
var changed = $prev[0] !== $next[0];
if (changed) {
this.$element.trigger('update.traverse', [this.$active]);
}
};

98
app/lib/common.js Normal file
View File

@ -0,0 +1,98 @@
var cheerio = require("cheerio");
var entities = require("entities");
var marked = require("marked");
var highlight = require('highlight.js');
var common = {
highlight: function(code, name) {
var highlighted;
if (name) {
highlighted = highlight.highlight(name, code).value;
} else {
highlighted = highlight.highlightAuto(code).value;
}
return highlight.fixMarkup(highlighted); //highlighted; //
},
/**
* Render a markdown formatted text as HTML.
* @param {string} `value` the markdown-formatted text
* @param {boolean} `stripParagraph` the marked-md-renderer wraps generated HTML in a <p>-tag by default.
* If this options is set to true, the <p>-tag is stripped.
* @returns {string} the markdown rendered as HTML.
*/
markdown: function(value, stripParagraph) {
if (!value) {
return value;
}
var html = marked(value);
// We strip the surrounding <p>-tag, if
if (stripParagraph) {
var $ = cheerio("<root>" + html + "</root>");
// Only strip <p>-tags and only if there is just one of them.
if ($.children().length === 1 && $.children('p').length === 1) {
html = $.children('p').html();
}
}
return html;
},
printSchema: function(value) {
if (!value) {
return '';
}
var schemaString = require("json-stable-stringify")(value, { space: 2 });
// Add an extra CRLR before the code so the postprocessor can determine
// the correct line indent for the <pre> tag.
var $ = cheerio.load(marked("```json\r\n\r\n" + schemaString + "\n```"));
var definitions = $('span:not(:has(span)):contains("#/definitions/")');
definitions.each(function(index, item) {
var ref = $(item).html();
var refLink = ref.replace(/&quot;/g, "").replace('#/definitions/', '#definition-')
// TODO: This should be done in a template
$(item).html("<a href=" + refLink + ">" + ref + "</a>");
});
// return '<pre><code class="hljs lang-json">' +
// this.highlight(schemaString, 'json') +
// '</code></pre>';
return $.html();
},
resolveSchemaReference: function(reference, json) {
reference = reference.trim();
if (reference.lastIndexOf('#', 0) < 0) {
console.warn('Remote references not supported yet. Reference must start with "#" (but was ' + reference + ')')
return {};
}
var components = reference.split('#');
var url = components[0];
var hash = components[1];
var hashParts = hash.split('/');
// TODO : Download remote json from url if url not empty
var current = json; //options.data.root
hashParts.forEach(function(hashPart) {
// Traverse schema from root along the path
if (hashPart.trim().length > 0) {
if (typeof current === 'undefined') {
console.warn("Reference '"+reference+"' cannot be resolved. '"+hashPart+"' is undefined.");
return {};
}
current = current[hashPart];
}
})
return current;
}
}
highlight.configure({
// "useBR": true
});
marked.setOptions({
highlight: common.highlight
});
module.exports = common;

64
app/lib/preprocessor.js Normal file
View File

@ -0,0 +1,64 @@
var _ = require('lodash')
var httpMethods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch']
// Preprocessor for the swagger-json, so that some of the logic can be taken out of the
// template
module.exports = function(swaggerJson) {
var copy = _.cloneDeep(swaggerJson)
var tagsByName = _.indexBy(copy.tags, 'name')
copy.tags = copy.tags || []
// The "body"-parameter in each operation is stored in a
// separate field "_request_body".
if (copy.paths) {
Object.keys(copy.paths).forEach(function(pathName) {
var path = copy.paths[pathName]
var pathParameters = path.parameters || []
Object.keys(path).forEach(function(method) {
if (httpMethods.indexOf(method) < 0) {
delete path[method]
return
}
var operation = path[method]
operation.path = pathName
operation.method = method
// Draw links from tags to operations referencing them
var operationTags = operation.tags || ['default']
operationTags.forEach(function(tag) {
if (!tagsByName[tag]) {
// New implicit declaration of tag not defined in global "tags"-object
// https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#user-content-swaggerTags
var tagDefinition = {
name: tag,
operations: []
}
tagsByName[tag] = tagDefinition
copy.tags.push(tagDefinition)
}
if (tagsByName[tag]) {
tagsByName[tag].operations = tagsByName[tag].operations || []
tagsByName[tag].operations.push(operation)
}
})
// Join parameters with path-parameters
operation.parameters = (operation.parameters || [])
.concat(pathParameters)
.filter(function(param) {
if (param.in === 'body') {
operation._request_body = param
return false
}
return true
})
// Show body section, if either a body-parameter or a consumes-property is present.
operation._show_requst_body_section = operation._request_body || operation.consumes
})
})
// If there are multiple tags, we show the tag-based summary
copy.showTagSummary = copy.tags.length > 1
}
return copy
}

View File

@ -0,0 +1,41 @@
//
// Include Foundation components
@import 'foundation'; //foundation-sites/scss/
@include foundation-global-styles;
@include foundation-grid;
//@include foundation-flex-grid;
@include foundation-typography;
@include foundation-button;
@include foundation-forms;
@include foundation-visibility-classes;
@include foundation-float-classes;
// @include foundation-accordion;
// @include foundation-accordion-menu;
// @include foundation-badge;
// @include foundation-breadcrumbs;
// @include foundation-button-group;
// @include foundation-callout;
// @include foundation-close-button;
// @include foundation-drilldown-menu;
// @include foundation-dropdown;
// @include foundation-dropdown-menu;
// @include foundation-flex-video;
@include foundation-label;
// @include foundation-media-object;
@include foundation-menu;
// @include foundation-off-canvas;
// @include foundation-orbit;
// @include foundation-pagination;
// @include foundation-progress-bar;
// @include foundation-slider;
// @include foundation-sticky;
// @include foundation-reveal;
// @include foundation-switch;
@include foundation-table;
// @include foundation-tabs;
// @include foundation-thumbnail;
// @include foundation-title-bar;
// @include foundation-tooltip;
@include foundation-top-bar;

View File

@ -0,0 +1,548 @@
// Foundation for Sites Settings
// -----------------------------
//
// Table of Contents:
//
// 1. Global
// 2. Breakpoints
// 3. The Grid
// 4. Base Typography
// 5. Typography Helpers
// 6. Abide
// 7. Accordion
// 8. Accordion Menu
// 9. Badge
// 10. Breadcrumbs
// 11. Button
// 12. Button Group
// 13. Callout
// 14. Close Button
// 15. Drilldown
// 16. Dropdown
// 17. Dropdown Menu
// 18. Flex Video
// 19. Forms
// 20. Label
// 21. Media Object
// 22. Menu
// 23. Off-canvas
// 24. Orbit
// 25. Pagination
// 26. Progress Bar
// 27. Reveal
// 28. Slider
// 29. Switch
// 30. Table
// 31. Tabs
// 32. Thumbnail
// 33. Title Bar
// 34. Tooltip
// 35. Top Bar
@import 'util/util';
// 1. Global
// ---------
$global-font-size: 95%;
$global-width: rem-calc(1200);
$global-lineheight: 1.5;
$primary-color: #2199e8;
$secondary-color: #777;
$success-color: #3adb76;
$warning-color: #ffae00;
$body-color: #3c3c3c;
$alert-color: #ec5840;
$light-gray: #e6e6e6;
$medium-gray: #cacaca;
$dark-gray: #7a7a7a;
$black: #23241f; //#0a0a0a;
$white: #fefefe;
$body-background: $white;
$body-font-color: scale-color($black, $lightness: 10%);
// $body-font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
$body-font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
$body-antialiased: true;
$global-margin: 1rem;
$global-padding: 1rem;
$global-weight-normal: 400;
$global-weight-bold: bold;
$global-radius: 0;
$global-text-direction: ltr;
// 2. Breakpoints
// --------------
$breakpoints: (
small: 0,
medium: 640px,
large: 1024px,
xlarge: 1200px,
xxlarge: 1440px,
);
$breakpoint-classes: (small medium large);
// 3. The Grid
// -----------
$grid-row-width: $global-width;
$grid-column-count: 12;
$grid-column-responsive-gutter: (
small: 20px,
medium: 30px,
);
$grid-column-align-edge: true;
$block-grid-max: 8;
// 4. Base Typography
// ------------------
$header-font-family: $body-font-family;
$header-font-weight: $global-weight-normal;
$header-font-style: normal;
$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace;
$header-sizes: (
small: (
'h1': 24,
'h2': 20,
'h3': 19,
'h4': 18,
'h5': 17,
'h6': 16,
),
medium: (
'h1': 26,
'h2': 24,
'h3': 20,
'h4': 18,
'h5': 17,
'h6': 16,
),
);
$header-color: $black;
$header-lineheight: 1.4;
$header-margin-bottom: 1rem;
$header-text-rendering: optimizeLegibility;
$small-font-size: 90%;
$header-small-font-color: $medium-gray;
$paragraph-lineheight: 1.5;
$paragraph-margin-bottom: 1rem;
$paragraph-text-rendering: optimizeLegibility;
$code-color: $black;
$code-font-family: $font-family-monospace;
$code-font-weight: $global-weight-normal;
$code-background: $light-gray;
$code-border: 1px solid $medium-gray;
$code-padding: rem-calc(2 5 1);
$anchor-color: $primary-color;
$anchor-color-hover: scale-color($anchor-color, $lightness: -14%);
$anchor-text-decoration: none;
$anchor-text-decoration-hover: none;
$hr-width: $global-width;
$hr-border: 1px solid $medium-gray;
$hr-margin: rem-calc(20) auto;
$list-lineheight: $paragraph-lineheight;
$list-margin-bottom: $paragraph-margin-bottom;
$list-style-type: disc;
$list-style-position: outside;
$list-side-margin: 1.25rem;
$list-nested-side-margin: 1.25rem;
$defnlist-margin-bottom: 1rem;
$defnlist-term-weight: $global-weight-bold;
$defnlist-term-margin-bottom: 0.3rem;
$blockquote-color: $dark-gray;
$blockquote-padding: rem-calc(9 20 0 19);
$blockquote-border: 1px solid $medium-gray;
$cite-font-size: rem-calc(13);
$cite-color: $dark-gray;
$keystroke-font: $font-family-monospace;
$keystroke-color: $black;
$keystroke-background: $light-gray;
$keystroke-padding: rem-calc(2 4 0);
$keystroke-radius: $global-radius;
$abbr-underline: 1px dotted $black;
// 5. Typography Helpers
// ---------------------
$lead-font-size: $global-font-size * 1.25;
$lead-lineheight: 1.6;
$subheader-lineheight: 1.4;
$subheader-color: $dark-gray;
$subheader-font-weight: $global-weight-normal;
$subheader-margin-top: 0.2rem;
$subheader-margin-bottom: 0.5rem;
$stat-font-size: 2.5rem;
// 6. Abide
// --------
$abide-inputs: true;
$abide-labels: true;
$input-background-invalid: $alert-color;
$form-label-color-invalid: $alert-color;
$input-error-color: $alert-color;
$input-error-font-size: rem-calc(12);
$input-error-font-weight: $global-weight-bold;
// 7. Accordion
// ------------
$accordion-background: $white;
$accordion-plusminus: true;
$accordion-item-color: foreground($accordion-background, $primary-color);
$accordion-item-background-hover: $light-gray;
$accordion-item-padding: 1.25rem 1rem;
$accordion-content-background: $white;
$accordion-content-border: 1px solid $light-gray;
$accordion-content-color: foreground($accordion-background, $primary-color);
$accordion-content-padding: 1rem;
// 8. Accordion Menu
// -----------------
$accordionmenu-arrows: true;
$accordionmenu-arrow-color: $primary-color;
// 9. Badge
// --------
$badge-background: $primary-color;
$badge-color: foreground($badge-background);
$badge-padding: 0.3em;
$badge-minwidth: 2.1em;
$badge-font-size: 0.6rem;
// 10. Breadcrumbs
// ---------------
$breadcrumbs-margin: 0 0 $global-margin 0;
$breadcrumbs-item-font-size: rem-calc(11);
$breadcrumbs-item-color: $primary-color;
$breadcrumbs-item-color-current: $black;
$breadcrumbs-item-color-disabled: $medium-gray;
$breadcrumbs-item-margin: 0.75rem;
$breadcrumbs-item-uppercase: true;
$breadcrumbs-item-slash: true;
// 11. Button
// ----------
$button-padding: 0.85em 1em;
$button-margin: 0 0 $global-margin 0;
$button-fill: solid;
$button-background: $primary-color;
$button-background-hover: scale-color($button-background, $lightness: -15%);
$button-color: #fff;
$button-color-alt: #000;
$button-radius: $global-radius;
$button-sizes: (
tiny: 0.6rem,
small: 0.75rem,
default: 0.9rem,
large: 1.25rem,
);
$button-opacity-disabled: 0.25;
// 12. Button Group
// ----------------
$buttongroup-margin: 1rem;
$buttongroup-spacing: 1px;
$buttongroup-child-selector: '.button';
$buttongroup-expand-max: 6;
// 13. Callout
// -----------
$callout-background: $white;
$callout-background-fade: 85%;
$callout-border: 1px solid rgba($black, 0.25);
$callout-margin: 0 0 1rem 0;
$callout-padding: 1rem;
$callout-font-color: $body-font-color;
$callout-font-color-alt: $body-background;
$callout-radius: $global-radius;
$callout-link-tint: 30%;
// 14. Close Button
// ----------------
$closebutton-position: right top;
$closebutton-offset-horizontal: 1rem;
$closebutton-offset-vertical: 0.5rem;
$closebutton-size: 2em;
$closebutton-lineheight: 1;
$closebutton-color: $dark-gray;
$closebutton-color-hover: $black;
// 15. Drilldown
// -------------
$drilldown-transition: transform 0.15s linear;
$drilldown-arrows: true;
$drilldown-arrow-color: $primary-color;
$drilldown-background: $white;
// 16. Dropdown
// ------------
$dropdown-padding: 1rem;
$dropdown-border: 1px solid $medium-gray;
$dropdown-font-size: 16rem;
$dropdown-width: 300px;
$dropdown-radius: $global-radius;
$dropdown-sizes: (
tiny: 100px,
small: 200px,
large: 400px,
);
// 17. Dropdown Menu
// -----------------
$dropdownmenu-arrows: true;
$dropdownmenu-arrow-color: $anchor-color;
$dropdownmenu-min-width: 200px;
$dropdownmenu-background: $white;
$dropdownmenu-border: 1px solid $medium-gray;
// 18. Flex Video
// --------------
$flexvideo-margin-bottom: rem-calc(16);
$flexvideo-ratio: 4 by 3;
$flexvideo-ratio-widescreen: 16 by 9;
// 19. Forms
// ---------
$fieldset-border: 1px solid $medium-gray;
$fieldset-padding: rem-calc(20);
$fieldset-margin: rem-calc(18 0);
$legend-padding: rem-calc(0 3);
$form-spacing: rem-calc(16);
$helptext-color: #333;
$helptext-font-size: rem-calc(13);
$helptext-font-style: italic;
$input-prefix-color: $black;
$input-prefix-background: $light-gray;
$input-prefix-border: 1px solid $medium-gray;
$input-prefix-padding: 1rem;
$form-label-color: $black;
$form-label-font-size: rem-calc(14);
$form-label-font-weight: $global-weight-normal;
$form-label-line-height: 1.8;
$select-background: $white;
$select-triangle-color: #333;
$select-radius: $global-radius;
$input-color: $black;
$input-font-family: inherit;
$input-font-size: rem-calc(16);
$input-background: $white;
$input-background-focus: $white;
$input-background-disabled: $light-gray;
$input-border: 1px solid $medium-gray;
$input-border-focus: 1px solid $dark-gray;
$input-shadow: inset 0 1px 2px rgba($black, 0.1);
$input-shadow-focus: 0 0 5px $medium-gray;
$input-cursor-disabled: default;
$input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out;
$input-number-spinners: true;
$input-radius: $global-radius;
// 20. Label
// ---------
$label-background: $primary-color;
$label-color: foreground($label-background);
$label-font-size: 0.8rem;
$label-padding: 0.33333rem 0.5rem;
$label-radius: $global-radius;
// 21. Media Object
// ----------------
$mediaobject-margin-bottom: $global-margin;
$mediaobject-section-padding: $global-padding;
$mediaobject-image-width-stacked: 100%;
// 22. Menu
// --------
$menu-margin: 0;
$menu-margin-nested: 1rem;
$menu-item-padding: 0.7rem 1rem;
$menu-icon-spacing: 0.25rem;
$menu-expand-max: 6;
// 23. Off-canvas
// --------------
$offcanvas-size: 250px;
$offcanvas-background: $light-gray;
$offcanvas-zindex: -1;
$offcanvas-transition-length: 0.5s;
$offcanvas-transition-timing: ease;
$offcanvas-fixed-reveal: true;
$offcanvas-exit-background: rgba($white, 0.25);
$maincontent-class: 'off-canvas-content';
$maincontent-shadow: 0 0 10px rgba($black, 0.5);
// 24. Orbit
// ---------
$orbit-bullet-background: $medium-gray;
$orbit-bullet-background-active: $dark-gray;
$orbit-bullet-diameter: 1.2rem;
$orbit-bullet-margin: 0.1rem;
$orbit-bullet-margin-top: 0.8rem;
$orbit-bullet-margin-bottom: 0.8rem;
$orbit-caption-background: rgba($black, 0.5);
$orbit-caption-padding: 1rem;
$orbit-control-background-hover: rgba($black, 0.5);
$orbit-control-padding: 1rem;
$orbit-control-zindex: 10;
// 25. Pagination
// --------------
$pagination-font-size: rem-calc(14);
$pagination-margin-bottom: $global-margin;
$pagination-item-color: $black;
$pagination-item-padding: rem-calc(3 10);
$pagination-item-spacing: rem-calc(1);
$pagination-radius: $global-radius;
$pagination-item-background-hover: $light-gray;
$pagination-item-background-current: $primary-color;
$pagination-item-color-current: foreground($pagination-item-background-current);
$pagination-item-color-disabled: $medium-gray;
$pagination-ellipsis-color: $black;
$pagination-mobile-items: false;
$pagination-arrows: true;
// 26. Progress Bar
// ----------------
$progress-height: 1rem;
$progress-background: $medium-gray;
$progress-margin-bottom: $global-margin;
$progress-meter-background: $primary-color;
$progress-radius: $global-radius;
// 27. Reveal
// ----------
$reveal-background: $white;
$reveal-width: 600px;
$reveal-max-width: $global-width;
$reveal-offset: rem-calc(100);
$reveal-padding: $global-padding;
$reveal-border: 1px solid $medium-gray;
$reveal-radius: $global-radius;
$reveal-zindex: 1005;
$reveal-overlay-background: rgba($black, 0.45);
// 28. Slider
// ----------
$slider-height: 0.5rem;
$slider-width-vertical: $slider-height;
$slider-background: $light-gray;
$slider-fill-background: $medium-gray;
$slider-handle-height: 1.4rem;
$slider-handle-width: 1.4rem;
$slider-handle-background: $primary-color;
$slider-opacity-disabled: 0.25;
$slider-radius: $global-radius;
$slider-transition: all 0.2s ease-in-out;
// 29. Switch
// ----------
$switch-background: $medium-gray;
$switch-background-active: $primary-color;
$switch-height: 2rem;
$switch-height-tiny: 1.5rem;
$switch-height-small: 1.75rem;
$switch-height-large: 2.5rem;
$switch-radius: $global-radius;
$switch-margin: $global-margin;
$switch-paddle-background: $white;
$switch-paddle-offset: 0.25rem;
$switch-paddle-radius: $global-radius;
$switch-paddle-transition: all 0.25s ease-out;
// 30. Table
// ---------
$table-background: $white;
$table-color-scale: 5%;
$table-border: 1px solid smart-scale($table-background, $table-color-scale);
$table-padding: rem-calc(8 10 10);
$table-hover-scale: 2%;
$table-row-hover: darken($table-background, $table-hover-scale);
$table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale);
$table-striped-background: smart-scale($table-background, $table-color-scale);
$table-stripe: even;
$table-head-background: smart-scale($table-background, $table-color-scale / 2);
$table-foot-background: smart-scale($table-background, $table-color-scale);
$table-head-font-color: $body-font-color;
$show-header-for-stacked: false;
// 31. Tabs
// --------
$tab-margin: 0;
$tab-background: $white;
$tab-background-active: $light-gray;
$tab-border: $light-gray;
$tab-item-color: foreground($tab-background, $primary-color);
$tab-item-background-hover: $white;
$tab-item-padding: 1.25rem 1.5rem;
$tab-expand-max: 6;
$tab-content-background: $white;
$tab-content-border: $light-gray;
$tab-content-color: foreground($tab-background, $primary-color);
$tab-content-padding: 1rem;
// 32. Thumbnail
// -------------
$thumbnail-border: solid 4px $white;
$thumbnail-margin-bottom: $global-margin;
$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2);
$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
$thumbnail-transition: box-shadow 200ms ease-out;
$thumbnail-radius: $global-radius;
// 33. Title Bar
// -------------
$titlebar-background: $black;
$titlebar-color: $white;
$titlebar-padding: 0.5rem;
$titlebar-text-font-weight: bold;
$titlebar-icon-color: $white;
$titlebar-icon-color-hover: $medium-gray;
$titlebar-icon-spacing: 0.25rem;
// 34. Tooltip
// -----------
$tooltip-background-color: $black;
$tooltip-color: $white;
$tooltip-padding: 0.75rem;
$tooltip-font-size: $small-font-size;
$tooltip-pip-width: 0.75rem;
$tooltip-pip-height: $tooltip-pip-width * 0.866;
$tooltip-pip-offset: 1.25rem;
$tooltip-radius: $global-radius;
// 35. Top Bar
// -----------
$topbar-padding: 0.5rem;
$topbar-background: $primary-color;
$topbar-link-color: $white;
$topbar-input-width: 200px;

View File

@ -0,0 +1,119 @@
.json-schema-description {
@include named-section($msg-json-section-description);
}
.json-schema-properties {
@include named-section($msg-json-section-properties);
dd {
color: $dark-gray;
}
dd:not(:last-child) {
padding-bottom: 0.75rem;
}
dl {
margin: 0;
}
}
.json-schema-ref-array {
&:before {
color: $dark-gray;
content: "Array<";
}
&:after {
color: $dark-gray;
content: ">";
}
}
.json-schema-example {
@include named-section($msg-json-section-example);
}
.json-schema-array-items {
@include named-section($msg-json-section-items);
}
.json-schema-allOf-inherited {
@include named-section($msg-json-section-inherited);
}
.json-schema-anyOf {
> dl {
padding-left: 1em;
dt:not(:first-child):before {
content: "or ";
}
dt:first-child:before {
content: "either ";
}
}
}
.json-schema-additionalProperties {
@include named-section($msg-json-section-additionalProperties);
}
.json-inner-schema {
.json-schema-properties,
.json-schema-array-items,
.json-schema-description,
.json-schema-example {
padding-left: 1em;
margin-top: 0.5em;
padding-bottom: 0.5em;
}
}
.json-property-discriminator {
@include named-label($msg-json-discriminator)
}
.json-property-required {
@include named-label($msg-json-required)
}
.json-property-read-only {
@include named-label($msg-json-read-only)
}
.json-property-type {
font-style: italic;
font-weight: 100;
}
.json-property-format {
font-size: smaller;
}
.json-property-enum {
font-weight: lighter;
font-size: small;
}
.json-property-default-value {
font-weight: lighter;
font-size: small;
&:before {
content: '(default: "';
}
&:after {
content: '")';
}
}
.json-property-enum-item {
&:before, &:after {
content: "\""
}
font-weight: lighter;
font-size: small;
}
.json-schema-reference {
font-size: 90%;
}

View File

@ -0,0 +1,83 @@
/*
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #23241f;
}
.hljs,
.hljs-tag,
.hljs-subst {
color: #f8f8f2;
}
.hljs-strong,
.hljs-emphasis {
color: #a8a8a2;
}
.hljs-bullet,
.hljs-quote,
.hljs-number,
.hljs-regexp,
.hljs-literal,
.hljs-link {
color: #ae81ff;
}
.hljs-code,
.hljs-title,
.hljs-section,
.hljs-selector-class {
color: #a6e22e;
}
.hljs-strong {
font-weight: bold;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-name,
.hljs-attr {
color: #f92672;
}
.hljs-symbol,
.hljs-attribute {
color: #66d9ef;
}
.hljs-params,
.hljs-class .hljs-title {
color: #f8f8f2;
}
.hljs-string,
.hljs-type,
.hljs-built_in,
.hljs-builtin-name,
.hljs-selector-id,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-addition,
.hljs-variable,
.hljs-template-variable {
color: #e6db74;
}
.hljs-comment,
.hljs-deletion,
.hljs-meta {
color: #75715e;
}

View File

@ -0,0 +1,141 @@
// Panels
.swagger-panel-operation {
&-get {
//@include swagger-panel-operation(#e7f6ec);
}
&-get {
//@include swagger-panel-operation(#e7f0f7);
}
&-put {
//@include swagger-panel-operation(#f9f2e9);
}
&-patch {
//@include swagger-panel-operation(#FCE9E3);
}
&-options {
//@include swagger-panel-operation(#e7f0f7);
}
&-delete {
//@include swagger-panel-operation(#f5e8e8);
}
&-head {
//@include swagger-panel-operation(#fcffcd);
}
}
.swagger-operation-path {
@include named-section($msg-swagger-section-operation-path);
}
.swagger-operation-description {
@include named-section($msg-swagger-section-operation-description);
}
.swagger-request-params {
@include named-section($msg-swagger-section-request-params);
}
.swagger-request-body {
@include named-section($msg-swagger-section-request-body);
}
.swagger-responses {
@include named-section($msg-swagger-section-responses);
}
.swagger-global {
@include named-label($msg-swagger-badge-global);
}
// Tables
table.table {
th.swagger-param-key {
@include table-header($msg-swagger-param-key, 10%);
}
th.swagger-param-name {
@include table-header($msg-swagger-param-name, 10%);
}
th.swagger-param-description {
@include table-header($msg-swagger-param-description);
}
th.swagger-param-data-type {
@include table-header($msg-swagger-param-data-type, 30%);
}
th.swagger-param-type {
@include table-header($msg-swagger-param-type, 10%);
}
th.swagger-request-security-schema {
@include table-header($msg-swagger-request-security-schema, 50%);
}
th.swagger-request-security-scopes {
@include table-header($msg-swagger-request-security-scopes, 50%);
}
// Response headers
th.swagger-response-header-name {
@include table-header($msg-swagger-response-header-name)
}
th.swagger-response-header-description {
@include table-header($msg-swagger-response-header-description)
}
th.swagger-response-header-data-type {
@include table-header($msg-swagger-response-header-data-type)
}
// Response table
th.swagger-response-code {
@include table-header($msg-swagger-response-code)
}
th.swagger-response-description {
@include table-header($msg-swagger-response-description)
}
th.swagger-response-schema {
@include table-header($msg-swagger-response-schema)
}
}
.swagger-response-name-value {
font-weight: bold;
}
.swagger-param-collection-format {
}
.swagger-response-description-text {
padding-bottom: 0.5em;
}
.swagger-request-security {
@include named-section($msg-swagger-section-request-security);
}
.swagger-security-definition-properties {
// @include named-section($msg-swagger-security-definition-properties);
}
.swagger-security-definition {
&-basic {
&:before {
color: $medium-gray;
content: $msg-swagger-security-definition-type-basic;
}
}
&-oauth2 {
&:before {
color: $medium-gray;
content: $msg-swagger-security-definition-type-oauth2;
}
}
&-apiKey {
&:before {
color: $medium-gray;
content: $msg-swagger-security-definition-type-apikey;
}
}
}

View File

@ -0,0 +1,43 @@
//
//= Mixins and helpers
//
// Named sections
@mixin named-section($title) {
&:before {
content: $title;
display: block;
@extend h6;
margin-bottom: 0.5em;
color: $accent-color;
text-transform: uppercase;
font-size: 0.9rem;
}
}
@mixin small-label($style:'alert') {
@include label();
@extend .label.#{$style};
font-size: 0.75rem;
border-radius: 4px;
padding: 3px 6px;
}
@mixin named-label($name, $style:'alert') {
&:before {
@include small-label($style);
content: $name;
}
}
// Draw a panel in a given color
@mixin swagger-panel-operation($color) {
background-color: $color;
}
@mixin table-header($label,$width: auto) {
width: auto;
&:before {
content: $label;
}
}

View File

@ -0,0 +1,71 @@
//
//= Layout variables
//
// The height of the top bar
$topbar-height: 50px;
// The width of the column padding
$column-padding: 2.25rem;
// The accent color to use for nav highlights and section headings
$accent-color: #f68b1f;
// The dark backgorund color behind code examples
$dark-background-color: $black;
// Medium device width (from Foundation)
$medium-width: 40em;
// Large device width (from Foundation)
$large-width: 64em;
//
// JSON schema labels
$msg-json-section-description: "Description";
$msg-json-section-properties: "Properties";
$msg-json-section-items: "Items";
$msg-json-section-inherited: "Inherited";
$msg-json-section-anyOf: "Any of";
$msg-json-discriminator: "discriminator";
$msg-json-read-only: "read only";
$msg-json-required: "required";
$msg-json-ref-prefix: "Details: ";
$msg-json-section-additionalProperties: "Additional properties";
$msg-json-section-example: "Example";
//
// Swagger labels
$msg-swagger-data-type: 'Data type';
$msg-swagger-description: 'Description';
$msg-swagger-section-operation-path: 'Path';
$msg-swagger-section-operation-description: $msg-swagger-description;
$msg-swagger-section-request-params: 'Request parameters';
$msg-swagger-section-request-body: 'Request body';
$msg-swagger-section-responses: 'Responses';
$msg-swagger-param-key: 'Key';
$msg-swagger-param-name: 'Name';
$msg-swagger-param-description: $msg-swagger-description;
$msg-swagger-param-data-type: 'Data type';
$msg-swagger-param-type: 'Type';
$msg-swagger-response-header-name: 'Header';
$msg-swagger-response-header-description: $msg-swagger-description;
$msg-swagger-response-header-data-type: 'Data type';
$msg-swagger-section-request-security: 'Security';
$msg-swagger-request-security-scopes: 'Scopes';
$msg-swagger-request-security-schema: 'Schema';
$msg-swagger-security-definition-properties: 'Properties';
$msg-swagger-security-definition-type-basic: '(HTTP Basic Authentication)';
$msg-swagger-security-definition-type-oauth2: '(OAuth2 Authentication)';
$msg-swagger-security-definition-type-apikey: '(API Key Authentication)';
$msg-swagger-badge-global: 'global';
$msg-swagger-response-code: 'Code';
$msg-swagger-response-description: 'Description';
$msg-swagger-response-schema: 'Schema';

421
app/stylesheets/main.scss Normal file
View File

@ -0,0 +1,421 @@
//
//= Main layout
//
@import 'foundation-settings';
@import 'foundation-includes';
@import 'variables';
@import 'utils';
@import 'json-schema';
@import 'swagger';
@import 'monokai';
body {
// background-color: #fff !important;
}
//
// Generic styles
.no-padding {
padding: 0 !important;
}
.no-margin {
margin: 0 !important;
}
.default-label {
@include small-label('secondary');
}
//
// Topbar layout
.top-bar {
padding: 0.5rem;
height: $topbar-height;
color: $white;
// Make it fixed
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 1;
}
#logo {
span {
opacity: 0.5;
margin-left: 5px;
}
}
//
// Page layout
#page {
margin-top: $topbar-height;
}
//
// Sidebar layout
#sidebar {
padding-top: 1rem;
padding-left: 1rem;
padding-right: 1rem;
padding-bottom: 2rem;
border-right: 1px solid #eee;
background-color: #f6f6f6 !important;
height: 100vh;
//height: 100%;
overflow: scroll;
h5 {
margin: 1.25rem 0 0.5rem;
text-transform: uppercase;
color: $medium-gray;
font-size: 0.9rem;
}
a {
display: block;
font-size: 0.95rem;
margin: 0 0 0.175rem;
color: scale-color($black, $lightness: 15%);
white-space: nowrap;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
&.active {
// color: $primary-color;
color: $accent-color;
font-weight: bold;
}
}
ul {
list-style-type: none;
padding: 0;
margin: 0 0 0.75rem 0.75rem;
}
section {
> ul {
display: none;
}
&.expand {
> ul {
display: block;
}
}
}
}
@media screen and (min-width: $medium-width) { // medium and up
#sidebar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: $topbar-height;
}
}
//
// Documentation layout
.doc-content {
@extend .large-6;
padding-left: $column-padding !important;
padding-right: $column-padding !important;
}
.doc-separator {
margin-top: 2em;
padding-top: 2em;
padding-bottom: 2em;
border-top: 1px solid #e2e2e2;
}
#docs {
overflow: hidden;
.example-box {
display: none;
}
@media screen and (min-width: $large-width) { // medium and up
.example-box {
// width: 50%;
display: block;
background-color: #2E2F28;
background-color: #23241f;
position: absolute;
right: 0;
top: 0;
bottom: 0;
z-index: -1;
}
}
}
article {
.no-description {
color: $dark-gray;
}
dt {
color: $black;
}
table.table {
width: 100%;
}
code {
font-size: 0.85em;
border-radius: 3px;
}
p:last-child:first-child {
margin-bottom: 0;
}
#introduction {
padding-top: 2rem; // 30px
}
// > h1:first-child {
// margin-top: 0;
// margin-bottom: 2.5rem;
// }
//
// Titles
h1 {
margin: 2.5rem 0 0;
padding-top: 0.75rem;
padding-bottom: 0.75rem;
padding-left: $column-padding;
padding-right: $column-padding;
border-top: 1px solid #e8e8e8;
border-bottom: 1px solid #e2e2e2;
// border-top: 1px solid #ddd;
// border-bottom: 1px solid #ccc;
// background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgi…pZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
// background-size: 100%;
// background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #fff), color-stop(100%, #f9f9f9));
// background-image: -moz-linear-gradient(top, #fff, #f9f9f9);
// background-image: -webkit-linear-gradient(top, #fff, #f9f9f9);
// background-image: linear-gradient(to bottom, #fff, #f9f9f9);
background-color: #f6f6f6 !important;
}
h2 {
@extend .doc-separator;
margin-bottom: 0;
padding-left: $column-padding;
padding-right: $column-padding;
padding-bottom: 0.25rem;
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgi…gd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');
background-size: 100%;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(255,255,255,0.4)), color-stop(100%, rgba(255,255,255,0)));
background-image: -moz-linear-gradient(top, rgba(255,255,255,0.4), rgba(255,255,255,0));
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.4), rgba(255,255,255,0));
background-image: linear-gradient(to bottom, rgba(255,255,255,0.4), rgba(255,255,255,0));
}
h3 {
margin: 0rem 0 0.75rem;
}
> h1, > h2 {
@extend .doc-content;
}
h1+.panel > h2, h1+span+.panel > h2, h1+span+span+.panel > h2 {
margin-top: 0;
border-top: none;
}
h1+.panel h3 {
margin-top: 1rem;
}
//
// Definition panel
.panel {
position: relative;
> h2, > h3 {
@extend .doc-content;
// width:50%;
// padding-left: $column-padding;
// padding-right: $column-padding;
}
}
//
// Property grid
.prop-row {
// @extend .doc-row;
@extend .row;
@extend .collapse;
padding-top: 0.75em;
padding-bottom: 0.75em;
border-top: 1px solid #eee;
&:first-child, &.prop-group {
border-top: 1px solid #ddd;
}
.prop-title {
font-weight: bold;
}
.prop-name {
@extend .columns;
@extend .small-4;
@extend .text-right;
padding-right: 0.85rem !important;
word-break: break-all;
}
.prop-value {
@extend .columns;
@extend .small-8;
padding-left: 0.85rem !important;
word-wrap: break-word;
}
&.prop-inner {
@extend small;
.prop-name {
color: $dark-gray;
}
}
}
//
// Default grid
.doc-row {
@extend .row;
@extend .collapse;
margin: 30px 0 20px;
}
.doc-copy {
@extend .doc-content;
@extend .columns;
// > h2 {
// width: 100%;
// margin-left: -$column-padding;
// margin-right: -$column-padding;
// }
}
.doc-examples {
@extend .doc-content;
@extend .columns;
padding-left: $column-padding !important;
padding-right: $column-padding !important;
//color: $dark-gray;
color: $white;
background-color: $dark-background-color;
h5 {
color: $white;
//color: $medium-gray;
font-size: 1rem;
opacity: 0.8;
span {
opacity: 0.5;
//color: $dark-gray;
}
}
}
@media screen and (max-width: $large-width) { // medium and lower
.doc-examples:not(:empty) {
margin-top: 1.5rem;
padding-top: 1.5rem;
padding-bottom: 0.5rem;
}
}
//
// Powered by link
.powered-by {
@extend small;
color: $medium-gray;
span {
color: $accent-color;
}
}
//
// Operation panel
.operation {
.operation-tags {
position: absolute;
top: 0;
text-align: right;
right: 0;
}
@media screen and (min-width: $large-width) { // large and up
.operation-tags {
right: 50%;
}
}
.operation-path {
word-break: break-all;
}
}
}
//
// Code highlighting
//
.hljs {
padding: 0;
pre {
line-height: 1.25;
padding: 0.15rem 2rem;
border-radius: 5px;
box-shadow: 0 0 200px rgba(0, 0, 0, 0.33) inset;
margin-bottom: 1.5rem;
border-top: 1px solid #000;
border-bottom: 1px solid #404040;
white-space: pre-wrap;
word-break: normal;
word-spacing: normal;
}
code {
font-family: Consolas,"Liberation Mono",Courier,monospace;
font-weight: inherit;
color: inherit;
background-color: transparent;
border: none;
padding: 0;
}
}

11
app/views/main.hbs Normal file
View File

@ -0,0 +1,11 @@
{{! Renders the entire HTML page}}
<!doctype html>
<html class="no-js" lang="en">
<head>
{{>layout/head}}
</head>
<body>
{{>layout/topbar}}
{{>layout/page}}
</body>
</html>

2
app/views/minimal.hbs Normal file
View File

@ -0,0 +1,2 @@
{{! Renders the HTML page content without the <body/> tag }}
{{>layout/page}}

View File

@ -0,0 +1,7 @@
{{!
Show a subschema for additionaProperties of a `object` definition.
}}
<section class="json-schema-additionalProperties">
<span class="json-property-type">{{>json-schema/datatype}}</span>
{{>json-schema/body}}
</section>

View File

@ -0,0 +1,20 @@
{{!--
Renders a json-schema "allOf"-definition.
--}}
<section class="json-schema-allOf">
{{! Display ref (i.e. superclasses)}}
<section class="json-schema-allOf-inherited">
<ul>
{{#each .}}
{{>json-schema/reference .}}
{{/each}}
</ul>
</section>
<section class="json-schema-allOf-additional">
{{#each .}}
{{#unless $ref}}
{{>json-schema/body}}
{{/unless}}
{{/each}}
</section>
</section>

View File

@ -0,0 +1,16 @@
{{!--
Renders a json-schema "anyOf"-definition.
--}}
<section class="json-schema-anyOf">
{{! Display ref (i.e. superclasses)}}
<dl>
{{#each .}}
<dt>
{{>json-schema/datatype}}
</dt>
<dd>
{{>json-schema/body $ref=""}}
</dd>
{{/each}}
</dl>
</section>

View File

@ -0,0 +1,9 @@
{{!
renders a json-schema "items"-definition of array-types.
}}
<section class="json-schema-array-items">
{{>json-schema/datatype .}}
<div class="json-inner-schema">
{{>json-schema/body}}
</div>
</section>

View File

@ -0,0 +1,35 @@
{{!
Renders a json-schema without the surrounding panel.
@param {boolean} showType whether or not to show the plain datatype for primitives
}}
{{#if description}}
<section class="json-schema-description">
{{md description}}
</section>
{{/if}}
{{#if example}}
<section class="json-schema-example">
{{json example}}
</section>
{{/if}}
{{#if type}}
{{! Render differently depending on type}}
{{#ifeq type 'object'}}
{{>json-schema/type-object}}
{{else}}
{{#ifeq type 'array'}}
{{#if items}}
{{>json-schema/array-items items}}
{{/if}}
{{/ifeq}}
{{/ifeq}}
{{else}}
{{>json-schema/type-object}}
{{/if}}
{{#if allOf}}
{{>json-schema/allOf allOf}}
{{/if}}
{{#if anyOf}}
{{>json-schema/anyOf anyOf}}
{{/if}}

View File

@ -0,0 +1,64 @@
{{!
When properties are renderered this partial renders the datatype of a property,
with a link to the type-definition (in case of a $ref).
Depending on the input, it renders an augmented data-type (e.g. "string[]"),
the 'format'-value (e.g. "date-time") and "enum"-values.
@param {boolean} discriminator true, this property is a swagger-discriminator (in which case enums are rendered as links)
}}
<span class="json-property-type">
{{~#if $ref}}
{{~>json-schema/reference .}}
{{~else}}
{{~schemaDatatype .}}
{{~/if}}
</span>
{{~#if format}}
<span class="json-property-format">({{format}})</span>
{{~/if~}}
{{!-- Enum values --}}
{{~#if enum}}
<span class="json-property-enum" title="Possible values">, x &isin; {
{{#each enum}}
{{! Render enums in the descriminator as links to the appropriate definitions}}
{{#if ../discriminator}}
<span class="json-property-enum-item"><a href="#definition-{{.}}">{{.}}</a></span>
{{else}}
<span class="json-property-enum-item">{{.}}</span>
{{/if}}
{{#ifeq ../default .}}
(default)
{{/ifeq}}
{{#unless @last}},{{/unless}}
{{/each}}
}
</span>
{{~/if}}
{{!-- min- and max-values --}}
<span class="json-property-range" title="Value limits">
{{~schemaRange . ~}}
</span>
{{!-- Default values (for non-enum types) --}}
{{~#unless enum}}
{{~#if default}}
<span class="json-property-default-value">{{~default}}</span>
{{/if}}
{{~/unless}}
{{#if minLength}}
<span class="json-property-range" title="String length limits">
{{#if maxLength}}
({{minLength}} to {{maxLength}} chars)
{{else}}
(at least {{minLength}} chars)
{{/if}}
</span>
{{else}}
{{#if maxLength}}
<span class="json-property-range" title="String length limits">
(up to {{maxlength}} chars)
</span>
{{/if}}
{{/if}}

View File

@ -0,0 +1,6 @@
{{!Renders all subschemas}}
{{#if definitions}}
{{#eachSorted definitions}}
{{>json-schema/main-panel . title=@key anchor=true}}
{{/eachSorted}}
{{/if}}

View File

@ -0,0 +1,33 @@
{{!Renders json-schema object properties.}}
{{#if properties}}
<section class="json-schema-properties">
<dl>
{{#each properties}}
<dt data-property-name="{{htmlId @key}}">
{{!Single property, default type is "object"}}
<span class="json-property-name">{{@key}}:</span>
{{>json-schema/datatype discriminator=(equal @key ../discriminator)}}
{{#ifcontains ../required @key}}
<span class="json-property-required"></span>
{{/ifcontains}}
{{#ifeq @key ../discriminator}}
<span class="json-property-discriminator"></span>
{{/ifeq}}
{{#if readOnly}}
<span class="json-property-read-only"></span>
{{/if}}
</dt>
{{#if description}}
<dd>
{{md description}}
{{!-- Show details of nested property schema
<div class="json-inner-schema">
{{>json-schema/body $ref="" description=""}}
</div>
--}}
</dd>
{{/if}}
{{/each}}
</dl>
</section>
{{/if}}

View File

@ -0,0 +1,10 @@
{{!Renders a reference to a subschema}}
{{#if $ref}}
<span class="{{#if type}}json-schema-ref-{{type}}{{/if}}">
<a class="json-schema-ref" href="{{$ref}}">{{schemaSubschemaName $ref}}</a>
</span>
{{else if items.$ref}}
<span class="{{#if type}}json-schema-ref-{{type}}{{/if}}">
<a class="json-schema-ref" href="{{items.$ref}}">{{schemaSubschemaName items.$ref}}</a>
</span>
{{/if}}

View File

@ -0,0 +1,7 @@
{{!--
Renders the properties of an `object`
--}}
{{>json-schema/properties}}
{{#if additionalProperties}}
{{>json-schema/additionalProperties additionalProperties}}
{{/if}}

View File

@ -0,0 +1,25 @@
{{!--
This partial renders the documentation content
@api public
--}}
<article>
{{>swagger/introduction}}
{{>swagger/securityDefinitions}}
{{#if showTagSummary}}
{{>swagger/tags}}
{{else}}
{{>swagger/paths}}
{{/if}}
{{!swagger/paths paths=paths}}
{{!swagger/parameterDefinitions parameters=parameters}}
{{!swagger/responseDefinitions responses=responses}}
{{>swagger/definitions definitions=definitions}}
{{! Powered by link }}
<div class="doc-row no-margin">
<div class="doc-copy doc-separator">
<a class="powered-by" href="http://sourcey.com/spectacle">Documentation by <span>Spectacle</span></a>
</div>
</div>
</article>

View File

@ -0,0 +1,9 @@
{{!--
Renders the HTML head content
--}}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{info.title}} | API Reference</title>
<link rel="stylesheet" href="stylesheets/main.min.css" />
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="/javascripts/main.js"></script>

View File

@ -0,0 +1,66 @@
{{!--
Renders the sidebar navigation
@param {object} $context$ the whole swagger definition
@api public
--}}
<nav id="nav" role="navigation">
<h5>API Reference</h5>
<a href="#introduction">Introduction</a>
{{#if securityDefinitions}}
<h5>Security</h5>
<a href="#authentication">Authentication</a>
{{/if}}
{{#if showTagSummary}}
<h5>Operations</h5>
{{#eachSorted tags}}
<section>
<a href="#tag-{{htmlId name}}">{{name}}</a>
<ul>
{{#each operations}}
<li>
<a href="#operation-{{htmlId path}}-{{htmlId method}}">
{{#if summary}}
{{summary}}
{{else}}
{{toUpperCase method}} {{path}}
{{/if}}
</a>
</li>
{{/each}}
</ul>
</section>
{{/eachSorted}}
{{else}}
<h5>Paths</h5>
{{#eachSorted paths}}
<!-- <section>
<a href="#path-{{htmlId @key}}">{{@key}}</a>
<ul> -->
{{#each .}}
<!-- <li> -->
<a href="#operation-{{htmlId path}}-{{htmlId method}}">
{{#if summary}}
{{summary}}
{{else}}
{{toUpperCase method}} {{path}}
{{/if}}
</a>
<!-- </li> -->
{{/each}}
<!-- </ul>
</section> -->
{{#eachSorted .}}
{{!swagger/operation . operation=. method=@key path=../path}}
{{/eachSorted}}
{{/eachSorted}}
{{/if}}
<h5>Schema Definitions</h5>
{{#eachSorted definitions}}
<a href="#definition-{{htmlId @key}}">
{{@key}}
</a>
{{/eachSorted}}
</nav>

View File

@ -0,0 +1,13 @@
{{!--
This partial renders the main page content
@api public
--}}
<div id="page" class="row collapse expanded">
<div id="docs" class="medium-9 large-10 medium-push-3 large-push-2 columns">
{{>layout/content}}
<div class="example-box doc-content"></div>
</div>
<div id="sidebar" class="medium-3 large-2 medium-pull-9 large-pull-10 columns">
{{>layout/nav}}
</div>
</div>

View File

@ -0,0 +1,28 @@
<div class="top-bar">
<div class="top-bar-left">
<ul class="dropdown menu" data-dropdown-menu>
<li id="logo" class="menu-text">
{{#if logo}}
{{logo}}
{{else}}
{{info.title}} <span>API Reference</span>
<!-- <span class="default-logo">Your Logo Here</span> -->
{{/if}}
</li>
</ul>
</div>
{{! Add right menu items like so... }}
{{!--
<div class="top-bar-right">
<ul class="menu">
<li><input type="search" placeholder="Search"></li>
<li><button type="button" class="button">Search</button></li>
<li><a href="#">curl</a></li>
<li><a href="#">Ruby</a></li>
<li><a href="#">JavaScript</a></li>
<li><a href="#">PHP</a></li>
<li><a href="#">Java</a></li>
</ul>
</div>
--}}
</div>

View File

@ -0,0 +1,27 @@
{{!
Renders a json.schema inside a bootstrap-panel.
@public
@readonly
}}
<div id="definition-{{htmlId @key}}"class="panel panel-definition"
data-traverse-target="definition-{{htmlId @key}}" >
{{#if title}}
{{#if anchor}}
<h2 class="panel-title">
<a name="/definitions/{{title}}"></a>{{title}}:
<span class="json-property-type">{{>json-schema/datatype}}</span>
</h2>
{{else}}
<h2 class="panel-title">{{title}}</h2>
{{/if}}
{{/if}}
<div class="doc-row">
<div class="doc-copy">
{{#if $ref}}
{{>json-schema/reference .}}
{{else}}
{{>json-schema/body}}
{{/if}}
</div>
</div>
</div>

View File

@ -0,0 +1,13 @@
{{!--
Renders the definition-section of the HTML-page.
@param {Definition[]} definitions a list of JSON-subschemas.
@api public
--}}
{{#if definitions}}
<h1>Schema definitions</h1>
{{#eachSorted definitions}}
{{>swagger/definition title=@key anchor="/definitions" }}
{{/eachSorted}}
{{/if}}

View File

@ -0,0 +1,26 @@
{{!--
Global parameter definitions (see https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#parametersDefinitionsObject)
@param {object<Parameter>} parameters the parameter-definitions object
@api public
--}}
{{#if parameters}}
<h1>Parameter definitions</h1>
<table id="table-parameter-definitions" class="table">
<thead>
<tr>
<th class="swagger-param-key"></th>
<th class="swagger-prop-name"></th>
<th class="swagger-prop-valueription"></th>
<th class="swagger-param-type"></th>
<th class="swagger-param-data-type"></th>
<th class="swagger-param-annotation"></th>
</tr>
</thead>
<tbody>
{{#each parameters}}
{{> swagger/parameterRow key=@key parameter=.}}
{{/each}}
</tbody>
</table>
{{/if}}

View File

@ -0,0 +1,33 @@
{{!--
Display a single parameter in a table row.
@param {Parameter} parameter a parameter object
@param {string=} key a reference key (if present, this is display in an additional
column in front of the other columns
@param {string=} $ref the reference path of the parameter, in case it is a reference to a default parameter
@api public
--}}
<tr>
{{#if key}}
<td><a name="/parameters/{{key}}"></a>{{key}}</td>
{{/if}}
<td>
{{parameter.name}}
<td>{{md parameter.description}}</td>
<td>{{parameter.in}}</td>
<td>
{{~>json-schema/datatype parameter ~}}
{{~#if parameter.collectionFormat ~}}
<span class="swagger-param-collection-format">,
{{md (swagger-collection-format parameter.collectionFormat parameter.name) stripParagraph=true}}
</span>
{{~/if}}
</td>
<td>
{{#if parameter.required}}
<span class="json-property-required"></span>
{{/if}}
{{#if $ref}}
<span class="swagger-global"></span> <span class="json-schema-reference"><a href="{{$ref}}">{{$ref}}</a></span>
{{/if}}
</td>
</tr>

View File

@ -0,0 +1,35 @@
{{!--
Renders the parameter table within a operation definition.
@param {Array<Parameter>|Object<Parameter>} parameters a list of Swagger-Parameter objects
If this is an object, an it is expected to be the global parameter list
https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#parametersDefinitionsObject
and the key of each entry is display in a column in front of the other columns.
@api public
--}}
{{#if parameters}}
<section class="doc-row swagger-request-params">
<div class="doc-copy">
<table class="table">
<thead>
<tr>
<th class="swagger-prop-name"></th>
<th class="swagger-prop-valueription"></th>
<th class="swagger-param-type"></th>
<th class="swagger-param-data-type"></th>
<th class="swagger-param-annotation"></th>
</tr>
</thead>
<tbody>
{{#each parameters}}
{{#if $ref}}
{{> swagger/parameterRow parameter=(json-schema-resolve-ref $ref) $ref=$ref}}
{{else}}
{{> swagger/parameterRow parameter=.}}
{{/if}}
{{/each}}
</tbody>
</table>
</div>
</section>
{{/if}}

View File

@ -0,0 +1,46 @@
{{!--
Renders details about a single response
@param {Response} response a swagger response-object
--}}
{{! If global is set, attach an id to the table row }}
<div class="row">
<div class="col-md-12">
{{md response.description}}
</div>
</div>
<div class="row">
<div class="col-md-6 swagger-response-model">{{>swagger/model model=response.schema}}</div>
{{#each response.examples}}
<div class="col-md-6 swagger-response-examples">
<div class="label secondary">Example for {{@key}}</div>
{{json .}}
</div>
{{/each}}
{{#if response.headers}}
<div class="col-md-12">
<section class="swagger-response-headers">
<table class="table">
<thead>
<tr>
<th class="swagger-response-header-name"></th>
<th class="swagger-response-header-description"></th>
<th class="swagger-response-header-data-type"></th>
</tr>
</thead>
<tbody>
{{#each response.headers}}
{{#if $ref}}
{{> swagger/responseHeaderRow header=(json-schema-resolve-ref $ref) name=@key}}
{{else}}
{{> swagger/responseHeaderRow header=. name=@key}}
{{/if}}
{{/each}}
</tbody>
</table>
</section>
</div>
{{/if}}
</div>

View File

@ -0,0 +1,15 @@
{{!--
Renders the response definitions
--}}
{{#if responses}}
<h1>Response definitions</h1>
<dl id="swagger-response-definitions">
{{#each responses}}
<dt><a name="{{key}}"></a>{{@key}}</dt>
<dd>
{{> swagger/response response=.}}
</dd>
{{/each}}
</dl>
{{/if}}

View File

@ -0,0 +1,20 @@
{{!--
Display a single parameter in a table row.
@param {Header} header a (Header)[https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#headerObject] object
@param {string} name the name of the response header
@api public
--}}
<tr class="swagger-response-header-{{htmlId name}}">
<td>
{{name}}
<td>{{md header.description}}</td>
<td>
{{>json-schema/datatype header}}
{{#if header.items}}
,{{>json-schema/datatype header.items}}
{{/if}}
{{#if header.collectionFormat}}
<div class="swagger-param-collection-format">{{md (swagger-collection-format header.collectionFormat)}}</div>
{{/if}}
</td>
</tr>

View File

@ -0,0 +1,37 @@
{{!--
Renders the responses section of an operation
@param {Response[]} responses a list of Swagger-Response definitions
@param {string[]} produces a list of response content types produced by the operation
@api public
--}}
{{#if responses}}
<section class="swagger-responses">
{{#if produces}}
<p>{{>swagger/list-of-labels values=produces}}</p>
{{else if @root.produces}}
<p><a href="#swagger-default-produces">Uses default content-types: </a>
{{>swagger/list-of-labels values=@root.produces}}
</p>
{{/if}}
<dl>
{{#each responses}}
<dt class="swagger-response-{{@key}}">
{{! Use response-code and http-name as text}}
{{@key}} {{httpResponseCode @key}}
{{#if $ref}}
<span class="swagger-global"></span> <span class="json-schema-reference"><a href="{{$ref}}">{{$ref}}</a></span>
{{/if}}
</dt>
<dd class="swagger-response-{{@key}}">
{{#if $ref}}
{{>swagger/response response=(json-schema-resolve-ref $ref)}}
{{else}}
{{>swagger/response response=.}}
{{/if}}
</dd>
{{/each}}
</dl>
</section>
{{/if}}

View File

@ -0,0 +1,34 @@
{{!--
Renders a summary of this services ignoring tags, containing references to all operations and paths
@todo params
@api public
--}}
<h1 id="swagger-summary-no-tags">Summary</h1>
<table class="table table-bordered table-condensed swagger-summary">
<thead>
<tr>
<th>Path</th>
<th>Operation</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{{#eachSorted paths}}
{{#eachSorted .}}
<tr>
{{#if @first}}
<td class="swagger-summary-path" rowspan="{{@length}}">
<a href="#path-{{htmlId @../key}}">{{@../key}}</a>
</td>
{{/if}}
<td>
<a href="#operation-{{htmlId @../key}}-{{htmlId @key}}">{{toUpperCase @key}}</a>
</td>
<td>
{{md summary}}
</td>
</tr>
{{/eachSorted}}
{{/eachSorted}}
</tbody>
</table>

View File

@ -0,0 +1,30 @@
{{!--
Renders a summary based on the tags of this services, containing references to all operations and paths
@param {Array<{name:string, summary:string, operations: object}>} tags a list of tags
@api public
--}}
<h1 id="swagger-summary-tags">Summary</h1>
{{#eachSorted tags}}
<div class="panel">
<h3 id="tag-{{htmlId name}}" class="swagger-summary-tag">Tag: {{name}}</h3>
{{md description}}
<table class="table table-bordered table-condensed swagger-summary">
<thead>
<tr>
<th>Operation</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{{#each operations}}
<tr>
<td><a href="#operation-{{htmlId path}}-{{htmlId method}}">{{toUpperCase method}} {{path}}</a></td>
<td>{{md summary}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
{{/eachSorted}}

View File

@ -0,0 +1,12 @@
{{!--
Renders a json-schema model within swagger (calls json-schema-partials).
@param {JsonSchema} model a JSON-schema definition
@param {string} title the name of the definition
@api public
--}}
{{#if description}}
{{md description}}
{{else}}
<p class="no-description">(no description)</p>
{{/if}}

View File

@ -0,0 +1,37 @@
<div id="introduction" data-traverse-target="introduction" class="doc-row">
<div class="doc-copy">
{{md info.description}}
</div>
<div class="doc-examples">
<section>
<h5>API Endpoint</h5>
<div class="hljs">
<pre><code>{{swaggerEndpoint}}</code></pre>
</div>
{{#if consumes}}
<h5>Request Content-Types:
<span>{{#each consumes}}{{.}}{{#unless @last}}, {{/unless}}{{/each}}</span>
</h5>
{{/if}}
{{#if produces}}
<h5>Response Content-Types:
<span>{{#each produces}}{{.}}{{#unless @last}}, {{/unless}}{{/each}}</span>
</h5>
{{/if}}
{{#if schemes}}
<h5>Schemes:
<span>{{#each schemes}}{{.}}{{#unless @last}}, {{/unless}}{{/each}}</span>
</h5>
{{/if}}
{{#if info.version}}
<h5>Version:
<span>{{info.version}}</span>
</h5>
{{/if}}
</section>
</div>
</div>

View File

@ -0,0 +1,5 @@
{{!--
Renders an array of strings as list of labels
@param {string[]} values
--}}
{{#each values}}<span class="default-label">{{.}}</span> {{/each}}

View File

@ -0,0 +1,9 @@
{{!--
Renders a json-schema model within swagger (calls json-schema-partials).
@param {JsonSchema} model a JSON-schema definition
@param {string} title the name of the definition
@api public
--}}
{{#if model}}
{{/if}}

View File

@ -0,0 +1,78 @@
{{!--
This partial renders a box containing information about a single operation of the service
(such as calling a POST on the "/pets" resource).
@param {Operation} operation a Swagger-Operation object.
@param {string} method the http-method (GET, POST, DELETE, PUT, PATCH)
@api public
--}}
<div id="operation-{{htmlId path}}-{{htmlId method}}" class="operation panel"
data-traverse-target="operation-{{htmlId path}}-{{htmlId method}}">
{{#if tags}}
<!-- <section class="operation-tags row"> -->
<!-- <div class="doc-copy"> -->
<div class="operation-tags">
{{#each tags}}
<a class="label" href="#tag-{{htmlId .}}">{{.}}</a><!--{{#unless @last}}, {{/unless}}-->
{{/each}}
</div>
<!-- </div> -->
<!-- </section> -->
{{/if}}
<h2 class="operation-title">
{{! Fill the title with the summary or path }}
{{#if summary}}
<div class="operation-summary">{{md summary stripParagraph="true"}}</div>
{{else}}
<div class="operation-name">
<span class="operation-name">{{toUpperCase method}}</span>
<span class="operation-path">{{path}}</span>
</div>
{{/if}}
</h2>
{{! Insert the operation name here if not in the title }}
{{#if summary}}
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-operation-path">
<span class="operation-method">{{toUpperCase method}}</span>
<span class="operation-path">{{path}}</span>
</section>
</div>
</div>
{{/if}}
{{#if operation.description}}
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-operation-description">
{{md operation.description}}
</section>
</div>
</div>
{{/if}}
<div class="doc-row">
<div class="doc-copy">
{{! The _request_body variable is filled with the parameter `body` by the preprocessor. }}
{{#if _show_requst_body_section}}
{{>swagger/request-body consumes=operation.consumes body=_request_body}}
{{/if}}
{{>swagger/parameters parameters=parameters}}
</div>
{{! Print examples without whitespace }}
<div class="doc-examples">{{#if _show_requst_body_section}}
<section>
<h5>Request Example</h5>
{{>swagger/print-schema _request_body.schema}}
</section>
{{/if}}</div>
</div>
{{>swagger/responses}}
{{>swagger/security}}
</div>

View File

@ -0,0 +1,48 @@
{{!--
Renders the parameter table within a operation definition.
@param {Array<Parameter>|Object<Parameter>} parameters a list of Swagger-Parameter objects
If this is an object, an it is expected to be the global parameter list
https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#parametersDefinitionsObject
and the key of each entry is display in a column in front of the other columns.
@api public
--}}
{{#if parameters}}
<section class="swagger-request-params">
{{#each parameters}}
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">{{name}}</div>
{{#if required}}
<span class="json-property-required"></span>
{{/if}}
{{!--
{{#if schema.$ref}}
<span class="swagger-global"></span> <span class="json-schema-reference"><a href="{{$ref}}">{{$ref}}</a></span>
{{/if}}
--}}
</div>
<div class="prop-value">
{{>swagger/description description}}
</div>
</div>
{{! parameter `collectionFormat` field }}
{{~#if collectionFormat ~}}
<div class="prop-row prop-inner">
<div class="prop-name param-label">format</div>
<div class="prop-value">{{md (swaggerCollectionFormat collectionFormat name) stripParagraph=true}}</div>
</div>
{{~/if}}
{{! parameter `type` field }}
<div class="prop-row prop-inner">
<div class="prop-name param-label">type</div>
<div class="prop-value">{{~>json-schema/datatype . ~}}</div>
</div>
{{! parameter `in` field }}
<div class="prop-row prop-inner">
<div class="prop-name param-label">in</div>
<div class="prop-value">{{in}}</div>
</div>
{{/each}}
</section>
{{/if}}

View File

@ -0,0 +1,11 @@
{{!--
Renders a single path definition with all its methods (GET, POST).
@param {string} path the request path
@param {object<Operation>} pathItems a swagger [path-item-object](http://swagger.io/specification/#pathItemObject)
@api public
--}}
<span id="path-{{htmlId path}}"></span>
{{#eachSorted pathItems}}
{{>swagger/operation . operation=. method=@key path=../path}}
{{/eachSorted}}

View File

@ -0,0 +1,10 @@
{{!--
Renders the paths-section of the Rest-Service definition
@params {Path[]} paths a list of Swagger Path-Objects
@api public
--}}
<h1>Paths</h1>
{{#eachSorted paths}}
{{>swagger/path pathItems=. path=@key}}
{{/eachSorted}}

View File

@ -0,0 +1,7 @@
{{#if $ref}}
<div class="hljs">{{{printSchemaReference $ref type=type}}}</div>
{{else if items.$ref}}
<div class="hljs">{{printSchemaReference items.$ref type=type}}</div>
{{else}}
<div class="hljs">{{!printSchema .}}</div>
{{/if}}

View File

@ -0,0 +1,35 @@
{{!--
Renders the request-body section of an operation.
@param {string[]} consumes a list of request content type eligible for this operation.
@param {Parameter} body the param-Object of the `body`-parameter
@api public
--}}
<section class="swagger-request-body">
{{!--
{{#if consumes}}
<p>{{>swagger/list-of-labels values=consumes}}</p>
{{else if @root.consumes}}
<p>
<!-- <a href="#swagger-default-consumes">Consumes: </a> -->
{{>swagger/list-of-labels values=@root.consumes}}
</p>
{{/if}}
--}}
{{#if body}}
{{#with body}}
<div class="prop-row">
<div class="prop-name">
<div class="swagger-request-model">
{{>json-schema/reference schema}}
</div>
</div>
<div class="prop-value columns small-6">
<!-- <div class="swagger-request-description"> -->
{{md description}}
<!-- </div> -->
</div>
</div>
{{/with}}
{{/if}}
</section>

View File

@ -0,0 +1,53 @@
{{!--
Renders the responses section of an operation
@param {Response[]} responses a list of Swagger-Response definitions
@param {string[]} produces a list of response content types produced by the operation
@api public
--}}
{{#if responses}}
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-responses">
{{!--
{{#if produces}}
<p>{{>swagger/list-of-labels values=produces}}</p>
{{else if @root.produces}}
<p>
<!-- <a href="#swagger-default-produces">Uses default content-types: </a> -->
{{>swagger/list-of-labels values=@root.produces}}
</p>
{{/if}}
--}}
{{#each responses}}
<div class="prop-row prop-group">
<div class="prop-name">
{{! Use response-code and http-name as text}}
<div class="prop-title">{{@key}} {{httpResponseCode @key}}</div>
{{#if schema}}
<div class="prop-ref">{{>json-schema/reference schema}}</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href="{{$ref}}">{{$ref}}</a></span> -->
{{/if}}
</div>
<div class="prop-value">
{{md description}}
</div>
</div>
{{/each}}
</section>
</div>
{{! Print examples without whitespace. }}
<div class="doc-examples">{{#each responses}}{{#if example}}
<section>
<h5>Response Example <span>({{@key}} {{httpResponseCode @key}})</span></h5>
{{>swagger/print-schema example}}
</section>
{{else if schema}}
<section>
<h5>Response Example <span>({{@key}} {{httpResponseCode @key}})</span></h5>
{{>swagger/print-schema schema}}
</section>
{{/if}}{{/each}}</div>
</div>
{{/if}}

View File

@ -0,0 +1,35 @@
{{!--
Renders the security definitions of the Rest-service.
@param {Security[]} security TODO
@api public
--}}
{{#if security}}
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-request-security">
<table class="table">
<thead>
<tr>
<th class="swagger-request-security-schema"></th>
<th class="swagger-request-security-scopes"></th>
</tr>
</thead>
<tbody>
{{#each security}}
{{#each this}}
<tr>
<td><a href="#security-definition-{{@key}}">{{@key}}</a></td>
<td>
{{#each this}}
{{#unless @first}}, {{/unless}}{{this}}
{{/each}}
</td>
</tr>
{{/each}}
{{/each}}
</tbody>
</table>
</section>
</div>
</div>
{{/if}}

View File

@ -0,0 +1,54 @@
{{!--
Renders the security-section of the HTML-page
TODO: Parameters
@api public
--}}
{{#if securityDefinitions}}
<!-- <h1 id="security" data-traverse-target="security">Security</h1> -->
<h1 id="authentication" data-traverse-target="authentication">Authentication</h1>
{{#each securityDefinitions}}
<div id="security-definition-{{@key}}" class="panel">
<div class="doc-row">
<div class="doc-copy">
<h3 class="security-definition-title">
<span class="security-name">{{@key}}</span>
<span class="swagger-security-definition-{{type}}"></span>
</h3>
<section class="swagger-security-definition-properties">
{{#each this}}
{{#ifeq @key 'type'}}
{{else}}
<div class="prop-row security-definition-property">
<div class="prop-name">
<div class="prop-title security-definition-property-name">{{@key}}</div>
</div>
<div class="prop-value security-definition-property-type">
{{this}}
</div>
</div>
{{/ifeq}}
{{/each}}
</section>
<!-- <h3 class="security-definition-title">
<span class="security-name">{{@key}}</span>
<strong><span class="swagger-security-definition-{{type}}"></span></strong>
</h3>
<section class="swagger-security-definition-properties">
{{#each this}}
{{#ifeq @key 'type'}}
{{else}}
<div class="security-definition-property">
<strong class="security-definition-property-name">{{@key}}:</strong>
<span class="security-definition-property-type">{{this}}</span>
<div>
{{/ifeq}}
{{/each}}
</section> -->
</div>
</div>
</div>
{{/each}}
{{/if}}

View File

@ -0,0 +1,8 @@
{{! Operations sorted by tags }}
{{#eachSorted tags}}
<h1 id="tag-{{htmlId name}}" class="swagger-summary-tag" data-traverse-target="tag-{{htmlId name}}">{{name}}</h1>
{{md description}}
{{#each operations}}
{{>swagger/operation . operation=. method=method path=path}}
{{/each}}
{{/eachSorted}}

204
bin/cli.js Normal file
View File

@ -0,0 +1,204 @@
#!/usr/bin/env node
var program = require('commander'),
grunt = require('grunt'),
fs = require('fs'),
path = require('path'),
predentation = require('predentation'),
package = require('../package');
// Set CWD to root dir
process.chdir(__dirname + '/..');
//
//= Process CLI input
program.version(package.version)
.usage('spactacle [options] <specfile>')
.description(package.description)
.option('-A, --skip-assets', 'omit CSS and JavaScript generation (default: false)', Boolean, false)
.option('-e, --embeddable', 'omit the HTML <body/> and generate the documentation content only (default: false)')
.option('-d, --development-mode', 'start HTTP server with the file watcher and live reload (default: false)')
.option('-s, --start-server', 'start the HTTP server without any development features')
.option('-p, --port <dir>', 'the port number for the HTTP server to listen on (default: 4400)', Number, 4400)
.option('-t, --target-dir <dir>', 'the target build directory (default: ./public)', String, './public')
.option('-a, --app-dir <dir>', 'the application source directory (default: ./app)', String, './app')
// .option('-f, --spec-file <file>', 'the input OpenAPI/Swagger spec file (default: test/fixtures/petstore.json)', String, 'test/fixtures/petstore.json')
// .option('-c, --config-file <file>', 'Specify a custom configuration file (default: config.json)')
// .option('-c, --cache-dir <dir>', 'the intermediate cache directory (default: ./.cache)', String, './.cache')
.parse(process.argv);
// Show help if no specfile or options are specified
if (program.args.length <= 1 || program.rawArgs.length <= 1) {
program.help();
}
//
//= Load the specification and set variables
var specFile = program.args[0],
schema = require(path.resolve(specFile)),
templateData = require(path.resolve(program.appDir + '/lib/preprocessor'))(schema);
//
//= Setup Grunt for the heavy lifting
grunt.initConfig({
pkg: package,
// jshint: {
// all: [program.appDir + '/**/*.js']
// },
compass: {
dist: {
options: {
sassDir: program.appDir + '/stylesheets',
cssDir: program.cacheDir + '/stylesheets',
environment: 'development',
outputStyle: 'compressed',
importPath: [
path.resolve(__dirname, '..', 'node_modules', 'foundation-sites', 'scss')
]
}
}
},
concat: {
js: {
src: [program.appDir + '/javascripts/**/*.js', '!' + program.appDir + '/javascripts/jquery*.js'],
dest: program.targetDir + '/javascripts/main.js',
},
css: {
src: [program.cacheDir + '/stylesheets/*.css'],
dest: program.targetDir + '/stylesheets/main.css',
}
},
uglify: {
build: {
src: program.targetDir + '/javascripts/main.js',
dest: program.targetDir + '/javascripts/main.min.js'
}
},
cssmin: {
minify: {
expand: true,
cwd: program.targetDir + '/stylesheets',
src: ['*.css', '!*.min.css'],
dest: program.targetDir + '/stylesheets',
ext: '.min.css'
}
},
// handlebars: {
// compile: {
// files: {
// program.cacheDir + '/hbs/templates.js': [program.appDir + '/views/**/*.hbs']
// }
// },
// options: {
// namespace: 'Spectacle.templates',
// partialsUseNamespace: true,
// processName: function(filePath) {
// var parts = filePath.split('/'),
// target = parts[parts.length - 1];
// return target.split('.')[0];
// }
// }
// },
'compile-handlebars': {
compile: {
files: [{
src: program.appDir + '/views/' + (program.embeddable ? 'minimal.hbs' : 'main.hbs'),
dest: program.cacheDir + '/index.html'
}],
templateData: templateData,
helpers: program.appDir + '/helpers/*.js',
partials: program.appDir + '/views/partials/**/*.hbs'
},
},
clean: {
cache: [program.cacheDir],
assets: [program.targetDir + '/**/*.css', program.targetDir + '/**/*.js'],
html: [program.cacheDir + '/**/*.html', program.targetDir + '/**/*.html']
},
connect: {
server: {
options: {
hostname: '*',
port: program.port,
base: program.targetDir,
livereload: true
}
}
},
watch: {
options: {
livereload: true
},
js: {
files: [program.appDir + '/javascripts/**/*.js'],
tasks: ['javascripts']
},
css: {
files: [program.appDir + '/stylesheets/**/*.scss'],
tasks: ['stylesheets']
},
templates: {
files: [
program.appDir + '/views/**/*.hbs',
program.appDir + '/helpers/**/*.js',
program.appDir + '/lib/**/*.js'
],
tasks: ['templates']
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-compile-handlebars');
grunt.registerTask('predentation', 'Remove indentation from generated <pre> tags.', function() {
fs.createReadStream(program.cacheDir + '/index.html')
.pipe(predentation())
.pipe(fs.createWriteStream(program.targetDir + '/index.html'));
});
grunt.registerTask('stylesheets', ['compass', 'concat:css', 'cssmin']);
grunt.registerTask('javascripts', ['concat:js', 'uglify']);
grunt.registerTask('templates', ['clean:html', 'compile-handlebars', 'predentation']); //, 'handlebars'
grunt.registerTask('default', ['stylesheets', 'javascripts', 'templates']);
grunt.registerTask('server', ['connect']);
grunt.registerTask('develop', ['server', 'watch']);
// Report, etc when all tasks have completed.
grunt.task.options({
error: function(e) {
console.warn('Task error:', e);
// TODO: fail here or push on?
},
done: function() {
console.log('All tasks complete');
}
});
//
//= Run the shiz
if (program.startServer) {
grunt.task.run('server');
}
else {
if (!program.skipAssets) {
grunt.task.run(['stylesheets', 'javascripts']);
}
grunt.task.run('templates');
if (program.developmentMode) {
grunt.task.run('develop');
}
}
grunt.task.start();

56
package.json Normal file
View File

@ -0,0 +1,56 @@
{
"name": "spectacle-docs",
"version": "0.2.2",
"description": "Generate beautiful HTML5 documentation from a OpenAPI/Swagger 2.0 specification",
"bin": {
"spectacle": "bin/cli.js"
},
"scripts": {
"build": "spectacle test/fixtures/petstore.json",
"develop": "spectacle -d test/fixtures/petstore.json",
"server": "spectacle -s"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sourcey/spectacle.git"
},
"keywords": [
"openapi",
"swagger",
"docs",
"documentation",
"builder",
"generator"
],
"author": "Kam Low <auscaster@gmail.com> (http://sourcey.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/sourcey/spectacle/issues"
},
"homepage": "https://github.com/sourcey/spectacle",
"dependencies": {
"cheerio": "^0.19.0",
"clarify": "^1.0.5",
"commander": "*",
"foundation-sites": "^6.1.1",
"grunt": "^0.4.5",
"grunt-compile-handlebars": "^2.0.0",
"grunt-contrib-clean": "^0.7.0",
"grunt-contrib-compass": "^1.0.4",
"grunt-contrib-concat": "^0.5.1",
"grunt-contrib-connect": "^0.11.2",
"grunt-contrib-cssmin": "^0.14.0",
"grunt-contrib-handlebars": "^0.11.0",
"grunt-contrib-jshint": "^0.11.3",
"grunt-contrib-uglify": "^0.11.0",
"grunt-contrib-watch": "^0.6.1",
"handlebars": "^4.0.5",
"highlight.js": "^9.1.0",
"json-stable-stringify": "*",
"lodash": "*",
"marked": "*",
"node-sass": "^3.4.2",
"predentation": "^0.1.1",
"trace": "^1.1.0"
}
}

729
public/index.html Normal file
View File

@ -0,0 +1,729 @@
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Swagger Petstore | API Reference</title>
<link rel="stylesheet" href="stylesheets/main.min.css">
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="/javascripts/main.js"></script>
</head>
<body>
<div class="top-bar">
<div class="top-bar-left">
<ul class="dropdown menu" data-dropdown-menu="">
<li id="logo" class="menu-text">
Swagger Petstore <span>API Reference</span>
<!-- <span class="default-logo">Your Logo Here</span> -->
</li>
</ul>
</div>
</div>
<div id="page" class="row collapse expanded">
<div id="docs" class="medium-9 large-10 medium-push-3 large-push-2 columns">
<article>
<div id="introduction" data-traverse-target="introduction" class="doc-row">
<div class="doc-copy">
<p>A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification</p>
</div>
<div class="doc-examples">
<section>
<h5>API Endpoint</h5>
<div class="hljs">
<pre><code>http://petstore.swagger.io/api</code></pre>
</div>
<h5>Request Content-Types:
<span>application/json</span>
</h5>
<h5>Response Content-Types:
<span>application/json</span>
</h5>
<h5>Schemes:
<span>http</span>
</h5>
<h5>Version:
<span>1.0.0</span>
</h5>
</section>
</div>
</div>
<h1>Paths</h1>
<span id="path--pets"></span>
<div id="operation--pets-get" class="operation panel" data-traverse-target="operation--pets-get">
<h2 class="operation-title">
<div class="operation-name">
<span class="operation-name">GET</span>
<span class="operation-path">/pets</span>
</div>
</h2>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-operation-description">
<p>Returns all pets from the system that the user has access to
Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia.</p>
<p>Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien.</p>
</section>
</div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-request-params">
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">tags</div>
</div>
<div class="prop-value">
<p class="no-description">(no description)</p>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">format</div>
<div class="prop-value">comma separated (<code>tags=aaa,bbb</code>)</div>
</div> <div class="prop-row prop-inner">
<div class="prop-name param-label">type</div>
<div class="prop-value"><span class="json-property-type">string[]</span>
<span class="json-property-range" title="Value limits"></span>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">in</div>
<div class="prop-value">query</div>
</div>
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">limit</div>
</div>
<div class="prop-value">
<p class="no-description">(no description)</p>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">type</div>
<div class="prop-value"><span class="json-property-type">integer</span> <span class="json-property-format">(int32)</span>
<span class="json-property-range" title="Value limits"></span>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">in</div>
<div class="prop-value">query</div>
</div>
</section>
</div>
<div class="doc-examples"></div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-responses">
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">200 OK</div>
<div class="prop-ref"> <span class="json-schema-ref-array">
<a class="json-schema-ref" href="#/definitions/Pet">Pet</a>
</span>
</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href=""></a></span> -->
</div>
<div class="prop-value">
<p>pet response</p>
</div>
</div>
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">default </div>
<div class="prop-ref"> <span class="">
<a class="json-schema-ref" href="#/definitions/Error">Error</a>
</span>
</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href=""></a></span> -->
</div>
<div class="prop-value">
<p>unexpected error</p>
</div>
</div>
</section>
</div>
<div class="doc-examples"> <section>
<h5>Response Example <span>(200 OK)</span></h5>
<div class="hljs"><pre><code class="lang-json">
[
{
&quot;<span class="hljs-attr">allOf</span>&quot;: [
{
&quot;<span class="hljs-attr">$ref</span>&quot;: <span class="hljs-string"><a href="#definition-NewPet">&quot;#/definitions/NewPet&quot;</a></span>
},
{
&quot;<span class="hljs-attr">properties</span>&quot;: {
&quot;<span class="hljs-attr">id</span>&quot;: {
&quot;<span class="hljs-attr">format</span>&quot;: <span class="hljs-string">&quot;int64&quot;</span>,
&quot;<span class="hljs-attr">type</span>&quot;: <span class="hljs-string">&quot;integer&quot;</span>
}
},
&quot;<span class="hljs-attr">required</span>&quot;: [
<span class="hljs-string">&quot;id&quot;</span>
]
}
],
&quot;<span class="hljs-attr">type</span>&quot;: <span class="hljs-string">&quot;object&quot;</span>
}
]
</code></pre>
</div>
</section>
<section>
<h5>Response Example <span>(default )</span></h5>
<div class="hljs"><pre><code class="lang-json">
{
&quot;<span class="hljs-attr">code</span>&quot;: <span class="hljs-string">&quot;integer(int32)&quot;</span>,
&quot;<span class="hljs-attr">message</span>&quot;: <span class="hljs-string">&quot;string&quot;</span>
}
</code></pre>
</div>
</section>
</div>
</div>
</div>
<div id="operation--pets-post" class="operation panel" data-traverse-target="operation--pets-post">
<h2 class="operation-title">
<div class="operation-name">
<span class="operation-name">POST</span>
<span class="operation-path">/pets</span>
</div>
</h2>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-operation-description">
<p>Creates a new pet in the store. Duplicates are allowed</p>
</section>
</div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-request-body">
<div class="prop-row">
<div class="prop-name">
<div class="swagger-request-model">
<span class="">
<a class="json-schema-ref" href="#/definitions/NewPet">NewPet</a>
</span>
</div>
</div>
<div class="prop-value columns small-6">
<!-- <div class="swagger-request-description"> -->
<p>Pet to add to the store</p>
<!-- </div> -->
</div>
</div>
</section>
</div>
<div class="doc-examples">
<section>
<h5>Request Example</h5>
<div class="hljs"><pre><code class="lang-json">
{
&quot;<span class="hljs-attr">name</span>&quot;: <span class="hljs-string">&quot;string&quot;</span>,
&quot;<span class="hljs-attr">tag</span>&quot;: <span class="hljs-string">&quot;string&quot;</span>
}
</code></pre>
</div>
</section>
</div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-responses">
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">200 OK</div>
<div class="prop-ref"> <span class="">
<a class="json-schema-ref" href="#/definitions/Pet">Pet</a>
</span>
</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href=""></a></span> -->
</div>
<div class="prop-value">
<p>pet response</p>
</div>
</div>
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">default </div>
<div class="prop-ref"> <span class="">
<a class="json-schema-ref" href="#/definitions/Error">Error</a>
</span>
</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href=""></a></span> -->
</div>
<div class="prop-value">
<p>unexpected error</p>
</div>
</div>
</section>
</div>
<div class="doc-examples"> <section>
<h5>Response Example <span>(200 OK)</span></h5>
<div class="hljs"><pre><code class="lang-json">
{
&quot;<span class="hljs-attr">allOf</span>&quot;: [
{
&quot;<span class="hljs-attr">$ref</span>&quot;: <span class="hljs-string"><a href="#definition-NewPet">&quot;#/definitions/NewPet&quot;</a></span>
},
{
&quot;<span class="hljs-attr">properties</span>&quot;: {
&quot;<span class="hljs-attr">id</span>&quot;: {
&quot;<span class="hljs-attr">format</span>&quot;: <span class="hljs-string">&quot;int64&quot;</span>,
&quot;<span class="hljs-attr">type</span>&quot;: <span class="hljs-string">&quot;integer&quot;</span>
}
},
&quot;<span class="hljs-attr">required</span>&quot;: [
<span class="hljs-string">&quot;id&quot;</span>
]
}
],
&quot;<span class="hljs-attr">type</span>&quot;: <span class="hljs-string">&quot;object&quot;</span>
}
</code></pre>
</div>
</section>
<section>
<h5>Response Example <span>(default )</span></h5>
<div class="hljs"><pre><code class="lang-json">
{
&quot;<span class="hljs-attr">code</span>&quot;: <span class="hljs-string">&quot;integer(int32)&quot;</span>,
&quot;<span class="hljs-attr">message</span>&quot;: <span class="hljs-string">&quot;string&quot;</span>
}
</code></pre>
</div>
</section>
</div>
</div>
</div>
<span id="path--pets--id-"></span>
<div id="operation--pets--id--delete" class="operation panel" data-traverse-target="operation--pets--id--delete">
<h2 class="operation-title">
<div class="operation-name">
<span class="operation-name">DELETE</span>
<span class="operation-path">/pets/{id}</span>
</div>
</h2>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-operation-description">
<p>deletes a single pet based on the ID supplied</p>
</section>
</div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-request-params">
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">id</div>
<span class="json-property-required"></span>
</div>
<div class="prop-value">
<p class="no-description">(no description)</p>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">type</div>
<div class="prop-value"><span class="json-property-type">integer</span> <span class="json-property-format">(int64)</span>
<span class="json-property-range" title="Value limits"></span>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">in</div>
<div class="prop-value">path</div>
</div>
</section>
</div>
<div class="doc-examples"></div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-responses">
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">204 No Content</div>
</div>
<div class="prop-value">
<p>pet deleted</p>
</div>
</div>
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">default </div>
<div class="prop-ref"> <span class="">
<a class="json-schema-ref" href="#/definitions/Error">Error</a>
</span>
</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href=""></a></span> -->
</div>
<div class="prop-value">
<p>unexpected error</p>
</div>
</div>
</section>
</div>
<div class="doc-examples"> <section>
<h5>Response Example <span>(default )</span></h5>
<div class="hljs"><pre><code class="lang-json">
{
&quot;<span class="hljs-attr">code</span>&quot;: <span class="hljs-string">&quot;integer(int32)&quot;</span>,
&quot;<span class="hljs-attr">message</span>&quot;: <span class="hljs-string">&quot;string&quot;</span>
}
</code></pre>
</div>
</section>
</div>
</div>
</div>
<div id="operation--pets--id--get" class="operation panel" data-traverse-target="operation--pets--id--get">
<h2 class="operation-title">
<div class="operation-name">
<span class="operation-name">GET</span>
<span class="operation-path">/pets/{id}</span>
</div>
</h2>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-operation-description">
<p>Returns a user based on a single ID, if the user does not have access to the pet</p>
</section>
</div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-request-params">
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">id</div>
<span class="json-property-required"></span>
</div>
<div class="prop-value">
<p class="no-description">(no description)</p>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">type</div>
<div class="prop-value"><span class="json-property-type">integer</span> <span class="json-property-format">(int64)</span>
<span class="json-property-range" title="Value limits"></span>
</div>
</div>
<div class="prop-row prop-inner">
<div class="prop-name param-label">in</div>
<div class="prop-value">path</div>
</div>
</section>
</div>
<div class="doc-examples"></div>
</div>
<div class="doc-row">
<div class="doc-copy">
<section class="swagger-responses">
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">200 OK</div>
<div class="prop-ref"> <span class="">
<a class="json-schema-ref" href="#/definitions/Pet">Pet</a>
</span>
</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href=""></a></span> -->
</div>
<div class="prop-value">
<p>pet response</p>
</div>
</div>
<div class="prop-row prop-group">
<div class="prop-name">
<div class="prop-title">default </div>
<div class="prop-ref"> <span class="">
<a class="json-schema-ref" href="#/definitions/Error">Error</a>
</span>
</div>
<!-- <span class="swagger-global"></span> <span class="json-schema-reference"><a href=""></a></span> -->
</div>
<div class="prop-value">
<p>unexpected error</p>
</div>
</div>
</section>
</div>
<div class="doc-examples"> <section>
<h5>Response Example <span>(200 OK)</span></h5>
<div class="hljs"><pre><code class="lang-json">
{
&quot;<span class="hljs-attr">allOf</span>&quot;: [
{
&quot;<span class="hljs-attr">$ref</span>&quot;: <span class="hljs-string"><a href="#definition-NewPet">&quot;#/definitions/NewPet&quot;</a></span>
},
{
&quot;<span class="hljs-attr">properties</span>&quot;: {
&quot;<span class="hljs-attr">id</span>&quot;: {
&quot;<span class="hljs-attr">format</span>&quot;: <span class="hljs-string">&quot;int64&quot;</span>,
&quot;<span class="hljs-attr">type</span>&quot;: <span class="hljs-string">&quot;integer&quot;</span>
}
},
&quot;<span class="hljs-attr">required</span>&quot;: [
<span class="hljs-string">&quot;id&quot;</span>
]
}
],
&quot;<span class="hljs-attr">type</span>&quot;: <span class="hljs-string">&quot;object&quot;</span>
}
</code></pre>
</div>
</section>
<section>
<h5>Response Example <span>(default )</span></h5>
<div class="hljs"><pre><code class="lang-json">
{
&quot;<span class="hljs-attr">code</span>&quot;: <span class="hljs-string">&quot;integer(int32)&quot;</span>,
&quot;<span class="hljs-attr">message</span>&quot;: <span class="hljs-string">&quot;string&quot;</span>
}
</code></pre>
</div>
</section>
</div>
</div>
</div>
<h1>Schema definitions</h1>
<div id="definition-Error" class="panel panel-definition" data-traverse-target="definition-Error">
<h2 class="panel-title">
<a name="/definitions/Error"></a>Error:
<span class="json-property-type"><span class="json-property-type">object</span>
<span class="json-property-range" title="Value limits"></span>
</span>
</h2>
<div class="doc-row">
<div class="doc-copy">
<section class="json-schema-properties">
<dl>
<dt data-property-name="code">
<span class="json-property-name">code:</span>
<span class="json-property-type">object</span>
<span class="json-property-range" title="Value limits"></span>
<span class="json-property-required"></span>
</dt>
<dt data-property-name="message">
<span class="json-property-name">message:</span>
<span class="json-property-type">object</span>
<span class="json-property-range" title="Value limits"></span>
<span class="json-property-required"></span>
</dt>
</dl>
</section>
</div>
</div>
</div>
<div id="definition-NewPet" class="panel panel-definition" data-traverse-target="definition-NewPet">
<h2 class="panel-title">
<a name="/definitions/NewPet"></a>NewPet:
<span class="json-property-type"><span class="json-property-type">object</span>
<span class="json-property-range" title="Value limits"></span>
</span>
</h2>
<div class="doc-row">
<div class="doc-copy">
<section class="json-schema-properties">
<dl>
<dt data-property-name="name">
<span class="json-property-name">name:</span>
<span class="json-property-type">object</span>
<span class="json-property-range" title="Value limits"></span>
<span class="json-property-required"></span>
</dt>
<dt data-property-name="tag">
<span class="json-property-name">tag:</span>
<span class="json-property-type">object</span>
<span class="json-property-range" title="Value limits"></span>
</dt>
</dl>
</section>
</div>
</div>
</div>
<div id="definition-Pet" class="panel panel-definition" data-traverse-target="definition-Pet">
<h2 class="panel-title">
<a name="/definitions/Pet"></a>Pet:
<span class="json-property-type"><span class="json-property-type"></span>
<span class="json-property-range" title="Value limits"></span>
</span>
</h2>
<div class="doc-row">
<div class="doc-copy">
<section class="json-schema-allOf">
<section class="json-schema-allOf-inherited">
<ul>
<span class="">
<a class="json-schema-ref" href="#/definitions/NewPet">NewPet</a>
</span>
</ul>
</section>
<section class="json-schema-allOf-additional">
<section class="json-schema-properties">
<dl>
<dt data-property-name="id">
<span class="json-property-name">id:</span>
<span class="json-property-type">integer</span> <span class="json-property-format">(int64)</span>
<span class="json-property-range" title="Value limits"></span>
<span class="json-property-required"></span>
</dt>
</dl>
</section>
</section>
</section>
</div>
</div>
</div>
<div class="doc-row no-margin">
<div class="doc-copy doc-separator">
<a class="powered-by" href="http://sourcey.com/spectacle">Documentation by <span>Spectacle</span></a>
</div>
</div>
</article>
<div class="example-box doc-content"></div>
</div>
<div id="sidebar" class="medium-3 large-2 medium-pull-9 large-pull-10 columns">
<nav id="nav" role="navigation">
<h5>API Reference</h5>
<a href="#introduction">Introduction</a>
<h5>Paths</h5>
<!-- <section>
<a href="#path--pets">/pets</a>
<ul> -->
<!-- <li> -->
<a href="#operation--pets-get">
GET /pets
</a>
<!-- </li> -->
<!-- <li> -->
<a href="#operation--pets-post">
POST /pets
</a>
<!-- </li> -->
<!-- </ul>
</section> -->
<!-- <section>
<a href="#path--pets--id-">/pets/{id}</a>
<ul> -->
<!-- <li> -->
<a href="#operation--pets--id--get">
GET /pets/{id}
</a>
<!-- </li> -->
<!-- <li> -->
<a href="#operation--pets--id--delete">
DELETE /pets/{id}
</a>
<!-- </li> -->
<!-- </ul>
</section> -->
<h5>Schema Definitions</h5>
<a href="#definition-Error">
Error
</a>
<a href="#definition-NewPet">
NewPet
</a>
<a href="#definition-Pet">
Pet
</a>
</nav>
</div>
</div>
</body>
</html>

206
public/javascripts/main.js Normal file
View File

@ -0,0 +1,206 @@
$(function() {
var $sidebar = $('#sidebar');
var $nav = $sidebar.find('nav');
var traverse = new Traverse($nav, {
threshold: 10,
barOffset: $sidebar.position().top
});
$nav.on('update.traverse', function(event, element) {
$nav.find('section').removeClass('expand');
var $section = element.parents('section:first');
if ($section.length) {
$section.addClass('expand');
}
});
});
/**
* Creates a new instance of Traverse.
* @class
* @fires Traverse#init
* @param {Object} element - jQuery object to add the trigger to.
* @param {Object} options - Overrides to the default plugin settings.
*/
function Traverse(element, options) {
this.$element = element;
this.options = $.extend({}, Traverse.defaults, this.$element.data(), options);
this._init();
}
/**
* Default settings for plugin
*/
Traverse.defaults = {
/**
* Amount of time, in ms, the animated scrolling should take between locations.
* @option
* @example 500
*/
animationDuration: 500,
/**
* Animation style to use when scrolling between locations.
* @option
* @example 'ease-in-out'
*/
animationEasing: 'linear',
/**
* Number of pixels to use as a marker for location changes.
* @option
* @example 50
*/
threshold: 50,
/**
* Class applied to the active locations link on the traverse container.
* @option
* @example 'active'
*/
activeClass: 'active',
/**
* Allows the script to manipulate the url of the current page, and if supported, alter the history.
* @option
* @example true
*/
deepLinking: false,
/**
* Number of pixels to offset the scroll of the page on item click if using a sticky nav bar.
* @option
* @example 25
*/
barOffset: 0
};
/**
* Initializes the Traverse plugin and calls functions to get equalizer functioning on load.
* @private
*/
Traverse.prototype._init = function() {
var id = this.$element[0].id, // || Foundation.GetYoDigits(6, 'traverse'),
_this = this;
this.$targets = $('[data-traverse-target]');
this.$links = this.$element.find('a');
this.$element.attr({
'data-resize': id,
'data-scroll': id,
'id': id
});
this.$active = $();
this.scrollPos = parseInt(window.pageYOffset, 10);
this._events();
};
/**
* Calculates an array of pixel values that are the demarcation lines between locations on the page.
* Can be invoked if new elements are added or the size of a location changes.
* @function
*/
Traverse.prototype.calcPoints = function(){
var _this = this,
body = document.body,
html = document.documentElement;
this.points = [];
this.winHeight = Math.round(Math.max(window.innerHeight, html.clientHeight));
this.docHeight = Math.round(Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight));
this.$targets.each(function(){
var $tar = $(this),
pt = $tar.offset().top; // Math.round($tar.offset().top - _this.options.threshold);
$tar.targetPoint = pt;
_this.points.push(pt);
});
};
/**
* Initializes events for Traverse.
* @private
*/
Traverse.prototype._events = function() {
var _this = this,
$body = $('html, body'),
opts = {
duration: _this.options.animationDuration,
easing: _this.options.animationEasing
};
$(window).one('load', function(){
_this.calcPoints();
_this._updateActive();
$(this).resize(function(e) {
_this.reflow();
}).scroll(function(e) {
_this._updateActive();
});
})
this.$element.on('click', 'a[href^="#"]', function(e) { //'click.zf.traverse'
e.preventDefault();
var arrival = this.getAttribute('href'),
scrollPos = $(arrival).offset().top - _this.options.barOffset; // - _this.options.threshold / 2 - _this.options.barOffset;
$body.stop(true).animate({
scrollTop: scrollPos
}, opts);
});
};
/**
* Calls necessary functions to update Traverse upon DOM change
* @function
*/
Traverse.prototype.reflow = function(){
this.calcPoints();
this._updateActive();
};
/**
* Updates the visibility of an active location link,
* and updates the url hash for the page, if deepLinking enabled.
* @private
* @function
* @fires Traverse#update
*/
Traverse.prototype._updateActive = function(){
var winPos = parseInt(window.pageYOffset, 10),
curIdx;
if(winPos + this.winHeight === this.docHeight){ curIdx = this.points.length - 1; }
else if(winPos < this.points[0]){ curIdx = 0; }
else{
var isDown = this.scrollPos < winPos,
_this = this,
curVisible = this.points.filter(function(p, i){
return isDown ?
p <= (winPos + _this.options.barOffset + _this.options.threshold) :
(p - (_this.options.barOffset + _this.options.threshold)) <= winPos;
// p <= (winPos - (offset - _this.options.threshold)) :
// (p - (-offset + _this.options.threshold)) <= winPos;
});
curIdx = curVisible.length ? curVisible.length - 1 : 0;
}
var $prev = this.$active;
var $next = this.$links.eq(curIdx);
this.$active.removeClass(this.options.activeClass);
this.$active = $next.addClass(this.options.activeClass);
if(this.options.deepLinking){
var hash = this.$active[0].getAttribute('href');
if(window.history.pushState){
window.history.pushState(null, null, hash);
}else{
window.location.hash = hash;
}
}
this.scrollPos = winPos;
// Fire event if the active element was changed
var changed = $prev[0] !== $next[0];
if (changed) {
this.$element.trigger('update.traverse', [this.$active]);
}
};

1
public/javascripts/main.min.js vendored Normal file
View File

@ -0,0 +1 @@
function Traverse(a,b){this.$element=a,this.options=$.extend({},Traverse.defaults,this.$element.data(),b),this._init()}$(function(){var a=$("#sidebar"),b=a.find("nav");new Traverse(b,{threshold:10,barOffset:a.position().top});b.on("update.traverse",function(a,c){b.find("section").removeClass("expand");var d=c.parents("section:first");d.length&&d.addClass("expand")})}),Traverse.defaults={animationDuration:500,animationEasing:"linear",threshold:50,activeClass:"active",deepLinking:!1,barOffset:0},Traverse.prototype._init=function(){var a=this.$element[0].id;this.$targets=$("[data-traverse-target]"),this.$links=this.$element.find("a"),this.$element.attr({"data-resize":a,"data-scroll":a,id:a}),this.$active=$(),this.scrollPos=parseInt(window.pageYOffset,10),this._events()},Traverse.prototype.calcPoints=function(){var a=this,b=document.body,c=document.documentElement;this.points=[],this.winHeight=Math.round(Math.max(window.innerHeight,c.clientHeight)),this.docHeight=Math.round(Math.max(b.scrollHeight,b.offsetHeight,c.clientHeight,c.scrollHeight,c.offsetHeight)),this.$targets.each(function(){var b=$(this),c=b.offset().top;b.targetPoint=c,a.points.push(c)})},Traverse.prototype._events=function(){var a=this,b=$("html, body"),c={duration:a.options.animationDuration,easing:a.options.animationEasing};$(window).one("load",function(){a.calcPoints(),a._updateActive(),$(this).resize(function(b){a.reflow()}).scroll(function(b){a._updateActive()})}),this.$element.on("click",'a[href^="#"]',function(d){d.preventDefault();var e=this.getAttribute("href"),f=$(e).offset().top-a.options.barOffset;b.stop(!0).animate({scrollTop:f},c)})},Traverse.prototype.reflow=function(){this.calcPoints(),this._updateActive()},Traverse.prototype._updateActive=function(){var a,b=parseInt(window.pageYOffset,10);if(b+this.winHeight===this.docHeight)a=this.points.length-1;else if(b<this.points[0])a=0;else{var c=this.scrollPos<b,d=this,e=this.points.filter(function(a,e){return c?a<=b+d.options.barOffset+d.options.threshold:a-(d.options.barOffset+d.options.threshold)<=b});a=e.length?e.length-1:0}var f=this.$active,g=this.$links.eq(a);if(this.$active.removeClass(this.options.activeClass),this.$active=g.addClass(this.options.activeClass),this.options.deepLinking){var h=this.$active[0].getAttribute("href");window.history.pushState?window.history.pushState(null,null,h):window.location.hash=h}this.scrollPos=b;var i=f[0]!==g[0];i&&this.$element.trigger("update.traverse",[this.$active])};

File diff suppressed because one or more lines are too long

1
public/stylesheets/main.min.css vendored Normal file

File diff suppressed because one or more lines are too long

681
test/fixtures/petstore.json vendored Normal file
View File

@ -0,0 +1,681 @@
{
"swagger":"2.0",
"info":{
"description":"This is a sample server Petstore server. You can find out more about Swagger at <a href=\"http://swagger.wordnik.com\">http://swagger.wordnik.com</a> or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters",
"version":"1.0.0",
"title":"Swagger Sample App",
"termsOfService":"http://helloreverb.com/terms/",
"contact":{
"name":"apiteam@wordnik.com"
},
"license":{
"name":"Apache 2.0",
"url":"http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"basePath":"/v2",
"paths":{
"/user/createWithList":{
"post":{
"tags":[
"user"
],
"summary":"Creates list of users with given input array",
"description":"",
"operationId":"createUsersWithListInput",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"body",
"name":"body",
"description":"List of user object",
"required":false,
"schema":{
"$ref":"#/definitions/User"
}
}
],
"responses":{
"default":{
"description":"successful operation"
}
}
}
},
"/pet":{
"put":{
"tags":[
"pet"
],
"summary":"Update an existing pet",
"description":"",
"operationId":"updatePet",
"consumes":[
"application/json",
"application/xml"
],
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"body",
"name":"body",
"description":"Pet object that needs to be added to the store",
"required":false,
"schema":{
"$ref":"#/definitions/Pet"
}
}
],
"responses":{
"405":{
"description":"Validation exception"
},
"404":{
"description":"Pet not found"
},
"400":{
"description":"Invalid ID supplied"
}
}
},
"post":{
"tags":[
"pet"
],
"summary":"Add a new pet to the store",
"description":"",
"operationId":"addPet",
"consumes":[
"application/json",
"application/xml"
],
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"body",
"name":"body",
"description":"Pet object that needs to be added to the store",
"required":false,
"schema":{
"$ref":"#/definitions/Pet"
}
}
],
"responses":{
"405":{
"description":"Invalid input"
}
}
}
},
"/store/order/{orderId}":{
"get":{
"tags":[
"store"
],
"summary":"Find purchase order by ID",
"description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions",
"operationId":"getOrderById",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"path",
"name":"orderId",
"description":"ID of pet that needs to be fetched",
"required":true,
"type":"string"
}
],
"responses":{
"404":{
"description":"Order not found"
},
"200":{
"description":"successful operation",
"schema":{
"$ref":"#/definitions/Order"
}
},
"400":{
"description":"Invalid ID supplied"
}
}
},
"delete":{
"tags":[
"store"
],
"summary":"Delete purchase order by ID",
"description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
"operationId":"deleteOrder",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"path",
"name":"orderId",
"description":"ID of the order that needs to be deleted",
"required":true,
"type":"string"
}
],
"responses":{
"404":{
"description":"Order not found"
},
"400":{
"description":"Invalid ID supplied"
}
}
}
},
"/user/createWithArray":{
"post":{
"tags":[
"user"
],
"summary":"Creates list of users with given input array",
"description":"",
"operationId":"createUsersWithArrayInput",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"body",
"name":"body",
"description":"List of user object",
"required":false
}
],
"responses":{
"default":{
"description":"successful operation"
}
}
}
},
"/store/order":{
"post":{
"tags":[
"store"
],
"summary":"Place an order for a pet",
"description":"",
"operationId":"placeOrder",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"body",
"name":"body",
"description":"order placed for purchasing the pet",
"required":false,
"schema":{
"$ref":"#/definitions/Order"
}
}
],
"responses":{
"200":{
"description":"successful operation",
"schema":{
"$ref":"#/definitions/Order"
}
},
"400":{
"description":"Invalid Order"
}
}
}
},
"/pet/findByStatus":{
"get":{
"tags":[
"pet"
],
"summary":"Finds Pets by status",
"description":"Multiple status values can be provided with comma seperated strings",
"operationId":"findPetsByStatus",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"query",
"name":"status",
"description":"Status values that need to be considered for filter",
"required":false,
"type":"array",
"items":{
"type":"string"
},
"collectionFormat":"jaxrs"
}
],
"responses":{
"200":{
"description":"successful operation",
"schema":{
"$ref":"#/definitions/Pet"
}
},
"400":{
"description":"Invalid status value"
}
}
}
},
"/user/{username}":{
"get":{
"tags":[
"user"
],
"summary":"Get user by user name",
"description":"",
"operationId":"getUserByName",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"path",
"name":"username",
"description":"The name that needs to be fetched. Use user1 for testing. ",
"required":true,
"type":"string"
}
],
"responses":{
"404":{
"description":"User not found"
},
"200":{
"description":"successful operation",
"schema":{
"$ref":"#/definitions/User"
}
},
"400":{
"description":"Invalid username supplied"
}
}
},
"put":{
"tags":[
"user"
],
"summary":"Updated user",
"description":"This can only be done by the logged in user.",
"operationId":"updateUser",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"path",
"name":"username",
"description":"name that need to be deleted",
"required":true,
"type":"string"
},
{
"in":"body",
"name":"body",
"description":"Updated user object",
"required":false,
"schema":{
"$ref":"#/definitions/User"
}
}
],
"responses":{
"404":{
"description":"User not found"
},
"400":{
"description":"Invalid user supplied"
}
}
},
"delete":{
"tags":[
"user"
],
"summary":"Delete user",
"description":"This can only be done by the logged in user.",
"operationId":"deleteUser",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"path",
"name":"username",
"description":"The name that needs to be deleted",
"required":true,
"type":"string"
}
],
"responses":{
"404":{
"description":"User not found"
},
"400":{
"description":"Invalid username supplied"
}
}
}
},
"/pet/findByTags":{
"get":{
"tags":[
"pet"
],
"summary":"Finds Pets by tags",
"description":"Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.",
"operationId":"findPetsByTags",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"query",
"name":"tags",
"description":"Tags to filter by",
"required":false,
"type":"array",
"items":{
"type":"string"
},
"collectionFormat":"jaxrs"
}
],
"responses":{
"200":{
"description":"successful operation",
"schema":{
"$ref":"#/definitions/Pet"
}
},
"400":{
"description":"Invalid tag value"
}
}
}
},
"/user":{
"post":{
"tags":[
"user"
],
"summary":"Create user",
"description":"This can only be done by the logged in user.",
"operationId":"createUser",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"body",
"name":"body",
"description":"Created user object",
"required":false,
"schema":{
"$ref":"#/definitions/User"
}
}
],
"responses":{
"default":{
"description":"successful operation"
}
}
}
},
"/user/logout":{
"get":{
"tags":[
"user"
],
"summary":"Logs out current logged in user session",
"description":"",
"operationId":"logoutUser",
"produces":[
"application/json",
"application/xml"
],
"responses":{
"default":{
"description":"successful operation"
}
}
}
},
"/user/login":{
"get":{
"tags":[
"user"
],
"summary":"Logs user into the system",
"description":"",
"operationId":"loginUser",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"query",
"name":"username",
"description":"The user name for login",
"required":false,
"type":"string"
},
{
"in":"query",
"name":"password",
"description":"The password for login in clear text",
"required":false,
"type":"string"
}
],
"responses":{
"200":{
"description":"successful operation",
"schema":{
"type":"string"
}
},
"400":{
"description":"Invalid username/password supplied"
}
}
}
},
"/pet/{petId}":{
"get":{
"tags":[
"pet"
],
"summary":"Find pet by ID",
"description":"Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions",
"operationId":"getPetById",
"produces":[
"application/json",
"application/xml"
],
"parameters":[
{
"in":"path",
"name":"petId",
"description":"ID of pet that needs to be fetched",
"required":true,
"type":"integer",
"format":"int64"
}
],
"responses":{
"404":{
"description":"Pet not found"
},
"200":{
"description":"successful operation",
"schema":{
"$ref":"#/definitions/Pet"
}
},
"400":{
"description":"Invalid ID supplied"
}
}
}
}
},
"definitions":{
"User":{
"properties":{
"phone":{
"type":"string"
},
"password":{
"type":"string"
},
"email":{
"type":"string"
},
"lastName":{
"type":"string"
},
"firstName":{
"type":"string"
},
"username":{
"type":"string"
},
"id":{
"type":"integer",
"format":"int64"
},
"userStatus":{
"type":"integer",
"format":"int32",
"position":0
}
}
},
"Category":{
"properties":{
"name":{
"type":"string"
},
"id":{
"type":"integer",
"format":"int64"
}
}
},
"Pet":{
"enum":[
"photoUrls",
"name"
],
"properties":{
"tags":{
"type":"array",
"items":{
"$ref":"Tag"
}
},
"photoUrls":{
"type":"array",
"items":{
"type":"string"
}
},
"category":{
"$ref":"Category"
},
"id":{
"type":"integer",
"format":"int64"
},
"name":{
"type":"string",
"example":"doggie",
"position":0
},
"status":{
"type":"string",
"position":0
}
}
},
"Tag":{
"properties":{
"name":{
"type":"string"
},
"id":{
"type":"integer",
"format":"int64"
}
}
},
"Order":{
"properties":{
"complete":{
"$ref":"boolean"
},
"shipDate":{
"type":"string",
"format":"date-time"
},
"quantity":{
"type":"integer",
"format":"int32"
},
"petId":{
"type":"integer",
"format":"int64"
},
"id":{
"type":"integer",
"format":"int64"
},
"status":{
"type":"string",
"position":0
}
}
},
"boolean":{
"properties":{
}
}
}
}

2469
test/fixtures/tommy.json vendored Normal file

File diff suppressed because it is too large Load Diff