Merge pull request #1095 from swagger-api/develop_2.0

merge from develop 2.0 for release
This commit is contained in:
Tony Tam
2015-03-28 19:30:19 -07:00
93 changed files with 25809 additions and 15796 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
* text eol=lf
dist/**/* binary

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@ swagger-ui.sublime-workspace
.idea
.project
node_modules/*
/nbproject/private/

5
.jshintignore Normal file
View File

@@ -0,0 +1,5 @@
node_modules
src/main/javascript/doc.js
dist
lib
.log

39
.jshintrc Normal file
View File

@@ -0,0 +1,39 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": false,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
"validthis": true,
"globals": {
// Libraries
"_": false,
"$": false,
"Backbone": false,
"Handlebars": false,
"jQuery": false,
"marked": false,
"SwaggerClient": false,
"hljs": false,
"SwaggerUi": false,
"define": false,
// Global object
// TODO: remove these
"Docs": false
}
}

View File

@@ -1,7 +1,10 @@
sudo: false
language: node_js
node_js:
- '0.10'
- '0.12'
install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install
- npm i -g jshint
- npm install

View File

@@ -30,6 +30,9 @@ Swagger UI Version | Release Date | Swagger Spec compatibility | Notes | Status
### Download
You can use the swagger-ui code AS-IS! No need to build or recompile--just clone this repo and use the pre-built files in the `dist` folder. If you like swagger-ui as-is, stop here.
##### Browser support
Swagger UI works in all evergreen desktop browsers (Chrome, Safari, Firefox). Internet Explorer support is version 8 (IE8) and above.
### Build
You can rebuild swagger-ui on your own to tweak it or just so you can say you did. To do so, follow these steps:
@@ -57,24 +60,24 @@ Once you open the Swagger UI, it will load the [Swagger Petstore](http://petstor
You may choose to customize Swagger UI for your organization. Here is an overview of whats in its various directories:
- dist: Contains a distribution which you can deploy on a server or load from your local machine.
- dist/lang: The swagger localisation
- lib: Contains javascript dependencies which swagger-ui depends on
- node_modules: Contains node modules which swagger-ui uses for its development.
- src
- src/main/coffeescript: main code in CoffeeScript
- src/main/templates: [handlebars](http://handlebarsjs.com/) templates used to render swagger-ui
- src/main/html: the html files, some images and css
- src/main/javascript: some legacy javascript referenced by CoffeeScript code
- src/main/javascript: main code
### SwaggerUi
To use swagger-ui you should take a look at the [source of swagger-ui html page](https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html) and customize it. This basically requires you to instantiate a SwaggerUi object and call load() on it as below:
```javascript
window.swaggerUi = new SwaggerUi({
var swaggerUi = new SwaggerUi({
url:"http://petstore.swagger.io/v2/swagger.json",
dom_id:"swagger-ui-container"
});
window.swaggerUi.load();
swaggerUi.load();
```
##### Parameters
@@ -87,11 +90,13 @@ validatorUrl | By default, Swagger-UI attempts to validate specs against swagger
dom_id | The id of a dom element inside which SwaggerUi will put the user interface for swagger.
booleanValues | SwaggerUI renders boolean data types as a dropdown. By default it provides a 'true' and 'false' string as the possible choices. You can use this parameter to change the values in dropdown to be something else, for example 0 and 1 by setting booleanValues to new Array(0, 1).
docExpansion | Controls how the API listing is displayed. It can be set to 'none' (default), 'list' (shows operations for each resource), or 'full' (fully expanded: shows operations and their details).
sorter | Apply a sort to the API list. It can be 'alpha' (sort paths alphanumerically) or 'method' (sort operations by HTTP method). Default is the order returned by the server unchanged.
apisSorter | Apply a sort to the API/tags list. It can be 'alpha' (sort by name) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
operationsSorter | Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged.
onComplete | This is a callback function parameter which can be passed to be notified of when SwaggerUI has completed rendering successfully.
onFailure | This is a callback function parameter which can be passed to be notified of when SwaggerUI encountered a failure was unable to render.
highlightSizeThreshold | Any size response below this threshold will be highlighted syntactically, attempting to highlight large responses can lead to browser hangs, not including a threshold will default to highlight all returned responses.
supportedSubmitMethods | An array of of the HTTP operations that will have the 'Try it out!` option. An empty array disables all operations. This does not filter the operations from the display.
oauth2RedirectUrl | OAuth redirect URL
* All other parameters are explained in greater detail below
@@ -104,11 +109,11 @@ swagger-ui supports invocation of all HTTP methods APIs including GET, PUT, POST
Header params are supported through a pluggable mechanism in [swagger-js](https://github.com/swagger-api/swagger-js). You can see the [index.html](https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html) for a sample of how to dynamically set headers:
```js
// add a new ApiKeyAuthorization when the api-key changes in the ui.
// add a new SwaggerClient.ApiKeyAuthorization when the api-key changes in the ui.
$('#input_apiKey').change(function() {
var key = $('#input_apiKey')[0].value;
if(key && key.trim() != "") {
window.authorizations.add("key", new ApiKeyAuthorization("api_key", key, "header"));
swaggerUi.api.clientAuthorizations.add("key", new SwaggerClient.ApiKeyAuthorization("api_key", key, "header"));
}
})
```
@@ -119,11 +124,36 @@ This will add header `api_key` with value `key` on every call to the server. Yo
If you have some header parameters which you need to send with every request, use the headers as below:
```js
window.authorizations.add("key", new ApiKeyAuthorization("Authorization", "XXXX", "header"));
swaggerUi.api.clientAuthorizations.add("key", new SwaggerClient.ApiKeyAuthorization("Authorization", "XXXX", "header"));
```
Note! You can pass multiple header params on a single request, just use unique names for them (`key` is used in the above example).
### Localization and translation
The localization files are in the dist/lang directory.
To enable translation you should append next two lines in your Swagger's index.html (or another entry point you use)
```html
<script src='lang/translator.js' type='text/javascript'></script>
<script src='lang/en.js' type='text/javascript'></script>
```
The first line script is a translator and the second one is your language lexemes.
If you wish to append support for new language you just need to create lang/your_lang.js and fill it like it's done in existing files.
To append new lexemex for translation you shoul do two things:
1. Add lexeme into the language file.
Example of new line: "new sentence":"translation of new sentence".
2. Mark this lexeme in source html with attribute data-sw-translate.
Example of changed source:
```html
<anyHtmlTag data-sw-translate>new sentence</anyHtmlTag>
or <anyHtmlTag data-sw-translate value='new sentence'/>
```
.
At this moment only inner html, title-attribute and value-attribute are going to be translated.
## CORS Support
CORS is a technique to prevent websites from doing bad things with your personal data. Most browsers + javascript toolkits not only support CORS but enforce it, which has implications for your API server which supports Swagger.
@@ -191,7 +221,8 @@ Create your own fork of [swagger-api/swagger-ui](https://github.com/swagger-api/
To share your changes, [submit a pull request](https://github.com/swagger-api/swagger-ui/pull/new/master).
Since the javascript files are compiled from coffeescript, please submit changes in the *.coffee files! We have to reject changes only in the .js files as they will be lost on each build of the ui.
## Change Log
Plsee see [releases](https://github.com/swagger-api/swagger-ui/releases) for change log.
## License

26
bower.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "swagger-ui",
"main": "dist/index.html",
"version": "2.1.8-M1",
"authors": [
"Mohsen Azimi <me@azimi.me>"
],
"description": "Swagger UI",
"moduleType": [
"globals"
],
"keywords": [
"Swagger",
"API"
],
"license": "Copyright 2015 Reverb Technologies, Inc.",
"homepage": "http://swagger.io",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

1155
dist/css/print.css vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
dist/images/favicon-16x16.png vendored Executable file

Binary file not shown.

BIN
dist/images/favicon-32x32.png vendored Executable file

Binary file not shown.

BIN
dist/images/favicon.ico vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 770 B

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 824 B

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

After

Width:  |  Height:  |  Size: 979 B

199
dist/index.html vendored
View File

@@ -1,98 +1,101 @@
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='print' rel='stylesheet' type='text/css'/>
<script type="text/javascript" src="lib/shred.bundle.js"></script>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='lib/swagger-client.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script src='lib/marked.js' type='text/javascript'></script>
<!-- enabling this will enable oauth2 implicit scope support -->
<script src='lib/swagger-oauth.js' type='text/javascript'></script>
<script type="text/javascript">
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "http://petstore.swagger.io/v2/swagger.json";
}
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
/*
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
*/
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
sorter : "alpha"
});
function addApiKeyAuthorization() {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if(key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("api_key", new ApiKeyAuthorization("api_key", key, "query"));
}
}
$('#input_apiKey').change(function() {
addApiKeyAuthorization();
});
// if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
/*
var apiKey = "myApiKeyXXXX123456789";
$('#input_apiKey').val(apiKey);
addApiKeyAuthorization();
*/
window.swaggerUi.load();
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script src='lib/marked.js' type='text/javascript'></script>
<script type="text/javascript">
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "http://petstore.swagger.io/v2/swagger.json";
}
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
/*
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
*/
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
sorter : "alpha"
});
function addApiKeyAuthorization() {
var key = encodeURIComponent($('#input_apiKey')[0].value);
log("key: " + key);
if(key && key.trim() != "") {
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("api_key", key, "query");
window.swaggerUi.api.clientAuthorizations.add("api_key", apiKeyAuth);
log("added key " + key);
}
}
$('#input_apiKey').change(addApiKeyAuthorization);
// if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
/*
var apiKey = "myApiKeyXXXX123456789";
$('#input_apiKey').val(apiKey);
addApiKeyAuthorization();
*/
window.swaggerUi.load();
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

2765
dist/lib/shred.bundle.js vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,193 +0,0 @@
// The purpose of the `Content` object is to abstract away the data conversions
// to and from raw content entities as strings. For example, you want to be able
// to pass in a Javascript object and have it be automatically converted into a
// JSON string if the `content-type` is set to a JSON-based media type.
// Conversely, you want to be able to transparently get back a Javascript object
// in the response if the `content-type` is a JSON-based media-type.
// One limitation of the current implementation is that it [assumes the `charset` is UTF-8](https://github.com/spire-io/shred/issues/5).
// The `Content` constructor takes an options object, which *must* have either a
// `body` or `data` property and *may* have a `type` property indicating the
// media type. If there is no `type` attribute, a default will be inferred.
var Content = function(options) {
this.body = options.body;
this.data = options.data;
this.type = options.type;
};
Content.prototype = {
// Treat `toString()` as asking for the `content.body`. That is, the raw content entity.
//
// toString: function() { return this.body; }
//
// Commented out, but I've forgotten why. :/
};
// `Content` objects have the following attributes:
Object.defineProperties(Content.prototype,{
// - **type**. Typically accessed as `content.type`, reflects the `content-type`
// header associated with the request or response. If not passed as an options
// to the constructor or set explicitly, it will infer the type the `data`
// attribute, if possible, and, failing that, will default to `text/plain`.
type: {
get: function() {
if (this._type) {
return this._type;
} else {
if (this._data) {
switch(typeof this._data) {
case "string": return "text/plain";
case "object": return "application/json";
}
}
}
return "text/plain";
},
set: function(value) {
this._type = value;
return this;
},
enumerable: true
},
// - **data**. Typically accessed as `content.data`, reflects the content entity
// converted into Javascript data. This can be a string, if the `type` is, say,
// `text/plain`, but can also be a Javascript object. The conversion applied is
// based on the `processor` attribute. The `data` attribute can also be set
// directly, in which case the conversion will be done the other way, to infer
// the `body` attribute.
data: {
get: function() {
if (this._body) {
return this.processor.parser(this._body);
} else {
return this._data;
}
},
set: function(data) {
if (this._body&&data) Errors.setDataWithBody(this);
this._data = data;
return this;
},
enumerable: true
},
// - **body**. Typically accessed as `content.body`, reflects the content entity
// as a UTF-8 string. It is the mirror of the `data` attribute. If you set the
// `data` attribute, the `body` attribute will be inferred and vice-versa. If
// you attempt to set both, an exception is raised.
body: {
get: function() {
if (this._data) {
return this.processor.stringify(this._data);
} else {
return this._body.toString();
}
},
set: function(body) {
if (this._data&&body) Errors.setBodyWithData(this);
this._body = body;
return this;
},
enumerable: true
},
// - **processor**. The functions that will be used to convert to/from `data` and
// `body` attributes. You can add processors. The two that are built-in are for
// `text/plain`, which is basically an identity transformation and
// `application/json` and other JSON-based media types (including custom media
// types with `+json`). You can add your own processors. See below.
processor: {
get: function() {
var processor = Content.processors[this.type];
if (processor) {
return processor;
} else {
// Return the first processor that matches any part of the
// content type. ex: application/vnd.foobar.baz+json will match json.
var main = this.type.split(";")[0];
var parts = main.split(/\+|\//);
for (var i=0, l=parts.length; i < l; i++) {
processor = Content.processors[parts[i]]
}
return processor || {parser:identity,stringify:toString};
}
},
enumerable: true
},
// - **length**. Typically accessed as `content.length`, returns the length in
// bytes of the raw content entity.
length: {
get: function() {
if (typeof Buffer !== 'undefined') {
return Buffer.byteLength(this.body);
}
return this.body.length;
}
}
});
Content.processors = {};
// The `registerProcessor` function allows you to add your own processors to
// convert content entities. Each processor consists of a Javascript object with
// two properties:
// - **parser**. The function used to parse a raw content entity and convert it
// into a Javascript data type.
// - **stringify**. The function used to convert a Javascript data type into a
// raw content entity.
Content.registerProcessor = function(types,processor) {
// You can pass an array of types that will trigger this processor, or just one.
// We determine the array via duck-typing here.
if (types.forEach) {
types.forEach(function(type) {
Content.processors[type] = processor;
});
} else {
// If you didn't pass an array, we just use what you pass in.
Content.processors[types] = processor;
}
};
// Register the identity processor, which is used for text-based media types.
var identity = function(x) { return x; }
, toString = function(x) { return x.toString(); }
Content.registerProcessor(
["text/html","text/plain","text"],
{ parser: identity, stringify: toString });
// Register the JSON processor, which is used for JSON-based media types.
Content.registerProcessor(
["application/json; charset=utf-8","application/json","json"],
{
parser: function(string) {
return JSON.parse(string);
},
stringify: function(data) {
return JSON.stringify(data); }});
var qs = require('querystring');
// Register the post processor, which is used for JSON-based media types.
Content.registerProcessor(
["application/x-www-form-urlencoded"],
{ parser : qs.parse, stringify : qs.stringify });
// Error functions are defined separately here in an attempt to make the code
// easier to read.
var Errors = {
setDataWithBody: function(object) {
throw new Error("Attempt to set data attribute of a content object " +
"when the body attributes was already set.");
},
setBodyWithData: function(object) {
throw new Error("Attempt to set body attribute of a content object " +
"when the data attributes was already set.");
}
}
module.exports = Content;

File diff suppressed because it is too large Load Diff

View File

@@ -97,18 +97,19 @@ function handleLogin() {
var authSchemes = window.swaggerUi.api.authSchemes;
var host = window.location;
var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
var redirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
var url = null;
for (var key in authSchemes) {
if (authSchemes.hasOwnProperty(key)) {
var flow = authSchemes[key].flow;
if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
var dets = authSchemes[key];
url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
window.swaggerUi.tokenName = dets.tokenName || 'access_token';
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
}
else if(authSchemes[key].grantTypes) {
// 1.2 support
@@ -135,11 +136,14 @@ function handleLogin() {
for(k =0; k < o.length; k++) {
var scope = $(o[k]).attr('scope');
if (scopes.indexOf(scope) === -1)
scopes.push(scope);
}
// Implicit auth recommends a state parameter.
var state = Math.random ();
window.enabledScopes=scopes;
redirect_uri = redirectUrl;
@@ -147,7 +151,8 @@ function handleLogin() {
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes);
url += '&scope=' + encodeURIComponent(scopes.join(' '));
url += '&state=' + encodeURIComponent(state);
window.open(url);
});
@@ -198,7 +203,7 @@ function initOAuth(opts) {
});
}
function processOAuthCode(data) {
window.processOAuthCode = function processOAuthCode(data) {
var params = {
'client_id': clientId,
'code': data.code,
@@ -210,18 +215,18 @@ function processOAuthCode(data) {
url : window.swaggerUi.tokenUrl,
type: "POST",
data: params,
success:function(data, textStatus, jqXHR)
success:function(data, textStatus, jqXHR)
{
onOAuthComplete(data);
},
error: function(jqXHR, textStatus, errorThrown)
error: function(jqXHR, textStatus, errorThrown)
{
onOAuthComplete("");
}
});
}
function onOAuthComplete(token) {
window.onOAuthComplete = function onOAuthComplete(token) {
if(token) {
if(token.error) {
var checkbox = $('input[type=checkbox],.secured')
@@ -268,7 +273,7 @@ function onOAuthComplete(token) {
// all scopes are satisfied
$(o).find('.api-ic').addClass('ic-info');
$(o).find('.api-ic').removeClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
$(o).find('.api-ic').removeClass('ic-error');
}
}
});

1
dist/lib/underscore-min.map vendored Normal file

File diff suppressed because one or more lines are too long

22887
dist/swagger-ui.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,9 +2,7 @@
var gulp = require('gulp');
var es = require('event-stream');
var gutil = require('gulp-util');
var clean = require('gulp-clean');
var coffee = require('gulp-coffee');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
@@ -16,6 +14,7 @@ var watch = require('gulp-watch');
var connect = require('gulp-connect');
var header = require('gulp-header');
var pkg = require('./package.json');
var order = require('gulp-order');
var banner = ['/**',
' * <%= pkg.name %> - <%= pkg.description %>',
' * @version v<%= pkg.version %>',
@@ -31,7 +30,7 @@ gulp.task('clean', function() {
return gulp
.src('./dist', {read: false})
.pipe(clean({force: true}))
.on('error', gutil.log);
.on('error', log);
});
/**
@@ -46,17 +45,7 @@ function templates() {
namespace: 'Handlebars.templates',
noRedeclare: true, // Avoid duplicate declarations
}))
.on('error', gutil.log);
}
/**
* Processes CoffeeScript files
*/
function coffeescript () {
return gulp
.src(['./src/main/coffeescript/**/*.coffee'])
.pipe(coffee({bare: true}))
.on('error', gutil.log);
.on('error', log);
}
/**
@@ -65,16 +54,21 @@ function coffeescript () {
gulp.task('dist', ['clean'], function() {
return es.merge(
gulp.src('./src/main/javascript/doc.js'),
coffeescript(),
gulp.src([
'./src/main/javascript/**/*.js',
'./node_modules/swagger-client/browser/swagger-client.js'
]),
templates()
)
.pipe(order(['scripts.js', 'templates.js']))
.pipe(concat('swagger-ui.js'))
.pipe(wrap('(function(){<%= contents %>}).call(this);'))
.pipe(header(banner, { pkg: pkg } ))
.pipe(gulp.dest('./dist'))
.pipe(uglify())
.on('error', log)
.pipe(rename({extname: '.min.js'}))
.on('error', gutil.log)
.on('error', log)
.pipe(gulp.dest('./dist'))
.pipe(connect.reload());
});
@@ -87,10 +81,11 @@ gulp.task('less', ['clean'], function() {
return gulp
.src([
'./src/main/less/screen.less',
'./src/main/less/print.less',
'./src/main/less/reset.less'
])
.pipe(less())
.on('error', gutil.log)
.on('error', log)
.pipe(gulp.dest('./src/main/html/css/'))
.pipe(connect.reload());
});
@@ -103,22 +98,22 @@ gulp.task('copy', ['less'], function() {
// copy JavaScript files inside lib folder
gulp
.src(['./lib/**/*.js'])
.src(['./lib/**/*.{js,map}'])
.pipe(gulp.dest('./dist/lib'))
.on('error', gutil.log)
.on('error', log);
// copy all files inside html folder
gulp
.src(['./src/main/html/**/*'])
.pipe(gulp.dest('./dist'))
.on('error', gutil.log)
.on('error', log);
});
/**
* Watch for changes and recompile
*/
gulp.task('watch', function() {
return watch(['./src/**/*.{coffee,js,less}'], function() {
return watch(['./src/**/*.{js,less,handlebars}'], function() {
gulp.start('default');
});
});
@@ -133,6 +128,10 @@ gulp.task('connect', function() {
});
});
function log(error) {
console.error(error.toString && error.toString());
}
gulp.task('default', ['dist', 'copy']);
gulp.task('serve', ['connect', 'watch'])
gulp.task('serve', ['connect', 'watch']);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1,193 +0,0 @@
// The purpose of the `Content` object is to abstract away the data conversions
// to and from raw content entities as strings. For example, you want to be able
// to pass in a Javascript object and have it be automatically converted into a
// JSON string if the `content-type` is set to a JSON-based media type.
// Conversely, you want to be able to transparently get back a Javascript object
// in the response if the `content-type` is a JSON-based media-type.
// One limitation of the current implementation is that it [assumes the `charset` is UTF-8](https://github.com/spire-io/shred/issues/5).
// The `Content` constructor takes an options object, which *must* have either a
// `body` or `data` property and *may* have a `type` property indicating the
// media type. If there is no `type` attribute, a default will be inferred.
var Content = function(options) {
this.body = options.body;
this.data = options.data;
this.type = options.type;
};
Content.prototype = {
// Treat `toString()` as asking for the `content.body`. That is, the raw content entity.
//
// toString: function() { return this.body; }
//
// Commented out, but I've forgotten why. :/
};
// `Content` objects have the following attributes:
Object.defineProperties(Content.prototype,{
// - **type**. Typically accessed as `content.type`, reflects the `content-type`
// header associated with the request or response. If not passed as an options
// to the constructor or set explicitly, it will infer the type the `data`
// attribute, if possible, and, failing that, will default to `text/plain`.
type: {
get: function() {
if (this._type) {
return this._type;
} else {
if (this._data) {
switch(typeof this._data) {
case "string": return "text/plain";
case "object": return "application/json";
}
}
}
return "text/plain";
},
set: function(value) {
this._type = value;
return this;
},
enumerable: true
},
// - **data**. Typically accessed as `content.data`, reflects the content entity
// converted into Javascript data. This can be a string, if the `type` is, say,
// `text/plain`, but can also be a Javascript object. The conversion applied is
// based on the `processor` attribute. The `data` attribute can also be set
// directly, in which case the conversion will be done the other way, to infer
// the `body` attribute.
data: {
get: function() {
if (this._body) {
return this.processor.parser(this._body);
} else {
return this._data;
}
},
set: function(data) {
if (this._body&&data) Errors.setDataWithBody(this);
this._data = data;
return this;
},
enumerable: true
},
// - **body**. Typically accessed as `content.body`, reflects the content entity
// as a UTF-8 string. It is the mirror of the `data` attribute. If you set the
// `data` attribute, the `body` attribute will be inferred and vice-versa. If
// you attempt to set both, an exception is raised.
body: {
get: function() {
if (this._data) {
return this.processor.stringify(this._data);
} else {
return this._body.toString();
}
},
set: function(body) {
if (this._data&&body) Errors.setBodyWithData(this);
this._body = body;
return this;
},
enumerable: true
},
// - **processor**. The functions that will be used to convert to/from `data` and
// `body` attributes. You can add processors. The two that are built-in are for
// `text/plain`, which is basically an identity transformation and
// `application/json` and other JSON-based media types (including custom media
// types with `+json`). You can add your own processors. See below.
processor: {
get: function() {
var processor = Content.processors[this.type];
if (processor) {
return processor;
} else {
// Return the first processor that matches any part of the
// content type. ex: application/vnd.foobar.baz+json will match json.
var main = this.type.split(";")[0];
var parts = main.split(/\+|\//);
for (var i=0, l=parts.length; i < l; i++) {
processor = Content.processors[parts[i]]
}
return processor || {parser:identity,stringify:toString};
}
},
enumerable: true
},
// - **length**. Typically accessed as `content.length`, returns the length in
// bytes of the raw content entity.
length: {
get: function() {
if (typeof Buffer !== 'undefined') {
return Buffer.byteLength(this.body);
}
return this.body.length;
}
}
});
Content.processors = {};
// The `registerProcessor` function allows you to add your own processors to
// convert content entities. Each processor consists of a Javascript object with
// two properties:
// - **parser**. The function used to parse a raw content entity and convert it
// into a Javascript data type.
// - **stringify**. The function used to convert a Javascript data type into a
// raw content entity.
Content.registerProcessor = function(types,processor) {
// You can pass an array of types that will trigger this processor, or just one.
// We determine the array via duck-typing here.
if (types.forEach) {
types.forEach(function(type) {
Content.processors[type] = processor;
});
} else {
// If you didn't pass an array, we just use what you pass in.
Content.processors[types] = processor;
}
};
// Register the identity processor, which is used for text-based media types.
var identity = function(x) { return x; }
, toString = function(x) { return x.toString(); }
Content.registerProcessor(
["text/html","text/plain","text"],
{ parser: identity, stringify: toString });
// Register the JSON processor, which is used for JSON-based media types.
Content.registerProcessor(
["application/json; charset=utf-8","application/json","json"],
{
parser: function(string) {
return JSON.parse(string);
},
stringify: function(data) {
return JSON.stringify(data); }});
var qs = require('querystring');
// Register the post processor, which is used for JSON-based media types.
Content.registerProcessor(
["application/x-www-form-urlencoded"],
{ parser : qs.parse, stringify : qs.stringify });
// Error functions are defined separately here in an attempt to make the code
// easier to read.
var Errors = {
setDataWithBody: function(object) {
throw new Error("Attempt to set data attribute of a content object " +
"when the body attributes was already set.");
},
setBodyWithData: function(object) {
throw new Error("Attempt to set body attribute of a content object " +
"when the data attributes was already set.");
}
}
module.exports = Content;

File diff suppressed because it is too large Load Diff

View File

@@ -97,18 +97,19 @@ function handleLogin() {
var authSchemes = window.swaggerUi.api.authSchemes;
var host = window.location;
var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
var redirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
var url = null;
for (var key in authSchemes) {
if (authSchemes.hasOwnProperty(key)) {
var flow = authSchemes[key].flow;
if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
var dets = authSchemes[key];
url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
window.swaggerUi.tokenName = dets.tokenName || 'access_token';
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
}
else if(authSchemes[key].grantTypes) {
// 1.2 support
@@ -135,11 +136,14 @@ function handleLogin() {
for(k =0; k < o.length; k++) {
var scope = $(o[k]).attr('scope');
if (scopes.indexOf(scope) === -1)
scopes.push(scope);
}
// Implicit auth recommends a state parameter.
var state = Math.random ();
window.enabledScopes=scopes;
redirect_uri = redirectUrl;
@@ -147,7 +151,8 @@ function handleLogin() {
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes);
url += '&scope=' + encodeURIComponent(scopes.join(' '));
url += '&state=' + encodeURIComponent(state);
window.open(url);
});
@@ -198,7 +203,7 @@ function initOAuth(opts) {
});
}
function processOAuthCode(data) {
window.processOAuthCode = function processOAuthCode(data) {
var params = {
'client_id': clientId,
'code': data.code,
@@ -210,18 +215,18 @@ function processOAuthCode(data) {
url : window.swaggerUi.tokenUrl,
type: "POST",
data: params,
success:function(data, textStatus, jqXHR)
success:function(data, textStatus, jqXHR)
{
onOAuthComplete(data);
},
error: function(jqXHR, textStatus, errorThrown)
error: function(jqXHR, textStatus, errorThrown)
{
onOAuthComplete("");
}
});
}
function onOAuthComplete(token) {
window.onOAuthComplete = function onOAuthComplete(token) {
if(token) {
if(token.error) {
var checkbox = $('input[type=checkbox],.secured')
@@ -268,7 +273,7 @@ function onOAuthComplete(token) {
// all scopes are satisfied
$(o).find('.api-ic').addClass('ic-info');
$(o).find('.api-ic').removeClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
$(o).find('.api-ic').removeClass('ic-error');
}
}
});

1
lib/underscore-min.map Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,48 +1,50 @@
{
"name": "swagger-ui",
"author": "Tony Tam <fehguy@gmail.com>",
"description": "Swagger UI is a dependency-free collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API",
"version": "2.1.8-M1",
"contributors": [{
"name": "Mohsen Azimi",
"email": "me@azimi.me"
}],
"description": "Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API",
"version": "2.1.0-M2",
"homepage": "http://swagger.io",
"license": "Apache 2.0",
"scripts": {
"build": "./node_modules/gulp/bin/gulp.js;",
"serve": "./node_modules/gulp/bin/gulp.js serve;",
"test": "./node_modules/gulp/bin/gulp.js; ./node_modules/mocha/bin/mocha"
"build": "gulp",
"serve": "gulp serve",
"prejshint": "gulp",
"jshint": "jshint .",
"pretest": "npm run jshint",
"test": "mocha"
},
"repository": {
"type": "git",
"url": "https://github.com/swagger-api/swagger-ui.git"
},
"readmeFilename": "README.md",
"dependencies": {
"shred": "0.8.10",
"btoa": "1.1.1",
"swagger-client": "2.1.9-M1"
},
"devDependencies": {
"chai": "^1.10.0",
"cors": "2.1.1",
"docco": "0.4.x",
"event-stream": "^3.2.1",
"express": "3.x",
"gulp": "^3.8.10",
"chai": "^2.1.0",
"cors": "^2.5.3",
"docco": "^0.7.0",
"event-stream": "^3.2.2",
"express": "^4.12.0",
"gulp": "^3.8.11",
"gulp-clean": "^0.3.1",
"gulp-coffee": "^2.2.0",
"gulp-concat": "^2.4.3",
"gulp-concat": "^2.5.2",
"gulp-connect": "^2.2.0",
"gulp-declare": "^0.3.0",
"gulp-handlebars": "^3.0.1",
"gulp-header": "1.2.2",
"gulp-less": "^2.0.1",
"gulp-header": "^1.2.2",
"gulp-less": "^3.0.1",
"gulp-order": "^1.1.1",
"gulp-rename": "^1.2.0",
"gulp-uglify": "^1.1.0",
"gulp-util": "^3.0.2",
"gulp-watch": "^4.1.0",
"gulp-wrap": "^0.10.1",
"http-server": "^0.7.4",
"less": "~1.4.2",
"gulp-watch": "^4.1.1",
"gulp-wrap": "^0.11.0",
"http-server": "git+https://github.com/nodeapps/http-server.git",
"less": "^2.4.0",
"mocha": "^2.1.0",
"selenium-webdriver": "^2.44.0"
"selenium-webdriver": "^2.45.0",
"swagger-client": "2.1.0-M2"
}
}

View File

@@ -1,130 +0,0 @@
class SwaggerUi extends Backbone.Router
# Defaults
dom_id: "swagger_ui"
# Attributes
options: null
api: null
headerView: null
mainView: null
# SwaggerUi accepts all the same options as SwaggerApi
initialize: (options={}) ->
# Allow dom_id to be overridden
if options.dom_id?
@dom_id = options.dom_id
delete options.dom_id
if not options.supportedSubmitMethods?
options.supportedSubmitMethods = ['get','put','post','delete','head','options','patch']
# Create an empty div which contains the dom_id
$('body').append('<div id="' + @dom_id + '"></div>') if not $('#' + @dom_id)?
@options = options
# Set the callbacks
@options.success = =>
@render()
@options.progress = (d) => @showMessage(d)
@options.failure = (d) =>
@onLoadFailure(d)
# Create view to handle the header inputs
@headerView = new HeaderView({el: $('#header')})
# Event handler for when the baseUrl/apiKey is entered by user
@headerView.on 'update-swagger-ui', (data) => @updateSwaggerUi(data)
# Set an option after initializing
setOption: (option,value) ->
@options[option] = value
# Get the value of a previously set option
getOption: (option) ->
@options[option]
# Event handler for when url/key is received from user
updateSwaggerUi: (data) ->
@options.url = data.url
@load()
# Create an api and render
load: ->
# Initialize the API object
@mainView?.clear()
url = @options.url
if url && url.indexOf("http") isnt 0
url = @buildUrl(window.location.href.toString(), url)
@options.url = url
@headerView.update(url)
@api = new SwaggerClient(@options)
# collapse all sections
collapseAll:() ->
Docs.collapseEndpointListForResource('')
# list operations for all sections
listAll:() ->
Docs.collapseOperationsForResource('')
# expand operations for all sections
expandAll:() ->
Docs.expandOperationsForResource('')
# This is bound to success handler for SwaggerApi
# so it gets called when SwaggerApi completes loading
render:() ->
@showMessage('Finished Loading Resource Information. Rendering Swagger UI...')
@mainView = new MainView({model: @api, el: $('#' + @dom_id), swaggerOptions: @options}).render()
@showMessage()
switch @options.docExpansion
when "full" then @expandAll()
when "list" then @listAll()
@renderGFM()
@options.onComplete(@api, @) if @options.onComplete
setTimeout(
=>
Docs.shebang()
100
)
buildUrl: (base, url) ->
if url.indexOf("/") is 0
parts = base.split("/")
base = parts[0] + "//" + parts[2]
base + url
else
endOfPath = base.length
if base.indexOf("?") > -1
endOfPath = Math.min(endOfPath, base.indexOf("?"))
if base.indexOf("#") > -1
endOfPath = Math.min(endOfPath, base.indexOf("#"))
base = base.substring(0, endOfPath);
if base.indexOf( "/", base.length - 1 ) isnt -1
return base + url
return base + "/" + url
# Shows message on topbar of the ui
showMessage: (data = '') ->
$('#message-bar').removeClass 'message-fail'
$('#message-bar').addClass 'message-success'
$('#message-bar').html data
# shows message in red
onLoadFailure: (data = '') ->
$('#message-bar').removeClass 'message-success'
$('#message-bar').addClass 'message-fail'
val = $('#message-bar').html data
@options.onFailure(data) if @options.onFailure?
val
# Renders GFM for elements with 'markdown' class
renderGFM: (data = '') ->
$('.markdown').each (index) ->
$(this).html(marked($(this).html()))
window.SwaggerUi = SwaggerUi

View File

@@ -1,5 +0,0 @@
Handlebars.registerHelper('sanitize', (html) ->
# Strip the script tags from the html, and return it as a Handlebars.SafeString
html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
return new Handlebars.SafeString(html)
)

View File

@@ -1,31 +0,0 @@
class ApiKeyButton extends Backbone.View
initialize: ->
render: ->
template = @template()
$(@el).html(template(@model))
@
events:
"click #apikey_button" : "toggleApiKeyContainer"
"click #apply_api_key" : "applyApiKey"
applyApiKey: ->
window.authorizations.add(@model.name, new ApiKeyAuthorization(@model.name, $("#input_apiKey_entry").val(), @model.in))
window.swaggerUi.load()
elem = $('#apikey_container').show()
toggleApiKeyContainer: ->
if $('#apikey_container').length > 0
elem = $('#apikey_container').first()
if elem.is ':visible'
elem.hide()
else
# hide others
$('.auth_container').hide()
elem.show()
template: ->
Handlebars.templates.apikey_button_view

View File

@@ -1,34 +0,0 @@
class BasicAuthButton extends Backbone.View
initialize: ->
render: ->
template = @template()
$(@el).html(template(@model))
@
events:
"click #basic_auth_button" : "togglePasswordContainer"
"click #apply_basic_auth" : "applyPassword"
applyPassword: ->
username = $(".input_username").val()
password = $(".input_password").val()
window.authorizations.add(@model.type, new PasswordAuthorization("basic", username, password))
window.swaggerUi.load()
elem = $('#basic_auth_container').hide()
togglePasswordContainer: ->
if $('#basic_auth_container').length > 0
elem = $('#basic_auth_container').show()
if elem.is ':visible'
elem.slideUp()
else
# hide others
$('.auth_container').hide()
elem.show()
template: ->
Handlebars.templates.basic_auth_button_view

View File

@@ -1,14 +0,0 @@
class ContentTypeView extends Backbone.View
initialize: ->
render: ->
template = @template()
$(@el).html(template(@model))
$('label[for=contentType]', $(@el)).text('Response Content Type')
@
template: ->
Handlebars.templates.content_type

View File

@@ -1,37 +0,0 @@
class HeaderView extends Backbone.View
events: {
'click #show-pet-store-icon' : 'showPetStore'
'click #show-wordnik-dev-icon' : 'showWordnikDev'
'click #explore' : 'showCustom'
'keyup #input_baseUrl' : 'showCustomOnKeyup'
'keyup #input_apiKey' : 'showCustomOnKeyup'
}
initialize: ->
showPetStore: (e) ->
@trigger(
'update-swagger-ui'
{url:"http://petstore.swagger.wordnik.com/api/api-docs"}
)
showWordnikDev: (e) ->
@trigger(
'update-swagger-ui'
{url:"http://api.wordnik.com/v4/resources.json"}
)
showCustomOnKeyup: (e) ->
@showCustom() if e.keyCode is 13
showCustom: (e) ->
e?.preventDefault()
@trigger(
'update-swagger-ui'
{url: $('#input_baseUrl').val(), apiKey: $('#input_apiKey').val()}
)
update: (url, apiKey, trigger = false) ->
$('#input_baseUrl').val url
#$('#input_apiKey').val apiKey
@trigger 'update-swagger-ui', {url:url} if trigger

View File

@@ -1,74 +0,0 @@
class MainView extends Backbone.View
sorters = {
'alpha' : (a,b) -> return a.path.localeCompare(b.path),
'method' : (a,b) -> return a.method.localeCompare(b.method),
}
initialize: (opts={}) ->
# set up the UI for input
@model.auths = []
for key, value of @model.securityDefinitions
auth = {name: key, type: value.type, value: value}
@model.auths.push auth
if @model.swaggerVersion is "2.0"
if "validatorUrl" of opts.swaggerOptions
# Validator URL specified explicitly
@model.validatorUrl = opts.swaggerOptions.validatorUrl
else if @model.url.indexOf("localhost") > 0
# Localhost override
@model.validatorUrl = null
else
# Default validator
@model.validatorUrl = "http://online.swagger.io/validator"
render: ->
if @model.securityDefinitions
for name of @model.securityDefinitions
auth = @model.securityDefinitions[name]
if auth.type is "apiKey" and $("#apikey_button").length is 0
button = new ApiKeyButton({model: auth}).render().el
$('.auth_main_container').append button
if auth.type is "basicAuth" and $("#basic_auth_button").length is 0
button = new BasicAuthButton({model: auth}).render().el
$('.auth_main_container').append button
# Render the outer container for resources
$(@el).html(Handlebars.templates.main(@model))
# Render each resource
resources = {}
counter = 0
for resource in @model.apisArray
id = resource.name
while typeof resources[id] isnt 'undefined'
id = id + "_" + counter
counter += 1
resource.id = id
resources[id] = resource
@addResource resource, @model.auths
$('.propWrap').hover(
->
$('.optionsWrapper', $(this)).show()
,->
$('.optionsWrapper', $(this)).hide()
)
@
addResource: (resource, auths) ->
# Render a resource and add it to resources li
resource.id = resource.id.replace(/\s/g, '_')
resourceView = new ResourceView({
model: resource,
tagName: 'li',
id: 'resource_' + resource.id,
className: 'resource',
auths: auths,
swaggerOptions: @options.swaggerOptions
})
$('#resources').append resourceView.render().el
clear: ->
$(@el).html ''

View File

@@ -1,458 +0,0 @@
class OperationView extends Backbone.View
invocationUrl: null
events: {
'submit .sandbox' : 'submitOperation'
'click .submit' : 'submitOperation'
'click .response_hider' : 'hideResponse'
'click .toggleOperation' : 'toggleOperationContent'
'mouseenter .api-ic' : 'mouseEnter'
'mouseout .api-ic' : 'mouseExit'
}
initialize: (opts={}) ->
@auths = opts.auths
@parentId = @model.parentId
@nickname = @model.nickname
@
mouseEnter: (e) ->
elem = $(@el).find '.content'
x = e.pageX
y = e.pageY
scX = $(window).scrollLeft()
scY = $(window).scrollTop()
scMaxX = scX + $(window).width()
scMaxY = scY + $(window).height()
wd = elem.width()
hgh = elem.height()
if (x + wd > scMaxX)
x = scMaxX - wd
if (x < scX)
x = scX
if (y + hgh > scMaxY)
y = scMaxY - hgh
if (y < scY)
y = scY
pos = {}
pos.top = y
pos.left = x
elem.css(pos)
$(e.currentTarget.parentNode).find('#api_information_panel').show()
mouseExit: (e) ->
$(e.currentTarget.parentNode).find('#api_information_panel').hide()
render: ->
isMethodSubmissionSupported = jQuery.inArray(@model.method, @model.supportedSubmitMethods()) >= 0
@model.isReadOnly = true unless isMethodSubmissionSupported
# 1.2 syntax for description was `notes`
@model.description = (@model.description || @model.notes)
if @model.description
@model.description = @model.description.replace(/(?:\r\n|\r|\n)/g, '<br />')
@model.oauth = null
modelAuths = @model.authorizations || @model.security
if modelAuths
if Array.isArray modelAuths
for auths in modelAuths
for key, auth of auths
for a of @auths
auth = @auths[a]
if auth.type == 'oauth2'
@model.oauth = {}
@model.oauth.scopes = []
for k, v of auth.value.scopes
scopeIndex = auths[key].indexOf k
if scopeIndex >= 0
o = {scope: k, description: v}
@model.oauth.scopes.push o
else
for k, v of modelAuths
if k == "oauth2"
if @model.oauth == null
@model.oauth = {}
if @model.oauth.scopes is undefined
@model.oauth.scopes = []
for o in v
@model.oauth.scopes.push o
if typeof @model.responses isnt 'undefined'
@model.responseMessages = []
for code, value of @model.responses
schema = null
schemaObj = @model.responses[code].schema
if schemaObj and schemaObj['$ref']
schema = schemaObj['$ref']
if schema.indexOf('#/definitions/') is 0
schema = schema.substring('#/definitions/'.length)
@model.responseMessages.push {code: code, message: value.description, responseModel: schema }
if typeof @model.responseMessages is 'undefined'
@model.responseMessages = []
# 2.0
signatureModel = null
if @model.successResponse
successResponse = @model.successResponse
for key of successResponse
value = successResponse[key]
@model.successCode = key
if typeof value is 'object' and typeof value.createJSONSample is 'function'
signatureModel =
sampleJSON: JSON.stringify(value.createJSONSample(), undefined, 2)
isParam: false
signature: value.getMockSignature()
# 1.2
else if @model.responseClassSignature and @model.responseClassSignature != 'string'
signatureModel =
sampleJSON: @model.responseSampleJSON
isParam: false
signature: @model.responseClassSignature
$(@el).html(Handlebars.templates.operation(@model))
if signatureModel
responseSignatureView = new SignatureView({model: signatureModel, tagName: 'div'})
$('.model-signature', $(@el)).append responseSignatureView.render().el
else
@model.responseClassSignature = 'string'
$('.model-signature', $(@el)).html(@model.type)
contentTypeModel =
isParam: false
contentTypeModel.consumes = @model.consumes
contentTypeModel.produces = @model.produces
for param in @model.parameters
type = param.type || param.dataType || ''
if typeof type is 'undefined'
schema = param.schema
if schema and schema['$ref']
ref = schema['$ref']
if ref.indexOf('#/definitions/') is 0
type = ref.substring('#/definitions/'.length)
else
type = ref
if type and type.toLowerCase() == 'file'
if !contentTypeModel.consumes
contentTypeModel.consumes = 'multipart/form-data'
param.type = type
responseContentTypeView = new ResponseContentTypeView({model: contentTypeModel})
$('.response-content-type', $(@el)).append responseContentTypeView.render().el
# Render each parameter
@addParameter param, contentTypeModel.consumes for param in @model.parameters
# Render each response code
@addStatusCode statusCode for statusCode in @model.responseMessages
@
addParameter: (param, consumes) ->
# Render a parameter
param.consumes = consumes
paramView = new ParameterView({model: param, tagName: 'tr', readOnly: @model.isReadOnly})
$('.operation-params', $(@el)).append paramView.render().el
addStatusCode: (statusCode) ->
# Render status codes
statusCodeView = new StatusCodeView({model: statusCode, tagName: 'tr'})
$('.operation-status', $(@el)).append statusCodeView.render().el
submitOperation: (e) ->
e?.preventDefault()
# Check for errors
form = $('.sandbox', $(@el))
error_free = true
form.find("input.required").each ->
$(@).removeClass "error"
if jQuery.trim($(@).val()) is ""
$(@).addClass "error"
$(@).wiggle
callback: => $(@).focus()
error_free = false
form.find("textarea.required").each ->
$(@).removeClass "error"
if jQuery.trim($(@).val()) is ""
$(@).addClass "error"
$(@).wiggle
callback: => $(@).focus()
error_free = false
# if error free submit it
if error_free
map = {}
opts = {parent: @}
isFileUpload = false
for o in form.find("input")
if(o.value? && jQuery.trim(o.value).length > 0)
map[o.name] = o.value
if o.type is "file"
map[o.name] = o.files[0]
isFileUpload = true
for o in form.find("textarea")
if(o.value? && jQuery.trim(o.value).length > 0)
map[o.name] = o.value
for o in form.find("select")
val = this.getSelectedValue o
if(val? && jQuery.trim(val).length > 0)
map[o.name] = val
opts.responseContentType = $("div select[name=responseContentType]", $(@el)).val()
opts.requestContentType = $("div select[name=parameterContentType]", $(@el)).val()
$(".response_throbber", $(@el)).show()
if isFileUpload
@handleFileUpload map, form
else
@model.do(map, opts, @showCompleteStatus, @showErrorStatus, @)
success: (response, parent) ->
parent.showCompleteStatus response
handleFileUpload: (map, form) ->
for o in form.serializeArray()
if(o.value? && jQuery.trim(o.value).length > 0)
map[o.name] = o.value
# requires HTML5 compatible browser
bodyParam = new FormData()
params = 0
# add params
for param in @model.parameters
if param.paramType is 'form' or param.in is 'formData'
if param.type.toLowerCase() isnt 'file' and map[param.name] != undefined
bodyParam.append(param.name, map[param.name])
# headers in operation
headerParams = {}
for param in @model.parameters
if param.paramType is 'header'
headerParams[param.name] = map[param.name]
# add files
for el in form.find('input[type~="file"]')
if typeof el.files[0] isnt 'undefined'
bodyParam.append($(el).attr('name'), el.files[0])
params += 1
@invocationUrl =
if @model.supportHeaderParams()
headerParams = @model.getHeaderParams(map)
delete headerParams['Content-Type']
@model.urlify(map, false)
else
@model.urlify(map, true)
$(".request_url", $(@el)).html("<pre></pre>")
$(".request_url pre", $(@el)).text(@invocationUrl);
obj =
type: @model.method
url: @invocationUrl
headers: headerParams
data: bodyParam
dataType: 'json'
contentType: false
processData: false
error: (data, textStatus, error) =>
@showErrorStatus(@wrap(data), @)
success: (data) =>
@showResponse(data, @)
complete: (data) =>
@showCompleteStatus(@wrap(data), @)
# apply authorizations
if window.authorizations
window.authorizations.apply obj
if params is 0
obj.data.append("fake", "true");
jQuery.ajax(obj)
false
# end of file-upload nastiness
# wraps a jquery response as a shred response
wrap: (data) ->
headers = {}
headerArray = data.getAllResponseHeaders().split("\r")
for i in headerArray
h = i.match(/^([^:]*?):(.*)$/)
if(!h)
h = []
h.shift()
if (h[0] != undefined && h[1] != undefined)
headers[h[0].trim()] = h[1].trim()
o = {}
o.content = {}
o.content.data = data.responseText
o.headers = headers
o.request = {}
o.request.url = @invocationUrl
o.status = data.status
o
getSelectedValue: (select) ->
if !select.multiple
select.value
else
options = []
options.push opt.value for opt in select.options when opt.selected
if options.length > 0
options
else
null
# handler for hide response link
hideResponse: (e) ->
e?.preventDefault()
$(".response", $(@el)).slideUp()
$(".response_hider", $(@el)).fadeOut()
# Show response from server
showResponse: (response) ->
prettyJson = JSON.stringify(response, null, "\t").replace(/\n/g, "<br>")
$(".response_body", $(@el)).html escape(prettyJson)
# Show error from server
showErrorStatus: (data, parent) ->
parent.showStatus data
# show the status codes
showCompleteStatus: (data, parent) ->
parent.showStatus data
# Adapted from http://stackoverflow.com/a/2893259/454004
formatXml: (xml) ->
reg = /(>)(<)(\/*)/g
wsexp = /[ ]*(.*)[ ]+\n/g
contexp = /(<.+>)(.+\n)/g
xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2')
pad = 0
formatted = ''
lines = xml.split('\n')
indent = 0
lastType = 'other'
# 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions
transitions =
'single->single': 0
'single->closing': -1
'single->opening': 0
'single->other': 0
'closing->single': 0
'closing->closing': -1
'closing->opening': 0
'closing->other': 0
'opening->single': 1
'opening->closing': 0
'opening->opening': 1
'opening->other': 1
'other->single': 0
'other->closing': -1
'other->opening': 0
'other->other': 0
for ln in lines
do (ln) ->
types =
# is this line a single tag? ex. <br />
single: Boolean(ln.match(/<.+\/>/))
# is this a closing tag? ex. </a>
closing: Boolean(ln.match(/<\/.+>/))
# is this even a tag (that's not <!something>)
opening: Boolean(ln.match(/<[^!?].*>/))
[type] = (key for key, value of types when value)
type = if type is undefined then 'other' else type
fromTo = lastType + '->' + type
lastType = type
padding = ''
indent += transitions[fromTo]
padding = (' ' for j in [0...(indent)]).join('')
if fromTo == 'opening->closing'
#substr removes line break (\n) from prev loop
formatted = formatted.substr(0, formatted.length - 1) + ln + '\n'
else
formatted += padding + ln + '\n'
formatted
# puts the response data in UI
showStatus: (response) ->
if response.content is undefined
content = response.data
url = response.url
else
content = response.content.data
url = response.request.url
headers = response.headers
# if server is nice, and sends content-type back, we can use it
contentType = null
if headers
contentType = headers["Content-Type"] or headers["content-type"]
if contentType
contentType = contentType.split(";")[0].trim()
$(".response_body", $(@el)).removeClass 'json'
$(".response_body", $(@el)).removeClass 'xml'
if !content
code = $('<code />').text("no content")
pre = $('<pre class="json" />').append(code)
else if contentType is "application/json" || /\+json$/.test(contentType)
json = null
try
json = JSON.stringify(JSON.parse(content), null, " ")
catch e
json = "can't parse JSON. Raw result:\n\n" + content
code = $('<code />').text(json)
pre = $('<pre class="json" />').append(code)
else if contentType is "application/xml" || /\+xml$/.test(contentType)
code = $('<code />').text(@formatXml(content))
pre = $('<pre class="xml" />').append(code)
else if contentType is "text/html"
code = $('<code />').html(_.escape(content))
pre = $('<pre class="xml" />').append(code)
else if /^image\//.test(contentType)
pre = $('<img>').attr('src',url)
else
# don't know what to render!
code = $('<code />').text(content)
pre = $('<pre class="json" />').append(code)
response_body = pre
$(".request_url", $(@el)).html("<pre></pre>")
$(".request_url pre", $(@el)).text(url);
$(".response_code", $(@el)).html "<pre>" + response.status + "</pre>"
$(".response_body", $(@el)).html response_body
$(".response_headers", $(@el)).html "<pre>" + _.escape(JSON.stringify(response.headers, null, " ")).replace(/\n/g, "<br>") + "</pre>"
$(".response", $(@el)).slideDown()
$(".response_hider", $(@el)).show()
$(".response_throbber", $(@el)).hide()
response_body_el = $('.response_body', $(@el))[0]
# only highlight the response if response is less than threshold, default state is highlight response
opts = @options.swaggerOptions
if opts.highlightSizeThreshold && response.data.length > opts.highlightSizeThreshold then response_body_el else hljs.highlightBlock(response_body_el)
toggleOperationContent: ->
elem = $('#' + Docs.escapeResourceName(@parentId + "_" + @nickname + "_content"))
if elem.is(':visible') then Docs.collapseOperation(elem) else Docs.expandOperation(elem)

View File

@@ -1,14 +0,0 @@
class ParameterContentTypeView extends Backbone.View
initialize: ->
render: ->
template = @template()
$(@el).html(template(@model))
$('label[for=parameterContentType]', $(@el)).text('Parameter content type:')
@
template: ->
Handlebars.templates.parameter_content_type

View File

@@ -1,79 +0,0 @@
class ParameterView extends Backbone.View
initialize: ->
Handlebars.registerHelper 'isArray',
(param, opts) ->
if param.type.toLowerCase() == 'array' || param.allowMultiple
opts.fn(@)
else
opts.inverse(@)
render: ->
type = @model.type || @model.dataType
if typeof type is 'undefined'
schema = @model.schema
if schema and schema['$ref']
ref = schema['$ref']
if ref.indexOf('#/definitions/') is 0
type = ref.substring('#/definitions/'.length)
else
type = ref
@model.type = type
@model.paramType = @model.in || @model.paramType
@model.isBody = true if @model.paramType == 'body' or @model.in == 'body'
@model.isFile = true if type and type.toLowerCase() == 'file'
@model.default = (@model.default || @model.defaultValue)
if@model.allowableValues
@model.isList = true
template = @template()
$(@el).html(template(@model))
signatureModel =
sampleJSON: @model.sampleJSON
isParam: true
signature: @model.signature
if @model.sampleJSON
signatureView = new SignatureView({model: signatureModel, tagName: 'div'})
$('.model-signature', $(@el)).append signatureView.render().el
else
$('.model-signature', $(@el)).html(@model.signature)
isParam = false
if @model.isBody
isParam = true
contentTypeModel =
isParam: isParam
contentTypeModel.consumes = @model.consumes
if isParam
parameterContentTypeView = new ParameterContentTypeView({model: contentTypeModel})
$('.parameter-content-type', $(@el)).append parameterContentTypeView.render().el
else
responseContentTypeView = new ResponseContentTypeView({model: contentTypeModel})
$('.response-content-type', $(@el)).append responseContentTypeView.render().el
@
# Return an appropriate template based on if the parameter is a list, readonly, required
template: ->
if @model.isList
Handlebars.templates.param_list
else
if @options.readOnly
if @model.required
Handlebars.templates.param_readonly_required
else
Handlebars.templates.param_readonly
else
if @model.required
Handlebars.templates.param_required
else
Handlebars.templates.param

View File

@@ -1,58 +0,0 @@
class ResourceView extends Backbone.View
initialize: (opts={}) ->
@auths = opts.auths
if "" is @model.description
@model.description = null
if @model.description?
@model.summary = @model.description
render: ->
methods = {}
$(@el).html(Handlebars.templates.resource(@model))
# Render each operation
for operation in @model.operationsArray
counter = 0
id = operation.nickname
while typeof methods[id] isnt 'undefined'
id = id + "_" + counter
counter += 1
methods[id] = operation
operation.nickname = id
operation.parentId = @model.id
@addOperation operation
$('.toggleEndpointList', @el).click(this.callDocs.bind(this, 'toggleEndpointListForResource'))
$('.collapseResource', @el).click(this.callDocs.bind(this, 'collapseOperationsForResource'))
$('.expandResource', @el).click(this.callDocs.bind(this, 'expandOperationsForResource'))
return @
addOperation: (operation) ->
operation.number = @number
# Render an operation and add it to operations li
operationView = new OperationView({
model: operation,
tagName: 'li',
className: 'endpoint',
swaggerOptions: @options.swaggerOptions,
auths: @auths
})
$('.endpoints', $(@el)).append operationView.render().el
@number++
#
# Generic Event handler (`Docs` is global)
#
callDocs: (fnName, e) ->
e.preventDefault()
Docs[fnName](e.currentTarget.getAttribute('data-id'))

View File

@@ -1,14 +0,0 @@
class ResponseContentTypeView extends Backbone.View
initialize: ->
render: ->
template = @template()
$(@el).html(template(@model))
$('label[for=responseContentType]', $(@el)).text('Response Content Type')
@
template: ->
Handlebars.templates.response_content_type

View File

@@ -1,51 +0,0 @@
class SignatureView extends Backbone.View
events: {
'click a.description-link' : 'switchToDescription'
'click a.snippet-link' : 'switchToSnippet'
'mousedown .snippet' : 'snippetToTextArea'
}
initialize: ->
render: ->
template = @template()
$(@el).html(template(@model))
@switchToSnippet()
@isParam = @model.isParam
if @isParam
$('.notice', $(@el)).text('Click to set as parameter value')
@
template: ->
Handlebars.templates.signature
# handler for show signature
switchToDescription: (e) ->
e?.preventDefault()
$(".snippet", $(@el)).hide()
$(".description", $(@el)).show()
$('.description-link', $(@el)).addClass('selected')
$('.snippet-link', $(@el)).removeClass('selected')
# handler for show sample
switchToSnippet: (e) ->
e?.preventDefault()
$(".description", $(@el)).hide()
$(".snippet", $(@el)).show()
$('.snippet-link', $(@el)).addClass('selected')
$('.description-link', $(@el)).removeClass('selected')
# handler for snippet to text area
snippetToTextArea: (e) ->
if @isParam
e?.preventDefault()
textArea = $('textarea', $(@el.parentNode.parentNode.parentNode))
if $.trim(textArea.val()) == ''
textArea.val(@model.sampleJSON)

View File

@@ -1,22 +0,0 @@
class StatusCodeView extends Backbone.View
initialize: ->
render: ->
template = @template()
$(@el).html(template(@model))
if swaggerUi.api.models.hasOwnProperty @model.responseModel
responseModel =
sampleJSON: JSON.stringify(swaggerUi.api.models[@model.responseModel].createJSONSample(), null, 2)
isParam: false
signature: swaggerUi.api.models[@model.responseModel].getMockSignature()
responseModelView = new SignatureView({model: responseModel, tagName: 'div'})
$('.model-signature', @$el).append responseModelView.render().el
else
$('.model-signature', @$el).html ''
@
template: ->
Handlebars.templates.status_code

1155
src/main/html/css/print.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Binary file not shown.

BIN
src/main/html/images/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 770 B

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 824 B

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

After

Width:  |  Height:  |  Size: 979 B

View File

@@ -1,98 +1,101 @@
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='print' rel='stylesheet' type='text/css'/>
<script type="text/javascript" src="lib/shred.bundle.js"></script>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='lib/swagger-client.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script src='lib/marked.js' type='text/javascript'></script>
<!-- enabling this will enable oauth2 implicit scope support -->
<script src='lib/swagger-oauth.js' type='text/javascript'></script>
<script type="text/javascript">
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "http://petstore.swagger.io/v2/swagger.json";
}
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
/*
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
*/
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
sorter : "alpha"
});
function addApiKeyAuthorization() {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if(key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("api_key", new ApiKeyAuthorization("api_key", key, "query"));
}
}
$('#input_apiKey').change(function() {
addApiKeyAuthorization();
});
// if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
/*
var apiKey = "myApiKeyXXXX123456789";
$('#input_apiKey').val(apiKey);
addApiKeyAuthorization();
*/
window.swaggerUi.load();
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
<link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
<script src='lib/underscore-min.js' type='text/javascript'></script>
<script src='lib/backbone-min.js' type='text/javascript'></script>
<script src='swagger-ui.js' type='text/javascript'></script>
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script src='lib/marked.js' type='text/javascript'></script>
<script type="text/javascript">
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "http://petstore.swagger.io/v2/swagger.json";
}
window.swaggerUi = new SwaggerUi({
url: url,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
/*
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
*/
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
sorter : "alpha"
});
function addApiKeyAuthorization() {
var key = encodeURIComponent($('#input_apiKey')[0].value);
log("key: " + key);
if(key && key.trim() != "") {
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("api_key", key, "query");
window.swaggerUi.api.clientAuthorizations.add("api_key", apiKeyAuth);
log("added key " + key);
}
}
$('#input_apiKey').change(addApiKeyAuthorization);
// if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
/*
var apiKey = "myApiKeyXXXX123456789";
$('#input_apiKey').val(apiKey);
addApiKeyAuthorization();
*/
window.swaggerUi.load();
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
});
</script>
</head>
<body class="swagger-section">
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.io">swagger</a>
<form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>

View File

@@ -0,0 +1,256 @@
'use strict';
window.SwaggerUi = Backbone.Router.extend({
dom_id: 'swagger_ui',
// Attributes
options: null,
api: null,
headerView: null,
mainView: null,
// SwaggerUi accepts all the same options as SwaggerApi
initialize: function(options) {
options = options || {};
// Allow dom_id to be overridden
if (options.dom_id) {
this.dom_id = options.dom_id;
delete options.dom_id;
}
if (!options.supportedSubmitMethods){
options.supportedSubmitMethods = [
'get',
'put',
'post',
'delete',
'head',
'options',
'patch'
];
}
if (typeof options.oauth2RedirectUrl === 'string') {
window.oAuthRedirectUrl = options.redirectUrl;
}
// Create an empty div which contains the dom_id
if (! $('#' + this.dom_id).length){
$('body').append('<div id="' + this.dom_id + '"></div>') ;
}
this.options = options;
// set marked options
marked.setOptions({gfm: true});
// Set the callbacks
var that = this;
this.options.success = function() { return that.render(); };
this.options.progress = function(d) { return that.showMessage(d); };
this.options.failure = function(d) { return that.onLoadFailure(d); };
// Create view to handle the header inputs
this.headerView = new SwaggerUi.Views.HeaderView({el: $('#header')});
// Event handler for when the baseUrl/apiKey is entered by user
this.headerView.on('update-swagger-ui', function(data) {
return that.updateSwaggerUi(data);
});
},
// Set an option after initializing
setOption: function(option, value) {
this.options[option] = value;
},
// Get the value of a previously set option
getOption: function(option) {
return this.options[option];
},
// Event handler for when url/key is received from user
updateSwaggerUi: function(data){
this.options.url = data.url;
this.load();
},
// Create an api and render
load: function(){
// Initialize the API object
if (this.mainView) {
this.mainView.clear();
}
var url = this.options.url;
if (url && url.indexOf('http') !== 0) {
url = this.buildUrl(window.location.href.toString(), url);
}
this.options.url = url;
this.headerView.update(url);
this.api = new SwaggerClient(this.options);
},
// collapse all sections
collapseAll: function(){
Docs.collapseEndpointListForResource('');
},
// list operations for all sections
listAll: function(){
Docs.collapseOperationsForResource('');
},
// expand operations for all sections
expandAll: function(){
Docs.expandOperationsForResource('');
},
// This is bound to success handler for SwaggerApi
// so it gets called when SwaggerApi completes loading
render: function(){
this.showMessage('Finished Loading Resource Information. Rendering Swagger UI...');
this.mainView = new SwaggerUi.Views.MainView({
model: this.api,
el: $('#' + this.dom_id),
swaggerOptions: this.options,
router: this
}).render();
this.showMessage();
switch (this.options.docExpansion) {
case 'full':
this.expandAll(); break;
case 'list':
this.listAll(); break;
default:
break;
}
this.renderGFM();
if (this.options.onComplete){
this.options.onComplete(this.api, this);
}
setTimeout(Docs.shebang.bind(this), 100);
},
buildUrl: function(base, url){
if (url.indexOf('/') === 0) {
var parts = base.split('/');
base = parts[0] + '//' + parts[2];
return base + url;
} else {
var endOfPath = base.length;
if (base.indexOf('?') > -1){
endOfPath = Math.min(endOfPath, base.indexOf('?'));
}
if (base.indexOf('#') > -1){
endOfPath = Math.min(endOfPath, base.indexOf('#'));
}
base = base.substring(0, endOfPath);
if (base.indexOf('/', base.length - 1 ) !== -1){
return base + url;
}
return base + '/' + url;
}
},
// Shows message on topbar of the ui
showMessage: function(data){
if (data === undefined) {
data = '';
}
$('#message-bar').removeClass('message-fail');
$('#message-bar').addClass('message-success');
$('#message-bar').html(data);
},
// shows message in red
onLoadFailure: function(data){
if (data === undefined) {
data = '';
}
$('#message-bar').removeClass('message-success');
$('#message-bar').addClass('message-fail');
var val = $('#message-bar').html(data);
if (this.options.onFailure) {
this.options.onFailure(data);
}
return val;
},
// Renders GFM for elements with 'markdown' class
renderGFM: function(){
$('.markdown').each(function(){
$(this).html(marked($(this).html()));
});
}
});
window.SwaggerUi.Views = {};
// don't break backward compatibility with previous versions and warn users to upgrade their code
(function(){
window.authorizations = {
add: function() {
warn('Using window.authorizations is deprecated. Please use SwaggerUi.api.clientAuthorizations.add().');
if (typeof window.swaggerUi === 'undefined') {
throw new TypeError('window.swaggerUi is not defined');
}
if (window.swaggerUi instanceof SwaggerUi) {
window.swaggerUi.api.clientAuthorizations.add.apply(window.swaggerUi.api.clientAuthorizations, arguments);
}
}
};
window.ApiKeyAuthorization = function() {
warn('window.ApiKeyAuthorization is deprecated. Please use SwaggerClient.ApiKeyAuthorization.');
SwaggerClient.ApiKeyAuthorization.apply(window, arguments);
};
window.PasswordAuthorization = function() {
warn('window.PasswordAuthorization is deprecated. Please use SwaggerClient.PasswordAuthorization.');
SwaggerClient.PasswordAuthorization.apply(window, arguments);
};
function warn(message) {
if ('console' in window && typeof window.console.warn === 'function') {
console.warn(message);
}
}
})();
// UMD
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['b'], function (b) {
return (root.SwaggerUi = factory(b));
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like enviroments that support module.exports,
// like Node.
module.exports = factory(require('b'));
} else {
// Browser globals
root.SwaggerUi = factory(root.b);
}
}(this, function () {
return SwaggerUi;
}));

View File

@@ -1,194 +1,197 @@
$(function() {
// Helper function for vertically aligning DOM elements
// http://www.seodenver.com/simple-vertical-align-plugin-for-jquery/
$.fn.vAlign = function() {
return this.each(function(i){
var ah = $(this).height();
var ph = $(this).parent().height();
var mh = (ph - ah) / 2;
$(this).css('margin-top', mh);
});
};
$.fn.stretchFormtasticInputWidthToParent = function() {
return this.each(function(i){
var p_width = $(this).closest("form").innerWidth();
var p_padding = parseInt($(this).closest("form").css('padding-left') ,10) + parseInt($(this).closest("form").css('padding-right'), 10);
var this_padding = parseInt($(this).css('padding-left'), 10) + parseInt($(this).css('padding-right'), 10);
$(this).css('width', p_width - p_padding - this_padding);
});
};
$('form.formtastic li.string input, form.formtastic textarea').stretchFormtasticInputWidthToParent();
// Vertically center these paragraphs
// Parent may need a min-height for this to work..
$('ul.downplayed li div.content p').vAlign();
// When a sandbox form is submitted..
$("form.sandbox").submit(function(){
var error_free = true;
// Cycle through the forms required inputs
$(this).find("input.required").each(function() {
// Remove any existing error styles from the input
$(this).removeClass('error');
// Tack the error style on if the input is empty..
if ($(this).val() == '') {
$(this).addClass('error');
$(this).wiggle();
error_free = false;
}
});
return error_free;
});
});
function clippyCopiedCallback(a) {
$('#api_key_copied').fadeIn().delay(1000).fadeOut();
// var b = $("#clippy_tooltip_" + a);
// b.length != 0 && (b.attr("title", "copied!").trigger("tipsy.reload"), setTimeout(function() {
// b.attr("title", "copy to clipboard")
// },
// 500))
}
// Logging function that accounts for browsers that don't have window.console
log = function(){
log.history = log.history || [];
log.history.push(arguments);
if(this.console){
console.log( Array.prototype.slice.call(arguments)[0] );
}
};
// Handle browsers that do console incorrectly (IE9 and below, see http://stackoverflow.com/a/5539378/7913)
if (Function.prototype.bind && console && typeof console.log == "object") {
[
"log","info","warn","error","assert","dir","clear","profile","profileEnd"
].forEach(function (method) {
console[method] = this.bind(console[method], console);
}, Function.prototype.call);
}
var Docs = {
shebang: function() {
// If shebang has an operation nickname in it..
// e.g. /docs/#!/words/get_search
var fragments = $.param.fragment().split('/');
fragments.shift(); // get rid of the bang
switch (fragments.length) {
case 1:
// Expand all operations for the resource and scroll to it
var dom_id = 'resource_' + fragments[0];
Docs.expandEndpointListForResource(fragments[0]);
$("#"+dom_id).slideto({highlight: false});
break;
case 2:
// Refer to the endpoint DOM element, e.g. #words_get_search
// Expand Resource
Docs.expandEndpointListForResource(fragments[0]);
$("#"+dom_id).slideto({highlight: false});
// Expand operation
var li_dom_id = fragments.join('_');
var li_content_dom_id = li_dom_id + "_content";
Docs.expandOperation($('#'+li_content_dom_id));
$('#'+li_dom_id).slideto({highlight: false});
break;
}
},
toggleEndpointListForResource: function(resource) {
var elem = $('li#resource_' + Docs.escapeResourceName(resource) + ' ul.endpoints');
if (elem.is(':visible')) {
Docs.collapseEndpointListForResource(resource);
} else {
Docs.expandEndpointListForResource(resource);
}
},
// Expand resource
expandEndpointListForResource: function(resource) {
var resource = Docs.escapeResourceName(resource);
if (resource == '') {
$('.resource ul.endpoints').slideDown();
return;
}
$('li#resource_' + resource).addClass('active');
var elem = $('li#resource_' + resource + ' ul.endpoints');
elem.slideDown();
},
// Collapse resource and mark as explicitly closed
collapseEndpointListForResource: function(resource) {
var resource = Docs.escapeResourceName(resource);
if (resource == '') {
$('.resource ul.endpoints').slideUp();
return;
}
$('li#resource_' + resource).removeClass('active');
var elem = $('li#resource_' + resource + ' ul.endpoints');
elem.slideUp();
},
expandOperationsForResource: function(resource) {
// Make sure the resource container is open..
Docs.expandEndpointListForResource(resource);
if (resource == '') {
$('.resource ul.endpoints li.operation div.content').slideDown();
return;
}
$('li#resource_' + Docs.escapeResourceName(resource) + ' li.operation div.content').each(function() {
Docs.expandOperation($(this));
});
},
collapseOperationsForResource: function(resource) {
// Make sure the resource container is open..
Docs.expandEndpointListForResource(resource);
if (resource == '') {
$('.resource ul.endpoints li.operation div.content').slideUp();
return;
}
$('li#resource_' + Docs.escapeResourceName(resource) + ' li.operation div.content').each(function() {
Docs.collapseOperation($(this));
});
},
escapeResourceName: function(resource) {
return resource.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g, "\\$&");
},
expandOperation: function(elem) {
elem.slideDown();
},
collapseOperation: function(elem) {
elem.slideUp();
}
};
'use strict';
$(function() {
// Helper function for vertically aligning DOM elements
// http://www.seodenver.com/simple-vertical-align-plugin-for-jquery/
$.fn.vAlign = function() {
return this.each(function(){
var ah = $(this).height();
var ph = $(this).parent().height();
var mh = (ph - ah) / 2;
$(this).css('margin-top', mh);
});
};
$.fn.stretchFormtasticInputWidthToParent = function() {
return this.each(function(){
var p_width = $(this).closest("form").innerWidth();
var p_padding = parseInt($(this).closest("form").css('padding-left') ,10) + parseInt($(this).closest('form').css('padding-right'), 10);
var this_padding = parseInt($(this).css('padding-left'), 10) + parseInt($(this).css('padding-right'), 10);
$(this).css('width', p_width - p_padding - this_padding);
});
};
$('form.formtastic li.string input, form.formtastic textarea').stretchFormtasticInputWidthToParent();
// Vertically center these paragraphs
// Parent may need a min-height for this to work..
$('ul.downplayed li div.content p').vAlign();
// When a sandbox form is submitted..
$("form.sandbox").submit(function(){
var error_free = true;
// Cycle through the forms required inputs
$(this).find("input.required").each(function() {
// Remove any existing error styles from the input
$(this).removeClass('error');
// Tack the error style on if the input is empty..
if ($(this).val() === '') {
$(this).addClass('error');
$(this).wiggle();
error_free = false;
}
});
return error_free;
});
});
function clippyCopiedCallback() {
$('#api_key_copied').fadeIn().delay(1000).fadeOut();
// var b = $("#clippy_tooltip_" + a);
// b.length != 0 && (b.attr("title", "copied!").trigger("tipsy.reload"), setTimeout(function() {
// b.attr("title", "copy to clipboard")
// },
// 500))
}
// Logging function that accounts for browsers that don't have window.console
function log(){
log.history = log.history || [];
log.history.push(arguments);
if(this.console){
console.log( Array.prototype.slice.call(arguments)[0] );
}
}
// Handle browsers that do console incorrectly (IE9 and below, see http://stackoverflow.com/a/5539378/7913)
if (Function.prototype.bind && console && typeof console.log === "object") {
[
"log","info","warn","error","assert","dir","clear","profile","profileEnd"
].forEach(function (method) {
console[method] = this.bind(console[method], console);
}, Function.prototype.call);
}
window.Docs = {
shebang: function() {
// If shebang has an operation nickname in it..
// e.g. /docs/#!/words/get_search
var fragments = $.param.fragment().split('/');
fragments.shift(); // get rid of the bang
switch (fragments.length) {
case 1:
// Expand all operations for the resource and scroll to it
var dom_id = 'resource_' + fragments[0];
Docs.expandEndpointListForResource(fragments[0]);
$("#"+dom_id).slideto({highlight: false});
break;
case 2:
// Refer to the endpoint DOM element, e.g. #words_get_search
// Expand Resource
Docs.expandEndpointListForResource(fragments[0]);
$("#"+dom_id).slideto({highlight: false});
// Expand operation
var li_dom_id = fragments.join('_');
var li_content_dom_id = li_dom_id + "_content";
Docs.expandOperation($('#'+li_content_dom_id));
$('#'+li_dom_id).slideto({highlight: false});
break;
}
},
toggleEndpointListForResource: function(resource) {
var elem = $('li#resource_' + Docs.escapeResourceName(resource) + ' ul.endpoints');
if (elem.is(':visible')) {
Docs.collapseEndpointListForResource(resource);
} else {
Docs.expandEndpointListForResource(resource);
}
},
// Expand resource
expandEndpointListForResource: function(resource) {
var resource = Docs.escapeResourceName(resource);
if (resource == '') {
$('.resource ul.endpoints').slideDown();
return;
}
$('li#resource_' + resource).addClass('active');
var elem = $('li#resource_' + resource + ' ul.endpoints');
elem.slideDown();
},
// Collapse resource and mark as explicitly closed
collapseEndpointListForResource: function(resource) {
var resource = Docs.escapeResourceName(resource);
if (resource == '') {
$('.resource ul.endpoints').slideUp();
return;
}
$('li#resource_' + resource).removeClass('active');
var elem = $('li#resource_' + resource + ' ul.endpoints');
elem.slideUp();
},
expandOperationsForResource: function(resource) {
// Make sure the resource container is open..
Docs.expandEndpointListForResource(resource);
if (resource == '') {
$('.resource ul.endpoints li.operation div.content').slideDown();
return;
}
$('li#resource_' + Docs.escapeResourceName(resource) + ' li.operation div.content').each(function() {
Docs.expandOperation($(this));
});
},
collapseOperationsForResource: function(resource) {
// Make sure the resource container is open..
Docs.expandEndpointListForResource(resource);
if (resource == '') {
$('.resource ul.endpoints li.operation div.content').slideUp();
return;
}
$('li#resource_' + Docs.escapeResourceName(resource) + ' li.operation div.content').each(function() {
Docs.collapseOperation($(this));
});
},
escapeResourceName: function(resource) {
return resource.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g, "\\$&");
},
expandOperation: function(elem) {
elem.slideDown();
},
collapseOperation: function(elem) {
elem.slideUp();
}
};

View File

@@ -0,0 +1,7 @@
'use strict';
Handlebars.registerHelper('sanitize', function(html) {
// Strip the script tags from the html, and return it as a Handlebars.SafeString
html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
return new Handlebars.SafeString(html);
});

View File

@@ -0,0 +1,54 @@
'use strict';
SwaggerUi.Views.ApiKeyButton = Backbone.View.extend({ // TODO: append this to global SwaggerUi
events:{
'click #apikey_button' : 'toggleApiKeyContainer',
'click #apply_api_key' : 'applyApiKey'
},
initialize: function(opts){
this.options = opts || {};
this.router = this.options.router;
},
render: function(){
var template = this.template();
$(this.el).html(template(this.model));
return this;
},
applyApiKey: function(){
var keyAuth = new SwaggerClient.ApiKeyAuthorization(
this.model.name,
$('#input_apiKey_entry').val(),
this.model.in
);
this.router.api.clientAuthorizations.add(this.model.name, keyAuth);
this.router.load();
$('#apikey_container').show();
},
toggleApiKeyContainer: function(){
if ($('#apikey_container').length) {
var elem = $('#apikey_container').first();
if (elem.is(':visible')){
elem.hide();
} else {
// hide others
$('.auth_container').hide();
elem.show();
}
}
},
template: function(){
return Handlebars.templates.apikey_button_view;
}
});

View File

@@ -0,0 +1,49 @@
'use strict';
SwaggerUi.Views.BasicAuthButton = Backbone.View.extend({
initialize: function (opts) {
this.options = opts || {};
this.router = this.options.router;
},
render: function(){
var template = this.template();
$(this.el).html(template(this.model));
return this;
},
events: {
'click #basic_auth_button' : 'togglePasswordContainer',
'click #apply_basic_auth' : 'applyPassword'
},
applyPassword: function(){
var username = $('.input_username').val();
var password = $('.input_password').val();
var basicAuth = new SwaggerClient.PasswordAuthorization('basic', username, password);
this.router.api.clientAuthorizations.add(this.model.type, basicAuth);
this.router.load();
$('#basic_auth_container').hide();
},
togglePasswordContainer: function(){
if ($('#basic_auth_container').length) {
var elem = $('#basic_auth_container').show();
if (elem.is(':visible')){
elem.slideUp();
} else {
// hide others
$('.auth_container').hide();
elem.show();
}
}
},
template: function(){
return Handlebars.templates.basic_auth_button_view;
}
});

View File

@@ -0,0 +1,13 @@
'use strict';
SwaggerUi.Views.ContentTypeView = Backbone.View.extend({
initialize: function() {},
render: function(){
$(this.el).html(Handlebars.templates.content_type(this.model));
$('label[for=contentType]', $(this.el)).text('Response Content Type');
return this;
}
});

View File

@@ -0,0 +1,55 @@
'use strict';
SwaggerUi.Views.HeaderView = Backbone.View.extend({
events: {
'click #show-pet-store-icon' : 'showPetStore',
'click #show-wordnik-dev-icon' : 'showWordnikDev',
'click #explore' : 'showCustom',
'keyup #input_baseUrl' : 'showCustomOnKeyup',
'keyup #input_apiKey' : 'showCustomOnKeyup'
},
initialize: function(){},
showPetStore: function(){
this.trigger('update-swagger-ui', {
url:'http://petstore.swagger.wordnik.com/api/api-docs'
});
},
showWordnikDev: function(){
this.trigger('update-swagger-ui', {
url: 'http://api.wordnik.com/v4/resources.json'
});
},
showCustomOnKeyup: function(e){
if (e.keyCode === 13) {
this.showCustom();
}
},
showCustom: function(e){
if (e) {
e.preventDefault();
}
this.trigger('update-swagger-ui', {
url: $('#input_baseUrl').val(),
apiKey: $('#input_apiKey').val()
});
},
update: function(url, apiKey, trigger){
if (trigger === undefined) {
trigger = false;
}
$('#input_baseUrl').val(url);
//$('#input_apiKey').val(apiKey);
if (trigger) {
this.trigger('update-swagger-ui', {url:url});
}
}
});

View File

@@ -0,0 +1,139 @@
'use strict';
SwaggerUi.Views.MainView = Backbone.View.extend({
apisSorter : {
alpha : function(a,b){ return a.name.localeCompare(b.name); }
},
operationsSorters : {
alpha : function(a,b){ return a.path.localeCompare(b.path); },
method : function(a,b){ return a.method.localeCompare(b.method); }
},
initialize: function(opts){
var sorterOption, sorterFn, key, value;
opts = opts || {};
this.router = opts.router;
// Sort APIs
if (opts.swaggerOptions.apisSorter) {
sorterOption = opts.swaggerOptions.apisSorter;
if (_.isFunction(sorterOption)) {
sorterFn = sorterOption;
} else {
sorterFn = this.apisSorter[sorterOption];
}
if (_.isFunction(sorterFn)) {
this.model.apisArray.sort(sorterFn);
}
}
// Sort operations of each API
if (opts.swaggerOptions.operationsSorter) {
sorterOption = opts.swaggerOptions.operationsSorter;
if (_.isFunction(sorterOption)) {
sorterFn = sorterOption;
} else {
sorterFn = this.operationsSorters[sorterOption];
}
if (_.isFunction(sorterFn)) {
for (key in this.model.apisArray) {
this.model.apisArray[key].operationsArray.sort(sorterFn);
}
}
}
// set up the UI for input
this.model.auths = [];
for (key in this.model.securityDefinitions) {
value = this.model.securityDefinitions[key];
this.model.auths.push({
name: key,
type: value.type,
value: value
});
}
if (this.model.swaggerVersion === '2.0') {
if ('validatorUrl' in opts.swaggerOptions) {
// Validator URL specified explicitly
this.model.validatorUrl = opts.swaggerOptions.validatorUrl;
} else if (this.model.url.indexOf('localhost') > 0) {
// Localhost override
this.model.validatorUrl = null;
} else {
// Default validator
this.model.validatorUrl = 'http://online.swagger.io/validator';
}
}
},
render: function(){
if (this.model.securityDefinitions) {
for (var name in this.model.securityDefinitions) {
var auth = this.model.securityDefinitions[name];
var button;
if (auth.type === 'apiKey' && $('#apikey_button').length === 0) {
button = new SwaggerUi.Views.ApiKeyButton({model: auth, router: this.router}).render().el;
$('.auth_main_container').append(button);
}
if (auth.type === 'basicAuth' && $('#basic_auth_button').length === 0) {
button = new SwaggerUi.Views.BasicAuthButton({model: auth, router: this.router}).render().el;
$('.auth_main_container').append(button);
}
}
}
// Render the outer container for resources
$(this.el).html(Handlebars.templates.main(this.model));
// Render each resource
var resources = {};
var counter = 0;
for (var i = 0; i < this.model.apisArray.length; i++) {
var resource = this.model.apisArray[i];
var id = resource.name;
while (typeof resources[id] !== 'undefined') {
id = id + '_' + counter;
counter += 1;
}
resource.id = id;
resources[id] = resource;
this.addResource(resource, this.model.auths);
}
$('.propWrap').hover(function onHover(){
$('.optionsWrapper', $(this)).show();
}, function offhover(){
$('.optionsWrapper', $(this)).hide();
});
return this;
},
addResource: function(resource, auths){
// Render a resource and add it to resources li
resource.id = resource.id.replace(/\s/g, '_');
var resourceView = new SwaggerUi.Views.ResourceView({
model: resource,
router: this.router,
tagName: 'li',
id: 'resource_' + resource.id,
className: 'resource',
auths: auths,
swaggerOptions: this.options.swaggerOptions
});
$('#resources').append(resourceView.render().el);
},
clear: function(){
$(this.el).html('');
}
});

View File

@@ -0,0 +1,665 @@
'use strict';
SwaggerUi.Views.OperationView = Backbone.View.extend({
invocationUrl: null,
events: {
'submit .sandbox' : 'submitOperation',
'click .submit' : 'submitOperation',
'click .response_hider' : 'hideResponse',
'click .toggleOperation' : 'toggleOperationContent',
'mouseenter .api-ic' : 'mouseEnter',
'mouseout .api-ic' : 'mouseExit',
},
initialize: function(opts) {
opts = opts || {};
this.router = opts.router;
this.auths = opts.auths;
this.parentId = this.model.parentId;
this.nickname = this.model.nickname;
this.model.encodedParentId = encodeURIComponent(this.parentId);
return this;
},
mouseEnter: function(e) {
var elem = $(this.el).find('.content');
var x = e.pageX;
var y = e.pageY;
var scX = $(window).scrollLeft();
var scY = $(window).scrollTop();
var scMaxX = scX + $(window).width();
var scMaxY = scY + $(window).height();
var wd = elem.width();
var hgh = elem.height();
if (x + wd > scMaxX) {
x = scMaxX - wd;
}
if (x < scX) {
x = scX;
}
if (y + hgh > scMaxY) {
y = scMaxY - hgh;
}
if (y < scY) {
y = scY;
}
var pos = {};
pos.top = y;
pos.left = x;
elem.css(pos);
$(e.currentTarget.parentNode).find('#api_information_panel').show();
},
mouseExit: function(e) {
$(e.currentTarget.parentNode).find('#api_information_panel').hide();
},
// Note: copied from CoffeeScript compiled file
// TODO: redactor
render: function() {
var a, auth, auths, code, contentTypeModel, isMethodSubmissionSupported, k, key, l, len, len1, len2, len3, len4, m, modelAuths, n, o, p, param, q, ref, ref1, ref2, ref3, ref4, ref5, responseContentTypeView, responseSignatureView, schema, schemaObj, scopeIndex, signatureModel, statusCode, successResponse, type, v, value;
isMethodSubmissionSupported = jQuery.inArray(this.model.method, this.model.supportedSubmitMethods()) >= 0;
if (!isMethodSubmissionSupported) {
this.model.isReadOnly = true;
}
this.model.description = this.model.description || this.model.notes;
this.model.oauth = null;
modelAuths = this.model.authorizations || this.model.security;
if (modelAuths) {
if (Array.isArray(modelAuths)) {
for (l = 0, len = modelAuths.length; l < len; l++) {
auths = modelAuths[l];
for (key in auths) {
auth = auths[key];
for (a in this.auths) {
auth = this.auths[a];
if (auth.type === 'oauth2') {
this.model.oauth = {};
this.model.oauth.scopes = [];
ref1 = auth.value.scopes;
for (k in ref1) {
v = ref1[k];
scopeIndex = auths[key].indexOf(k);
if (scopeIndex >= 0) {
o = {
scope: k,
description: v
};
this.model.oauth.scopes.push(o);
}
}
}
}
}
}
} else {
for (k in modelAuths) {
v = modelAuths[k];
if (k === 'oauth2') {
if (this.model.oauth === null) {
this.model.oauth = {};
}
if (this.model.oauth.scopes === void 0) {
this.model.oauth.scopes = [];
}
for (m = 0, len1 = v.length; m < len1; m++) {
o = v[m];
this.model.oauth.scopes.push(o);
}
}
}
}
}
if (typeof this.model.responses !== 'undefined') {
this.model.responseMessages = [];
ref2 = this.model.responses;
for (code in ref2) {
value = ref2[code];
schema = null;
schemaObj = this.model.responses[code].schema;
if (schemaObj && schemaObj.$ref) {
schema = schemaObj.$ref;
if (schema.indexOf('#/definitions/') === 0) {
schema = schema.substring('#/definitions/'.length);
}
}
this.model.responseMessages.push({
code: code,
message: value.description,
responseModel: schema
});
}
}
if (typeof this.model.responseMessages === 'undefined') {
this.model.responseMessages = [];
}
signatureModel = null;
if (this.model.successResponse) {
successResponse = this.model.successResponse;
for (key in successResponse) {
value = successResponse[key];
this.model.successCode = key;
if (typeof value === 'object' && typeof value.createJSONSample === 'function') {
signatureModel = {
sampleJSON: JSON.stringify(value.createJSONSample(), void 0, 2),
isParam: false,
signature: value.getMockSignature()
};
}
}
} else if (this.model.responseClassSignature && this.model.responseClassSignature !== 'string') {
signatureModel = {
sampleJSON: this.model.responseSampleJSON,
isParam: false,
signature: this.model.responseClassSignature
};
}
$(this.el).html(Handlebars.templates.operation(this.model));
if (signatureModel) {
responseSignatureView = new SwaggerUi.Views.SignatureView({
model: signatureModel,
router: this.router,
tagName: 'div'
});
$('.model-signature', $(this.el)).append(responseSignatureView.render().el);
} else {
this.model.responseClassSignature = 'string';
$('.model-signature', $(this.el)).html(this.model.type);
}
contentTypeModel = {
isParam: false
};
contentTypeModel.consumes = this.model.consumes;
contentTypeModel.produces = this.model.produces;
ref3 = this.model.parameters;
for (n = 0, len2 = ref3.length; n < len2; n++) {
param = ref3[n];
type = param.type || param.dataType || '';
if (typeof type === 'undefined') {
schema = param.schema;
if (schema && schema.$ref) {
ref = schema.$ref;
if (ref.indexOf('#/definitions/') === 0) {
type = ref.substring('#/definitions/'.length);
} else {
type = ref;
}
}
}
if (type && type.toLowerCase() === 'file') {
if (!contentTypeModel.consumes) {
contentTypeModel.consumes = 'multipart/form-data';
}
}
param.type = type;
}
responseContentTypeView = new SwaggerUi.Views.ResponseContentTypeView({
model: contentTypeModel,
router: this.router
});
$('.response-content-type', $(this.el)).append(responseContentTypeView.render().el);
ref4 = this.model.parameters;
for (p = 0, len3 = ref4.length; p < len3; p++) {
param = ref4[p];
this.addParameter(param, contentTypeModel.consumes);
}
ref5 = this.model.responseMessages;
for (q = 0, len4 = ref5.length; q < len4; q++) {
statusCode = ref5[q];
this.addStatusCode(statusCode);
}
return this;
},
addParameter: function(param, consumes) {
// Render a parameter
param.consumes = consumes;
var paramView = new SwaggerUi.Views.ParameterView({
model: param,
tagName: 'tr',
readOnly: this.model.isReadOnly
});
$('.operation-params', $(this.el)).append(paramView.render().el);
},
addStatusCode: function(statusCode) {
// Render status codes
var statusCodeView = new SwaggerUi.Views.StatusCodeView({
model: statusCode,
tagName: 'tr',
router: this.router
});
$('.operation-status', $(this.el)).append(statusCodeView.render().el);
},
// Note: copied from CoffeeScript compiled file
// TODO: redactor
submitOperation: function(e) {
var error_free, form, isFileUpload, l, len, len1, len2, m, map, n, o, opts, ref1, ref2, ref3, val;
if (e !== null) {
e.preventDefault();
}
form = $('.sandbox', $(this.el));
error_free = true;
form.find('input.required').each(function() {
$(this).removeClass('error');
if (jQuery.trim($(this).val()) === '') {
$(this).addClass('error');
$(this).wiggle({
callback: (function(_this) {
return function() {
$(_this).focus();
};
})(this)
});
error_free = false;
}
});
form.find('textarea.required').each(function() {
$(this).removeClass('error');
if (jQuery.trim($(this).val()) === '') {
$(this).addClass('error');
$(this).wiggle({
callback: (function(_this) {
return function() {
return $(_this).focus();
};
})(this)
});
error_free = false;
}
});
if (error_free) {
map = {};
opts = {
parent: this
};
isFileUpload = false;
ref1 = form.find('input');
for (l = 0, len = ref1.length; l < len; l++) {
o = ref1[l];
if ((o.value !== null) && jQuery.trim(o.value).length > 0) {
map[o.name] = o.value;
}
if (o.type === 'file') {
map[o.name] = o.files[0];
isFileUpload = true;
}
}
ref2 = form.find('textarea');
for (m = 0, len1 = ref2.length; m < len1; m++) {
o = ref2[m];
if ((o.value !== null) && jQuery.trim(o.value).length > 0) {
map[o.name] = o.value;
}
}
ref3 = form.find('select');
for (n = 0, len2 = ref3.length; n < len2; n++) {
o = ref3[n];
val = this.getSelectedValue(o);
if ((val !== null) && jQuery.trim(val).length > 0) {
map[o.name] = val;
}
}
opts.responseContentType = $('div select[name=responseContentType]', $(this.el)).val();
opts.requestContentType = $('div select[name=parameterContentType]', $(this.el)).val();
$('.response_throbber', $(this.el)).show();
if (isFileUpload) {
return this.handleFileUpload(map, form);
} else {
return this.model['do'](map, opts, this.showCompleteStatus, this.showErrorStatus, this);
}
}
},
success: function(response, parent) {
parent.showCompleteStatus(response);
},
// Note: This is compiled code
// TODO: Refactor
handleFileUpload: function(map, form) {
var bodyParam, el, headerParams, l, len, len1, len2, len3, m, n, o, obj, p, param, params, ref1, ref2, ref3, ref4;
ref1 = form.serializeArray();
for (l = 0, len = ref1.length; l < len; l++) {
o = ref1[l];
if ((o.value !== null) && jQuery.trim(o.value).length > 0) {
map[o.name] = o.value;
}
}
bodyParam = new FormData();
params = 0;
ref2 = this.model.parameters;
for (m = 0, len1 = ref2.length; m < len1; m++) {
param = ref2[m];
if (param.paramType === 'form' || param['in'] === 'formData') {
if (param.type.toLowerCase() !== 'file' && map[param.name] !== void 0) {
bodyParam.append(param.name, map[param.name]);
}
}
}
headerParams = {};
ref3 = this.model.parameters;
for (n = 0, len2 = ref3.length; n < len2; n++) {
param = ref3[n];
if (param.paramType === 'header') {
headerParams[param.name] = map[param.name];
}
}
ref4 = form.find('input[type~="file"]');
for (p = 0, len3 = ref4.length; p < len3; p++) {
el = ref4[p];
if (typeof el.files[0] !== 'undefined') {
bodyParam.append($(el).attr('name'), el.files[0]);
params += 1;
}
}
this.invocationUrl = this.model.supportHeaderParams() ? (headerParams = this.model.getHeaderParams(map), delete headerParams['Content-Type'], this.model.urlify(map, false)) : this.model.urlify(map, true);
$('.request_url', $(this.el)).html('<pre></pre>');
$('.request_url pre', $(this.el)).text(this.invocationUrl);
obj = {
type: this.model.method,
url: this.invocationUrl,
headers: headerParams,
data: bodyParam,
dataType: 'json',
contentType: false,
processData: false,
error: (function(_this) {
return function(data) {
return _this.showErrorStatus(_this.wrap(data), _this);
};
})(this),
success: (function(_this) {
return function(data) {
return _this.showResponse(data, _this);
};
})(this),
complete: (function(_this) {
return function(data) {
return _this.showCompleteStatus(_this.wrap(data), _this);
};
})(this)
};
if (window.authorizations) {
window.authorizations.apply(obj);
}
jQuery.ajax(obj);
return false;
// end of file-upload nastiness
},
// wraps a jquery response as a shred response
wrap: function(data) {
var h, headerArray, headers, i, l, len, o;
headers = {};
headerArray = data.getAllResponseHeaders().split('\r');
for (l = 0, len = headerArray.length; l < len; l++) {
i = headerArray[l];
h = i.match(/^([^:]*?):(.*)$/);
if (!h) {
h = [];
}
h.shift();
if (h[0] !== void 0 && h[1] !== void 0) {
headers[h[0].trim()] = h[1].trim();
}
}
o = {};
o.content = {};
o.content.data = data.responseText;
o.headers = headers;
o.request = {};
o.request.url = this.invocationUrl;
o.status = data.status;
return o;
},
getSelectedValue: function(select) {
if (!select.multiple) {
return select.value;
} else {
var options = [];
for (var l = 0, len = select.options.length; l < len; l++) {
var opt = select.options[l];
if (opt.selected) {
options.push(opt.value);
}
}
if (options.length > 0) {
return options;
} else {
return null;
}
}
},
// handler for hide response link
hideResponse: function(e) {
if (e) { e.preventDefault(); }
$('.response', $(this.el)).slideUp();
$('.response_hider', $(this.el)).fadeOut();
},
// Show response from server
showResponse: function(response) {
var prettyJson = JSON.stringify(response, null, '\t').replace(/\n/g, '<br>');
$('.response_body', $(this.el)).html(_.escape(prettyJson));
},
// Show error from server
showErrorStatus: function(data, parent) {
parent.showStatus(data);
},
// show the status codes
showCompleteStatus: function(data, parent){
parent.showStatus(data);
},
// Adapted from http://stackoverflow.com/a/2893259/454004
// Note: directly ported from CoffeeScript
// TODO: Cleanup CoffeeScript artifacts
formatXml: function(xml) {
var contexp, fn, formatted, indent, l, lastType, len, lines, ln, pad, reg, transitions, wsexp;
reg = /(>)(<)(\/*)/g;
wsexp = /[ ]*(.*)[ ]+\n/g;
contexp = /(<.+>)(.+\n)/g;
xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
pad = 0;
formatted = '';
lines = xml.split('\n');
indent = 0;
lastType = 'other';
transitions = {
'single->single': 0,
'single->closing': -1,
'single->opening': 0,
'single->other': 0,
'closing->single': 0,
'closing->closing': -1,
'closing->opening': 0,
'closing->other': 0,
'opening->single': 1,
'opening->closing': 0,
'opening->opening': 1,
'opening->other': 1,
'other->single': 0,
'other->closing': -1,
'other->opening': 0,
'other->other': 0
};
fn = function(ln) {
var fromTo, j, key, padding, type, types, value;
types = {
single: Boolean(ln.match(/<.+\/>/)),
closing: Boolean(ln.match(/<\/.+>/)),
opening: Boolean(ln.match(/<[^!?].*>/))
};
type = ((function() {
var results;
results = [];
for (key in types) {
value = types[key];
if (value) {
results.push(key);
}
}
return results;
})())[0];
type = type === void 0 ? 'other' : type;
fromTo = lastType + '->' + type;
lastType = type;
padding = '';
indent += transitions[fromTo];
padding = ((function() {
var m, ref1, results;
results = [];
for (j = m = 0, ref1 = indent; 0 <= ref1 ? m < ref1 : m > ref1; j = 0 <= ref1 ? ++m : --m) {
results.push(' ');
}
return results;
})()).join('');
if (fromTo === 'opening->closing') {
formatted = formatted.substr(0, formatted.length - 1) + ln + '\n';
} else {
formatted += padding + ln + '\n';
}
};
for (l = 0, len = lines.length; l < len; l++) {
ln = lines[l];
fn(ln);
}
return formatted;
},
// puts the response data in UI
showStatus: function(response) {
var url, content;
if (response.content === undefined) {
content = response.data;
url = response.url;
} else {
content = response.content.data;
url = response.request.url;
}
var headers = response.headers;
// if server is nice, and sends content-type back, we can use it
var contentType = null;
if (headers) {
contentType = headers['Content-Type'] || headers['content-type'];
if (contentType) {
contentType = contentType.split(';')[0].trim();
}
}
$('.response_body', $(this.el)).removeClass('json');
$('.response_body', $(this.el)).removeClass('xml');
var supportsAudioPlayback = function(contentType){
var audioElement = document.createElement('audio');
return !!(audioElement.canPlayType && audioElement.canPlayType(contentType).replace(/no/, ''));
};
var pre;
var code;
if (!content) {
code = $('<code />').text('no content');
pre = $('<pre class="json" />').append(code);
// JSON
} else if (contentType === 'application/json' || /\+json$/.test(contentType)) {
var json = null;
try {
json = JSON.stringify(JSON.parse(content), null, ' ');
} catch (_error) {
json = 'can\'t parse JSON. Raw result:\n\n' + content;
}
code = $('<code />').text(json);
pre = $('<pre class="json" />').append(code);
// XML
} else if (contentType === 'application/xml' || /\+xml$/.test(contentType)) {
code = $('<code />').text(this.formatXml(content));
pre = $('<pre class="xml" />').append(code);
// HTML
} else if (contentType === 'text/html') {
code = $('<code />').html(_.escape(content));
pre = $('<pre class="xml" />').append(code);
// Image
} else if (/^image\//.test(contentType)) {
pre = $('<img>').attr('src', url);
// Audio
} else if (/^audio\//.test(contentType) && supportsAudioPlayback(contentType)) {
pre = $('<audio controls>').append($('<source>').attr('src', url).attr('type', contentType));
// Download
} else if (headers['Content-Disposition'].test(/attachment/) ||
headers['content-disposition'].test(/attachment/) ||
headers['Content-Description'].test(/File Transfer/) ||
headers['content-description'].test(/File Transfer/)) {
if ('Blob' in window) {
var type = contentType || 'text/html';
var blob = new Blob([content], {type: type});
var a = document.createElement('a');
var href = window.URL.createObjectURL(blob);
var fileName = response.url.substr(response.url.lastIndexOf('/') + 1);
var download = [type, fileName, href].join(':');
a.setAttribute('href', href);
a.setAttribute('download', download);
a.innerText = 'Download ' + fileName;
pre = $('<div/>').append(a);
} else {
pre = $('<pre class="json" />').append('Download headers detected but your browser does not support downloading binary via XHR (Blob).');
}
// Location header based redirect download
} else if(headers.location || headers.Location) {
window.location = response.url;
// Anything else (CORS)
} else {
code = $('<code />').text(content);
pre = $('<pre class="json" />').append(code);
}
var response_body = pre;
$('.request_url', $(this.el)).html('<pre></pre>');
$('.request_url pre', $(this.el)).text(url);
$('.response_code', $(this.el)).html('<pre>' + response.status + '</pre>');
$('.response_body', $(this.el)).html(response_body);
$('.response_headers', $(this.el)).html('<pre>' + _.escape(JSON.stringify(response.headers, null, ' ')).replace(/\n/g, '<br>') + '</pre>');
$('.response', $(this.el)).slideDown();
$('.response_hider', $(this.el)).show();
$('.response_throbber', $(this.el)).hide();
var response_body_el = $('.response_body', $(this.el))[0];
// only highlight the response if response is less than threshold, default state is highlight response
var opts = this.options.swaggerOptions;
if (opts.highlightSizeThreshold && response.data.length > opts.highlightSizeThreshold) {
return response_body_el;
} else {
return hljs.highlightBlock(response_body_el);
}
},
toggleOperationContent: function() {
var elem = $('#' + Docs.escapeResourceName(this.parentId + '_' + this.nickname + '_content'));
if (elem.is(':visible')){
Docs.collapseOperation(elem);
} else {
Docs.expandOperation(elem);
}
}
});

View File

@@ -0,0 +1,14 @@
'use strict';
SwaggerUi.Views.ParameterContentTypeView = Backbone.View.extend({
initialize: function () {},
render: function(){
$(this.el).html(Handlebars.templates.parameter_content_type(this.model));
$('label[for=parameterContentType]', $(this.el)).text('Parameter content type:');
return this;
}
});

View File

@@ -0,0 +1,101 @@
'use strict';
SwaggerUi.Views.ParameterView = Backbone.View.extend({
initialize: function(){
Handlebars.registerHelper('isArray', function(param, opts) {
if (param.type.toLowerCase() === 'array' || param.allowMultiple) {
opts.fn(this);
} else {
opts.inverse(this);
}
});
},
render: function() {
var type = this.model.type || this.model.dataType;
if (typeof type === 'undefined') {
var schema = this.model.schema;
if (schema && schema.$ref) {
var ref = schema.$ref;
if (ref.indexOf('#/definitions/') === 0) {
type = ref.substring('#/definitions/'.length);
} else {
type = ref;
}
}
}
this.model.type = type;
this.model.paramType = this.model.in || this.model.paramType;
this.model.isBody = this.model.paramType === 'body' || this.model.in === 'body';
this.model.isFile = type && type.toLowerCase() === 'file';
this.model.default = (this.model.default || this.model.defaultValue);
if (this.model.allowableValues) {
this.model.isList = true;
}
var template = this.template();
$(this.el).html(template(this.model));
var signatureModel = {
sampleJSON: this.model.sampleJSON,
isParam: true,
signature: this.model.signature
};
if (this.model.sampleJSON) {
var signatureView = new SwaggerUi.Views.SignatureView({model: signatureModel, tagName: 'div'});
$('.model-signature', $(this.el)).append(signatureView.render().el);
}
else {
$('.model-signature', $(this.el)).html(this.model.signature);
}
var isParam = false;
if (this.model.isBody) {
isParam = true;
}
var contentTypeModel = {
isParam: isParam
};
contentTypeModel.consumes = this.model.consumes;
if (isParam) {
var parameterContentTypeView = new SwaggerUi.Views.ParameterContentTypeView({model: contentTypeModel});
$('.parameter-content-type', $(this.el)).append(parameterContentTypeView.render().el);
}
else {
var responseContentTypeView = new SwaggerUi.Views.ResponseContentTypeView({model: contentTypeModel});
$('.response-content-type', $(this.el)).append(responseContentTypeView.render().el);
}
return this;
},
// Return an appropriate template based on if the parameter is a list, readonly, required
template: function(){
if (this.model.isList) {
return Handlebars.templates.param_list;
} else {
if (this.options.readOnly) {
if (this.model.required) {
return Handlebars.templates.param_readonly_required;
} else {
return Handlebars.templates.param_readonly;
}
} else {
if (this.model.required) {
return Handlebars.templates.param_required;
} else {
return Handlebars.templates.param;
}
}
}
}
});

View File

@@ -0,0 +1,73 @@
'use strict';
SwaggerUi.Views.ResourceView = Backbone.View.extend({
initialize: function(opts) {
opts = opts || {};
this.router = opts.router;
this.auths = opts.auths;
if ('' === this.model.description) {
this.model.description = null;
}
if (this.model.description) {
this.model.summary = this.model.description;
}
},
render: function(){
var methods = {};
$(this.el).html(Handlebars.templates.resource(this.model));
// Render each operation
for (var i = 0; i < this.model.operationsArray.length; i++) {
var operation = this.model.operationsArray[i];
var counter = 0;
var id = operation.nickname;
while (typeof methods[id] !== 'undefined') {
id = id + '_' + counter;
counter += 1;
}
methods[id] = operation;
operation.nickname = id;
operation.parentId = this.model.id;
this.addOperation(operation);
}
$('.toggleEndpointList', this.el).click(this.callDocs.bind(this, 'toggleEndpointListForResource'));
$('.collapseResource', this.el).click(this.callDocs.bind(this, 'collapseOperationsForResource'));
$('.expandResource', this.el).click(this.callDocs.bind(this, 'expandOperationsForResource'));
return this;
},
addOperation: function(operation) {
operation.number = this.number;
// Render an operation and add it to operations li
var operationView = new SwaggerUi.Views.OperationView({
model: operation,
router: this.router,
tagName: 'li',
className: 'endpoint',
swaggerOptions: this.options.swaggerOptions,
auths: this.auths
});
$('.endpoints', $(this.el)).append(operationView.render().el);
this.number++;
},
// Generic Event handler (`Docs` is global)
callDocs: function(fnName, e) {
e.preventDefault();
Docs[fnName](e.currentTarget.getAttribute('data-id'));
}
});

View File

@@ -0,0 +1,13 @@
'use strict';
SwaggerUi.Views.ResponseContentTypeView = Backbone.View.extend({
initialize: function(){},
render: function(){
$(this.el).html(Handlebars.templates.response_content_type(this.model));
$('label[for=responseContentType]', $(this.el)).text('Response Content Type');
return this;
}
});

View File

@@ -0,0 +1,60 @@
'use strict';
SwaggerUi.Views.SignatureView = Backbone.View.extend({
events: {
'click a.description-link' : 'switchToDescription',
'click a.snippet-link' : 'switchToSnippet',
'mousedown .snippet' : 'snippetToTextArea'
},
initialize: function () {
},
render: function(){
$(this.el).html(Handlebars.templates.signature(this.model));
this.switchToSnippet();
this.isParam = this.model.isParam;
if (this.isParam) {
$('.notice', $(this.el)).text('Click to set as parameter value');
}
return this;
},
// handler for show signature
switchToDescription: function(e){
if (e) { e.preventDefault(); }
$('.snippet', $(this.el)).hide();
$('.description', $(this.el)).show();
$('.description-link', $(this.el)).addClass('selected');
$('.snippet-link', $(this.el)).removeClass('selected');
},
// handler for show sample
switchToSnippet: function(e){
if (e) { e.preventDefault(); }
$('.description', $(this.el)).hide();
$('.snippet', $(this.el)).show();
$('.snippet-link', $(this.el)).addClass('selected');
$('.description-link', $(this.el)).removeClass('selected');
},
// handler for snippet to text area
snippetToTextArea: function(e) {
if (this.isParam) {
if (e) { e.preventDefault(); }
var textArea = $('textarea', $(this.el.parentNode.parentNode.parentNode));
if ($.trim(textArea.val()) === '') {
textArea.val(this.model.sampleJSON);
}
}
}
});

View File

@@ -0,0 +1,26 @@
'use strict';
SwaggerUi.Views.StatusCodeView = Backbone.View.extend({
initialize: function (opts) {
this.options = opts || {};
this.router = this.options.router;
},
render: function(){
$(this.el).html(Handlebars.templates.status_code(this.model));
if (this.router.api.models.hasOwnProperty(this.model.responseModel)) {
var responseModel = {
sampleJSON: JSON.stringify(this.router.api.models[this.model.responseModel].createJSONSample(), null, 2),
isParam: false,
signature: this.router.api.models[this.model.responseModel].getMockSignature(),
};
var responseModelView = new SwaggerUi.Views.SignatureView({model: responseModel, tagName: 'div'});
$('.model-signature', this.$el).append(responseModelView.render().el);
} else {
$('.model-signature', this.$el).html('');
}
return this;
}
});

41
src/main/less/print.less Normal file
View File

@@ -0,0 +1,41 @@
@import 'src/main/less/highlight_default.less';
@import 'src/main/less/specs.less';
@import 'src/main/less/auth.less';
#header {
display: none;
}
.swagger-section {
.swagger-ui-wrap {
.model-signature pre {
max-height: none;
}
.body-textarea {
width: 100px;
}
input.parameter {
width: 100px;
}
ul#resources {
li.resource {
div.heading ul.options {
display: none;
}
ul.endpoints {
display: block !important;
li.endpoint ul.operations li.operation div.content {
display: block !important;
}
}
}
}
}
}

View File

@@ -2,6 +2,11 @@
{{#if info}}
<div class="info_title">{{info.title}}</div>
<div class="info_description markdown">{{{info.description}}}</div>
{{#if externalDocs}}
<h5>More documentations</h5>
<p>{{externalDocs.description}}</p>
<a href="{{externalDocs.url}}" target="_blank">{{externalDocs.url}}</a>
{{/if}}
{{#if info.termsOfServiceUrl}}<div class="info_tos"><a href="{{info.termsOfServiceUrl}}">Terms of service</a></div>{{/if}}
{{#if info.contact.name}}<div class='info_name'>Created by {{info.contact.name}}</div>{{/if}}
{{#if info.contact.url}}<div class='info_url'>See more at <a href="{{info.contact.url}}">{{info.contact.url}}</a></div>{{/if}}

View File

@@ -4,15 +4,15 @@
<div class='heading'>
<h3>
<span class='http_method'>
<a href='#!/{{parentId}}/{{nickname}}' class="toggleOperation">{{method}}</a>
<a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation">{{method}}</a>
</span>
<span class='path'>
<a href='#!/{{parentId}}/{{nickname}}' class="toggleOperation {{#if deprecated}}deprecated{{/if}}">{{path}}</a>
<a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation {{#if deprecated}}deprecated{{/if}}">{{path}}</a>
</span>
</h3>
<ul class='options'>
<li>
<a href='#!/{{parentId}}/{{nickname}}' class="toggleOperation">{{{summary}}}</a>
<a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation">{{{summary}}}</a>
</li>
</ul>
</div>
@@ -22,7 +22,7 @@
{{/if}}
{{#if description}}
<h4>Implementation Notes</h4>
<p class="markdown">{{{description}}}</p>
<div class="markdown">{{{description}}}</div>
{{/if}}
{{#oauth}}
<div class="auth">
@@ -74,10 +74,11 @@
<th>HTTP Status Code</th>
<th>Reason</th>
<th>Response Model</th>
<th>Headers</th>
</tr>
</thead>
<tbody class="operation-status">
</tbody>
</table>
{{/if}}

View File

@@ -1,3 +1,6 @@
{{#if required}}
<td class='code required'>{{name}}</td>
{{/if}}
<td class='code'>{{name}}</td>
<td>
<select {{#isArray this}} multiple='multiple'{{/isArray}} class='parameter' name='{{name}}'>

View File

@@ -16,9 +16,11 @@
Expand Operations
</a>
</li>
{{#url}}<li>
{{#if url}}
<li>
<a href='{{url}}'>Raw</a>
</li>{{/url}}
</li>
{{/if}}
</ul>
</div>
<ul class='endpoints' id='{{id}}_endpoint_list' style='display:none'>

View File

@@ -1,3 +1,16 @@
<td width='15%' class='code'>{{code}}</td>
<td>{{{message}}}</td>
<td width='50%'><span class="model-signature" /></td>
<td width='50%'><span class="model-signature" /></td>
<td class="headers">
<table>
<tbody>
{{#each headers}}
<tr>
<td>{{@key}}</td>
<td>{{this.description}}</td>
<td>{{this.type}}</td>
</tr>
{{/each}}
</tbody>
</table>
</td>

10
test/.jshintrc Normal file
View File

@@ -0,0 +1,10 @@
{
"extends": "../.jshintrc",
"expr": true,
"jasmine": true,
"globals": {
"before": false,
"after": false,
"expect": true
}
}

10
test/e2e/driver.js Normal file
View File

@@ -0,0 +1,10 @@
/*
* Web driver manager
*/
'use strict';
var webdriver = require('selenium-webdriver');
var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.firefox()).build();
module.exports = driver;

38
test/e2e/servers.js Normal file
View File

@@ -0,0 +1,38 @@
/*
* Swagger UI and Specs Servers
*/
'use strict';
var path = require('path');
var createServer = require('http-server').createServer;
var dist = path.join(__dirname, '..', '..', 'dist');
var specs = path.join(__dirname, '..', '..', 'test', 'specs');
var DOCS_PORT = 8080;
var SPEC_SERVER_PORT = 8081;
var driver = require('./driver');
var swaggerUI;
var specServer;
module.exports.start = function (specsLocation, done) {
swaggerUI = createServer({ root: dist, cors: true });
specServer = createServer({ root: specs, cors: true });
swaggerUI.listen(DOCS_PORT);
specServer.listen(SPEC_SERVER_PORT);
var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + specsLocation);
var url = 'http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation;
setTimeout(function(){
driver.get(url);
done();
}, process.env.TRAVIS ? 20000 : 3000);
};
module.exports.close = function() {
swaggerUI.close();
specServer.close();
};

View File

@@ -1,17 +1,9 @@
var webdriver = require('selenium-webdriver');
var createServer = require('http-server').createServer;
'use strict';
var expect = require('chai').expect;
var path = require('path')
var dist = path.join(__dirname, '..', '..', 'dist');
var specs = path.join(__dirname, '..', '..', 'test', 'specs');
var DOCS_PORT = 8080;
var SPEC_SERVER_PORT = 8081
var headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
};
var driver = require('./driver');
var servers = require('./servers');
var webdriver = require('selenium-webdriver');
var elements = [
'swagger-ui-container',
@@ -23,21 +15,12 @@ var elements = [
'header'
];
describe('swagger 1.x spec tests', function (done) {
describe('swagger 1.x spec tests', function () {
this.timeout(10 * 1000);
var swaggerUI, specServer, driver;
before(function () {
swaggerUI = createServer({ root: dist, headers: headers });
specServer = createServer({ root: specs, headers: headers });
driver = new webdriver.Builder().
withCapabilities(webdriver.Capabilities.firefox()).build();
swaggerUI.listen(DOCS_PORT);
specServer.listen(SPEC_SERVER_PORT);
var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + '/v1.2/petstore/api-docs')
driver.get('http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation);
before(function (done) {
this.timeout(25 * 1000);
servers.start('/v1.2/petstore/api-docs.json', done);
});
afterEach(function(){
@@ -46,8 +29,9 @@ describe('swagger 1.x spec tests', function (done) {
var errors = [];
browserLogs.forEach(function(log){
// 900 and above is "error" level. Console should not have any errors
if (log.level.value > 900)
if (log.level.value > 900) {
console.log('browser error message:', log.message); errors.push(log);
}
});
expect(errors).to.be.empty;
done();
@@ -65,7 +49,7 @@ describe('swagger 1.x spec tests', function (done) {
elements.forEach(function (id) {
it('should render element: ' + id, function (done) {
var locator = webdriver.By.id(id)
var locator = webdriver.By.id(id);
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
done();
@@ -73,7 +57,8 @@ describe('swagger 1.x spec tests', function (done) {
});
});
it('should find the contact name element', function(done){
// TODO: enable me
xit('should find the contact name element', function(done){
var locator = webdriver.By.css('.info_name');
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
@@ -82,15 +67,16 @@ describe('swagger 1.x spec tests', function (done) {
});
it('should find the pet link', function(done){
var locator = webdriver.By.xpath("//*[@data-id='pet']");
var locator = webdriver.By.xpath('//*[@data-id="pet"]');
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
done();
});
});
it('should find the pet resource description', function(done){
var locator = webdriver.By.xpath("//div[contains(., 'Operations about pets')]");
// TODO: enable me
xit('should find the pet resource description', function(done){
var locator = webdriver.By.xpath('//div[contains(., "Operations about pets")]');
driver.findElements(locator).then(function (elements) {
expect(elements.length).to.not.equal(0);
done();
@@ -98,7 +84,7 @@ describe('swagger 1.x spec tests', function (done) {
});
it('should find the user link', function(done){
var locator = webdriver.By.xpath("//*[@data-id='user']");
var locator = webdriver.By.xpath('//*[@data-id="user"]');
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
done();
@@ -106,16 +92,14 @@ describe('swagger 1.x spec tests', function (done) {
});
it('should find the store link', function(done){
var locator = webdriver.By.xpath("//*[@data-id='store']");
var locator = webdriver.By.xpath('//*[@data-id="store"]');
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
done();
});
});
after(function() {
swaggerUI.close();
specServer.close();
driver.quit();
after(function(){
servers.close();
});
});

View File

@@ -1,17 +1,10 @@
var webdriver = require('selenium-webdriver');
var createServer = require('http-server').createServer;
'use strict';
var expect = require('chai').expect;
var path = require('path')
var webdriver = require('selenium-webdriver');
var driver = require('./driver');
var servers = require('./servers');
var dist = path.join(__dirname, '..', '..', 'dist');
var specs = path.join(__dirname, '..', '..', 'test', 'specs');
var DOCS_PORT = 8080;
var SPEC_SERVER_PORT = 8081
var headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
};
var elements = [
'swagger-ui-container',
@@ -23,21 +16,12 @@ var elements = [
'header'
];
describe('swagger 2.0 spec tests', function (done) {
describe('swagger 2.0 spec tests', function () {
this.timeout(10 * 1000);
var swaggerUI, specServer, driver;
before(function () {
swaggerUI = createServer({ root: dist, headers: headers });
specServer = createServer({ root: specs, headers: headers });
driver = new webdriver.Builder().
withCapabilities(webdriver.Capabilities.firefox()).build();
swaggerUI.listen(DOCS_PORT);
specServer.listen(SPEC_SERVER_PORT);
var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + '/v2/petstore.json')
driver.get('http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation);
before(function (done) {
this.timeout(25 * 1000);
servers.start('/v2/petstore.json', done);
});
afterEach(function(){
@@ -46,8 +30,9 @@ describe('swagger 2.0 spec tests', function (done) {
var errors = [];
browserLogs.forEach(function(log){
// 900 and above is "error" level. Console should not have any errors
if (log.level.value > 900)
if (log.level.value > 900) {
console.log('browser error message:', log.message); errors.push(log);
}
});
expect(errors).to.be.empty;
done();
@@ -98,7 +83,7 @@ describe('swagger 2.0 spec tests', function (done) {
});
it('should find the pet link', function(done){
var locator = webdriver.By.xpath("//*[@data-id='pet']");
var locator = webdriver.By.xpath('//*[@data-id="pet"]');
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
done();
@@ -106,7 +91,7 @@ describe('swagger 2.0 spec tests', function (done) {
});
it('should find the pet resource description', function(done){
var locator = webdriver.By.xpath("//div[contains(., 'Everything about your Pets')]");
var locator = webdriver.By.xpath('//div[contains(., "Everything about your Pets")]');
driver.findElements(locator).then(function (elements) {
expect(elements.length).to.not.equal(0);
done();
@@ -114,7 +99,7 @@ describe('swagger 2.0 spec tests', function (done) {
});
it('should find the user link', function(done){
var locator = webdriver.By.xpath("//*[@data-id='user']");
var locator = webdriver.By.xpath('//*[@data-id="user"]');
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
done();
@@ -122,7 +107,7 @@ describe('swagger 2.0 spec tests', function (done) {
});
it('should find the store link', function(done){
var locator = webdriver.By.xpath("//*[@data-id='store']");
var locator = webdriver.By.xpath('//*[@data-id="store"]');
driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true;
done();
@@ -130,8 +115,6 @@ describe('swagger 2.0 spec tests', function (done) {
});
after(function() {
swaggerUI.close();
specServer.close();
driver.quit();
servers.close();
});
});

View File

@@ -1 +1 @@
--recursive
--recursive --timeout 5000

View File

@@ -3,15 +3,15 @@
"swaggerVersion": "1.2",
"apis": [
{
"path": "http://localhost:8081/v1.2/petstore/pet",
"path": "http://localhost:8081/v1.2/petstore/pet.json",
"description": "Operations about pets"
},
{
"path": "http://localhost:8081/v1.2/petstore/user",
"path": "http://localhost:8081/v1.2/petstore/user.json",
"description": "Operations about user"
},
{
"path": "http://localhost:8081/v1.2/petstore/store",
"path": "http://localhost:8081/v1.2/petstore/store.json",
"description": "Operations about store"
}
],
@@ -31,18 +31,18 @@
"grantTypes": {
"implicit": {
"loginEndpoint": {
"url": "http://petstore.swagger.wordnik.com/oauth/dialog"
"url": "http://petstore.swagger.io/oauth/dialog"
},
"tokenName": "access_token"
},
"authorization_code": {
"tokenRequestEndpoint": {
"url": "http://petstore.swagger.wordnik.com/oauth/requestToken",
"url": "http://petstore.swagger.io/oauth/requestToken",
"clientIdName": "client_id",
"clientSecretName": "client_secret"
},
"tokenEndpoint": {
"url": "http://petstore.swagger.wordnik.com/oauth/token",
"url": "http://petstore.swagger.io/oauth/token",
"tokenName": "access_code"
}
}
@@ -51,7 +51,7 @@
},
"info": {
"title": "Swagger Sample App",
"description": "This is a sample server Petstore server. You can find out more about Swagger \n at <a href=\"http://swagger.wordnik.com\">http://swagger.wordnik.com</a> or on irc.freenode.net, #swagger. For this sample,\n you can use the api key \"special-key\" to test the authorization filters",
"description": "This is a sample server Petstore server. You can find out more about Swagger \n at <a href=\"http://swagger.io\">http://swagger.io</a> or on irc.freenode.net, #swagger. For this sample,\n you can use the api key \"special-key\" to test the authorization filters",
"termsOfServiceUrl": "http://helloreverb.com/terms/",
"contact": "apiteam@wordnik.com",
"license": "Apache 2.0",

View File

@@ -1,7 +1,7 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://petstore.swagger.wordnik.com/api",
"basePath": "http://petstore.swagger.io/api",
"resourcePath": "/pet",
"produces": [
"application/json",

View File

@@ -1,7 +1,7 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://petstore.swagger.wordnik.com/api",
"basePath": "http://petstore.swagger.io/api",
"resourcePath": "/store",
"produces": [
"application/json"

View File

@@ -1,7 +1,7 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://petstore.swagger.wordnik.com/api",
"basePath": "http://petstore.swagger.io/api",
"resourcePath": "/user",
"produces": [
"application/json"

View File

@@ -1,7 +1,7 @@
{
"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",
"description": "This is a sample server Petstore server. You can find out more about Swagger at <a href=\"http://swagger.io\">http://swagger.io</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 Petstore",
"termsOfService": "http://helloreverb.com/terms/",
@@ -15,7 +15,7 @@
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"host": "petstore.swagger.wordnik.com",
"host": "petstore.swagger.io",
"basePath": "/v2",
"schemes": [
"http"
@@ -742,7 +742,7 @@
},
"petstore_auth": {
"type": "oauth2",
"authorizationUrl": "http://petstore.swagger.wordnik.com/api/oauth/dialog",
"authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog",
"flow": "implicit"
}
},