Merge branch 'develop_2.0' into JSONEditor

Conflicts:
	dist/index.html
	dist/swagger-ui.js
	dist/swagger-ui.min.js
	src/main/coffeescript/view/MainView.coffee
	src/main/coffeescript/view/OperationView.coffee
	src/main/coffeescript/view/ParameterView.coffee
	src/main/coffeescript/view/ResourceView.coffee
	src/main/coffeescript/view/SignatureView.coffee
	src/main/html/index.html
This commit is contained in:
laurent lepinay
2015-03-24 07:06:07 -07:00
76 changed files with 34309 additions and 8132 deletions

3
.gitattributes vendored Normal file
View File

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

2
.gitignore vendored
View File

@@ -2,6 +2,8 @@
*.ipr *.ipr
*.iml *.iml
*.iws *.iws
*.flags.json
npm-debug.log
web/ web/
lib/*.zip lib/*.zip
version.properties version.properties

4
.jshintignore Normal file
View File

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

37
.jshintrc Normal file
View File

@@ -0,0 +1,37 @@
{
"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,
"globals": {
// Libraries
"_": false,
"$": false,
"Backbone": false,
"Handlebars": false,
"jQuery": false,
"marked": false,
"SwaggerClient": false,
"hljs": false,
"SwaggerUi": false,
// Global object
// TODO: remove these
"Docs": false
}
}

View File

@@ -5,4 +5,9 @@ node_js:
install: install:
- export DISPLAY=:99.0 - export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start - sh -e /etc/init.d/xvfb start
- npm i -g jshint
- npm install - npm install
script:
- jshint .
- npm test

View File

@@ -30,6 +30,9 @@ Swagger UI Version | Release Date | Swagger Spec compatibility | Notes | Status
### Download ### 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. 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 ### 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: 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:
@@ -70,12 +73,12 @@ You may choose to customize Swagger UI for your organization. Here is an overvie
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: 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 ```javascript
window.swaggerUi = new SwaggerUi({ var swaggerUi = new SwaggerUi({
url:"http://petstore.swagger.io/v2/swagger.json", url:"http://petstore.swagger.io/v2/swagger.json",
dom_id:"swagger-ui-container" dom_id:"swagger-ui-container"
}); });
window.swaggerUi.load(); swaggerUi.load();
``` ```
##### Parameters ##### Parameters
@@ -105,11 +108,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: 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 ```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() { $('#input_apiKey').change(function() {
var key = $('#input_apiKey')[0].value; var key = $('#input_apiKey')[0].value;
if(key && key.trim() != "") { 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"));
} }
}) })
``` ```
@@ -120,15 +123,15 @@ 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: If you have some header parameters which you need to send with every request, use the headers as below:
```js ```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). Note! You can pass multiple header params on a single request, just use unique names for them (`key` is used in the above example).
### Localisation and translation ### Localization and translation
The localisation files are in the dist/lang directory. 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) To enable translation you should append next two lines in your Swagger's index.html (or another entry point you use)
```html ```html
<script src='lang/translator.js' type='text/javascript'></script> <script src='lang/translator.js' type='text/javascript'></script>
<script src='lang/en.js' type='text/javascript'></script> <script src='lang/en.js' type='text/javascript'></script>
@@ -217,8 +220,6 @@ 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). 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 ## Change Log
Plsee see [releases](https://github.com/swagger-api/swagger-ui/releases) for change log. Plsee see [releases](https://github.com/swagger-api/swagger-ui/releases) for change log.

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

18
dist/index.html vendored
View File

@@ -1,7 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Swagger UI</title> <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/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/> <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/> <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
@@ -58,17 +61,16 @@
}); });
function addApiKeyAuthorization() { function addApiKeyAuthorization() {
var key = $('#input_apiKey')[0].value; var key = encodeURIComponent($('#input_apiKey')[0].value);
log("key: " + key); log("key: " + key);
if(key && key.trim() != "") { 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); log("added key " + key);
window.authorizations.add("api_key", new ApiKeyAuthorization("api_key", key, "query"));
} }
} }
$('#input_apiKey').change(function() { $('#input_apiKey').change(addApiKeyAuthorization);
addApiKeyAuthorization();
});
// if you have an apiKey you would like to pre-populate on the page for demonstration purposes... // if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
/* /*
@@ -78,6 +80,12 @@
*/ */
window.swaggerUi.load(); window.swaggerUi.load();
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
}); });
</script> </script>
</head> </head>

File diff suppressed because one or more lines are too long

View File

@@ -202,7 +202,7 @@ function initOAuth(opts) {
}); });
} }
function processOAuthCode(data) { window.processOAuthCode = function processOAuthCode(data) {
var params = { var params = {
'client_id': clientId, 'client_id': clientId,
'code': data.code, 'code': data.code,
@@ -225,7 +225,7 @@ function processOAuthCode(data) {
}); });
} }
function onOAuthComplete(token) { window.onOAuthComplete = function onOAuthComplete(token) {
if(token) { if(token) {
if(token.error) { if(token.error) {
var checkbox = $('input[type=checkbox],.secured') var checkbox = $('input[type=checkbox],.secured')

1641
dist/swagger-ui.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -2,9 +2,7 @@
var gulp = require('gulp'); var gulp = require('gulp');
var es = require('event-stream'); var es = require('event-stream');
var gutil = require('gulp-util');
var clean = require('gulp-clean'); var clean = require('gulp-clean');
var coffee = require('gulp-coffee');
var concat = require('gulp-concat'); var concat = require('gulp-concat');
var uglify = require('gulp-uglify'); var uglify = require('gulp-uglify');
var rename = require('gulp-rename'); var rename = require('gulp-rename');
@@ -32,7 +30,7 @@ gulp.task('clean', function() {
return gulp return gulp
.src('./dist', {read: false}) .src('./dist', {read: false})
.pipe(clean({force: true})) .pipe(clean({force: true}))
.on('error', gutil.log); .on('error', log);
}); });
/** /**
@@ -47,17 +45,7 @@ function templates() {
namespace: 'Handlebars.templates', namespace: 'Handlebars.templates',
noRedeclare: true, // Avoid duplicate declarations noRedeclare: true, // Avoid duplicate declarations
})) }))
.on('error', gutil.log); .on('error', log);
}
/**
* Processes CoffeeScript files
*/
function coffeescript () {
return gulp
.src(['./src/main/coffeescript/**/*.coffee'])
.pipe(coffee({bare: true}))
.on('error', gutil.log);
} }
/** /**
@@ -66,17 +54,18 @@ function coffeescript () {
gulp.task('dist', ['clean'], function() { gulp.task('dist', ['clean'], function() {
return es.merge( return es.merge(
gulp.src('./src/main/javascript/doc.js'), gulp.src('./src/main/javascript/**/*.js'),
coffeescript(),
templates() templates()
) )
.pipe(order(['docs.js', 'scripts.js', 'templates.js'])) .pipe(order(['scripts.js', 'templates.js']))
.pipe(concat('swagger-ui.js')) .pipe(concat('swagger-ui.js'))
.pipe(wrap('(function(){<%= contents %>}).call(this);'))
.pipe(header(banner, { pkg: pkg } )) .pipe(header(banner, { pkg: pkg } ))
.pipe(gulp.dest('./dist')) .pipe(gulp.dest('./dist'))
.pipe(uglify()) .pipe(uglify())
.on('error', log)
.pipe(rename({extname: '.min.js'})) .pipe(rename({extname: '.min.js'}))
.on('error', gutil.log) .on('error', log)
.pipe(gulp.dest('./dist')) .pipe(gulp.dest('./dist'))
.pipe(connect.reload()); .pipe(connect.reload());
}); });
@@ -93,7 +82,7 @@ gulp.task('less', ['clean'], function() {
'./src/main/less/reset.less' './src/main/less/reset.less'
]) ])
.pipe(less()) .pipe(less())
.on('error', gutil.log) .on('error', log)
.pipe(gulp.dest('./src/main/html/css/')) .pipe(gulp.dest('./src/main/html/css/'))
.pipe(connect.reload()); .pipe(connect.reload());
}); });
@@ -108,13 +97,13 @@ gulp.task('copy', ['less'], function() {
gulp gulp
.src(['./lib/**/*.{js,map}']) .src(['./lib/**/*.{js,map}'])
.pipe(gulp.dest('./dist/lib')) .pipe(gulp.dest('./dist/lib'))
.on('error', gutil.log) .on('error', log);
// copy all files inside html folder // copy all files inside html folder
gulp gulp
.src(['./src/main/html/**/*']) .src(['./src/main/html/**/*'])
.pipe(gulp.dest('./dist')) .pipe(gulp.dest('./dist'))
.on('error', gutil.log) .on('error', log);
}); });
/** /**
@@ -136,6 +125,10 @@ gulp.task('connect', function() {
}); });
}); });
function log(error) {
console.error(error.toString && error.toString());
}
gulp.task('default', ['dist', 'copy']); 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

View File

@@ -202,7 +202,7 @@ function initOAuth(opts) {
}); });
} }
function processOAuthCode(data) { window.processOAuthCode = function processOAuthCode(data) {
var params = { var params = {
'client_id': clientId, 'client_id': clientId,
'code': data.code, 'code': data.code,
@@ -225,7 +225,7 @@ function processOAuthCode(data) {
}); });
} }
function onOAuthComplete(token) { window.onOAuthComplete = function onOAuthComplete(token) {
if(token) { if(token) {
if(token.error) { if(token.error) {
var checkbox = $('input[type=checkbox],.secured') var checkbox = $('input[type=checkbox],.secured')

View File

@@ -29,7 +29,6 @@
"express": "^4.12.0", "express": "^4.12.0",
"gulp": "^3.8.11", "gulp": "^3.8.11",
"gulp-clean": "^0.3.1", "gulp-clean": "^0.3.1",
"gulp-coffee": "^2.3.1",
"gulp-concat": "^2.5.2", "gulp-concat": "^2.5.2",
"gulp-connect": "^2.2.0", "gulp-connect": "^2.2.0",
"gulp-declare": "^0.3.0", "gulp-declare": "^0.3.0",
@@ -39,10 +38,9 @@
"gulp-order": "^1.1.1", "gulp-order": "^1.1.1",
"gulp-rename": "^1.2.0", "gulp-rename": "^1.2.0",
"gulp-uglify": "^1.1.0", "gulp-uglify": "^1.1.0",
"gulp-util": "^3.0.4",
"gulp-watch": "^4.1.1", "gulp-watch": "^4.1.1",
"gulp-wrap": "^0.11.0", "gulp-wrap": "^0.11.0",
"http-server": "^0.7.5", "http-server": "git+https://github.com/nodeapps/http-server.git",
"less": "^2.4.0", "less": "^2.4.0",
"mocha": "^2.1.0", "mocha": "^2.1.0",
"selenium-webdriver": "^2.45.0" "selenium-webdriver": "^2.45.0"

View File

@@ -1,133 +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 marked options
marked.setOptions(gfm: true)
# 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,84 +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"
# JSonEditor requires type='object' to be present on defined types, we add it if it's missing
# is there any valid case were it should not be added ?
for def of @model.definitions
@model.definitions[def].type = 'object'
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, '_')
# Make all definitions available at the root of the resource so that they can
# be loaded by the JSonEditor
resource.definitions = @model.definitions
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,488 +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
@
# Is this already available somewhere else ?
extend : (object, properties) ->
for key, val of properties
object[key] = val
object
addParameter: (param, consumes) ->
# Render a parameter
param.consumes = consumes
# Copy this param JSON spec so that it will be available for JsonEditor
if param.schema
@extend param.schema, @model.definitions[param.type]
param.schema.definitions = @model.definitions
# This is required for JsonEditor to display the root properly
param.schema.type = "object"
# This is the title that will be used by JsonEditor for the root
# Since we already display the parameter's name in the Parameter column
# We set this to space, we can't set it to null or space otherwise JsonEditor
# will replace it with the text "root" which won't look good on screen
param.schema.title = " "
paramView = new ParameterView({model: param, tagName: 'tr', readOnly: @model.isReadOnly, swaggerOptions: @options.swaggerOptions})
$('.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
for p in @model.parameters
if p.jsonEditor? && p.jsonEditor.isEnabled()
json = p.jsonEditor.getValue()
map[p.name] = JSON.stringify(json)
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'
supportsAudioPlayback = (contentType) ->
audioElement = document.createElement('audio')
return !!(audioElement.canPlayType && audioElement.canPlayType(contentType).replace(/no/, ''))
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 if /^audio\//.test(contentType) and supportsAudioPlayback(contentType)
pre = $('<audio controls>').append($('<source>').attr('src', url).attr('type', contentType))
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,101 +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 @options.swaggerOptions.jsonEditor && @model.isBody && @model.schema
$self = $(@el)
@model.jsonEditor =
new JSONEditor($('.editor_holder', $self)[0],
{schema: @model.schema, startval : @model.default, ajax:true })
# This is so that the signature can send back the sample to the json editor
# TODO: SignatureView should expose an event "onSampleClicked" instead
signatureModel.jsonEditor = @model.jsonEditor
$('.body-textarea', $self).hide()
$('.editor_holder', $self).show()
$('.parameter-content-type', $self)
.change(
(e) ->
if e.target.value == "application/xml"
$('.body-textarea', $self).show()
$('.editor_holder', $self).hide()
@model.jsonEditor.disable()
else
$('.body-textarea', $self).hide()
$('.editor_holder', $self).show()
@model.jsonEditor.enable())
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,59 +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
operation.definitions = @model.definitions # make Json Schema available for JSonEditor in this operation
@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,55 +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)
# TODO move this code outside of the view and expose an event instead
if @model.jsonEditor && @model.jsonEditor.isEnabled()
@model.jsonEditor.setValue(JSON.parse(this.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

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,7 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Swagger UI</title> <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/typography.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/> <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/> <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
@@ -58,17 +61,16 @@
}); });
function addApiKeyAuthorization() { function addApiKeyAuthorization() {
var key = $('#input_apiKey')[0].value; var key = encodeURIComponent($('#input_apiKey')[0].value);
log("key: " + key); log("key: " + key);
if(key && key.trim() != "") { 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); log("added key " + key);
window.authorizations.add("api_key", new ApiKeyAuthorization("api_key", key, "query"));
} }
} }
$('#input_apiKey').change(function() { $('#input_apiKey').change(addApiKeyAuthorization);
addApiKeyAuthorization();
});
// if you have an apiKey you would like to pre-populate on the page for demonstration purposes... // if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
/* /*
@@ -78,6 +80,12 @@
*/ */
window.swaggerUi.load(); window.swaggerUi.load();
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
}); });
</script> </script>
</head> </head>

View File

@@ -0,0 +1,231 @@
'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'
];
}
// 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 depreciated. Please use waggerUi.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 depreciated. Please use SwaggerClient.ApiKeyAuthorization.');
SwaggerClient.ApiKeyAuthorization.apply(window, arguments);
};
window.PasswordAuthorization = function() {
warn('window.PasswordAuthorization is depreciated. Please use SwaggerClient.PasswordAuthorization.');
SwaggerClient.PasswordAuthorization.apply(window, arguments);
};
function warn(message) {
if ('console' in window && typeof window.console.warn === 'function') {
console.warn(message);
}
}
})();

View File

@@ -1,9 +1,12 @@
'use strict';
$(function() { $(function() {
// Helper function for vertically aligning DOM elements // Helper function for vertically aligning DOM elements
// http://www.seodenver.com/simple-vertical-align-plugin-for-jquery/ // http://www.seodenver.com/simple-vertical-align-plugin-for-jquery/
$.fn.vAlign = function() { $.fn.vAlign = function() {
return this.each(function(i){ return this.each(function(){
var ah = $(this).height(); var ah = $(this).height();
var ph = $(this).parent().height(); var ph = $(this).parent().height();
var mh = (ph - ah) / 2; var mh = (ph - ah) / 2;
@@ -12,9 +15,9 @@ $(function() {
}; };
$.fn.stretchFormtasticInputWidthToParent = function() { $.fn.stretchFormtasticInputWidthToParent = function() {
return this.each(function(i){ return this.each(function(){
var p_width = $(this).closest("form").innerWidth(); 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 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); var this_padding = parseInt($(this).css('padding-left'), 10) + parseInt($(this).css('padding-right'), 10);
$(this).css('width', p_width - p_padding - this_padding); $(this).css('width', p_width - p_padding - this_padding);
}); });
@@ -38,7 +41,7 @@ $(function() {
$(this).removeClass('error'); $(this).removeClass('error');
// Tack the error style on if the input is empty.. // Tack the error style on if the input is empty..
if ($(this).val() == '') { if ($(this).val() === '') {
$(this).addClass('error'); $(this).addClass('error');
$(this).wiggle(); $(this).wiggle();
error_free = false; error_free = false;
@@ -51,7 +54,7 @@ $(function() {
}); });
function clippyCopiedCallback(a) { function clippyCopiedCallback() {
$('#api_key_copied').fadeIn().delay(1000).fadeOut(); $('#api_key_copied').fadeIn().delay(1000).fadeOut();
// var b = $("#clippy_tooltip_" + a); // var b = $("#clippy_tooltip_" + a);
@@ -62,16 +65,16 @@ function clippyCopiedCallback(a) {
} }
// Logging function that accounts for browsers that don't have window.console // Logging function that accounts for browsers that don't have window.console
log = function(){ function log(){
log.history = log.history || []; log.history = log.history || [];
log.history.push(arguments); log.history.push(arguments);
if(this.console){ if(this.console){
console.log( Array.prototype.slice.call(arguments)[0] ); console.log( Array.prototype.slice.call(arguments)[0] );
} }
}; }
// Handle browsers that do console incorrectly (IE9 and below, see http://stackoverflow.com/a/5539378/7913) // 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") { if (Function.prototype.bind && console && typeof console.log === "object") {
[ [
"log","info","warn","error","assert","dir","clear","profile","profileEnd" "log","info","warn","error","assert","dir","clear","profile","profileEnd"
].forEach(function (method) { ].forEach(function (method) {
@@ -79,7 +82,7 @@ if (Function.prototype.bind && console && typeof console.log == "object") {
}, Function.prototype.call); }, Function.prototype.call);
} }
var Docs = { window.Docs = {
shebang: function() { shebang: function() {

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,125 @@
'use strict';
SwaggerUi.Views.MainView = Backbone.View.extend({
// TODO: sorters were not used in any place, do we need them?
// sorters = {
// alpha : function(a,b){ return a.path.localeCompare(b.path); },
// method : function(a,b){ return a.method.localeCompare(b.method); },
// },
initialize: function(opts){
opts = opts || {};
this.router = opts.router;
// set up the UI for input
this.model.auths = [];
var key, value;
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';
}
}
// JSonEditor requires type='object' to be present on defined types, we add it if it's missing
// is there any valid case were it should not be added ?
var def;
for(def in this.model.definitions){
this.model.definitions[def].type = 'object';
}
},
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, '_');
// Make all definitions available at the root of the resource so that they can
// be loaded by the JSonEditor
resource.definitions = this.model.definitions
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,653 @@
'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;
// Copy this param JSON spec so that it will be available for JsonEditor
if(param.schema){
$.extend(true, param.schema, this.model.definitions[param.type]);
param.schema.definitions = this.model.definitions;
// This is required for JsonEditor to display the root properly
param.schema.type = "object";
// This is the title that will be used by JsonEditor for the root
// Since we already display the parameter's name in the Parameter column
// We set this to space, we can't set it to null or space otherwise JsonEditor
// will replace it with the text "root" which won't look good on screen
param.schema.title = " ";
}
var paramView = new SwaggerUi.Views.ParameterView({
model: param,
tagName: 'tr',
readOnly: this.model.isReadOnly,
swaggerOptions: this.options.swaggerOptions
});
$('.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;
}
}
var pi;
for(pi = 0; pi < this.model.parameters.length; pi++){
var p = this.model.parameters[pi];
if( p.jsonEditor && p.jsonEditor.isEnabled()){
var json = p.jsonEditor.getValue();
map[p.name] = JSON.stringify(json);
}
}
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);
} 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);
} else if (contentType === 'application/xml' || /\+xml$/.test(contentType)) {
code = $('<code />').text(this.formatXml(content));
pre = $('<pre class="xml" />').append(code);
} else if (contentType === 'text/html') {
code = $('<code />').html(_.escape(content));
pre = $('<pre class="xml" />').append(code);
} else if (/^image\//.test(contentType)) {
pre = $('<img>').attr('src', url);
} else if (/^audio\//.test(contentType) && supportsAudioPlayback(contentType)) {
pre = $('<audio controls>').append($('<source>').attr('src', url).attr('type', contentType));
} 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,127 @@
'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.options.swaggerOptions.jsonEditor && this.model.isBody && this.model.schema){
var $self = $(this.el);
this.model.jsonEditor =
new JSONEditor($('.editor_holder', $self)[0],
{schema: this.model.schema, startval : this.model.default, ajax:true });
// This is so that the signature can send back the sample to the json editor
// TODO: SignatureView should expose an event "onSampleClicked" instead
signatureModel.jsonEditor = this.model.jsonEditor;
$('.body-textarea', $self).hide();
$('.editor_holder', $self).show();
$('.parameter-content-type', $self)
.change(function(e){
if(e.target.value == "application/xml"){
$('.body-textarea', $self).show();
$('.editor_holder', $self).hide();
this.model.jsonEditor.disable();
}
else {
$('.body-textarea', $self).hide();
$('.editor_holder', $self).show();
this.model.jsonEditor.enable();
}
});
}
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,74 @@
'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;
operation.definitions = this.model.definitions // make Json Schema available for JSonEditor in this operation
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,64 @@
'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);
// TODO move this code outside of the view and expose an event instead
if( this.model.jsonEditor && this.model.jsonEditor.isEnabled()){
this.model.jsonEditor.setValue(JSON.parse(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;
}
});

View File

@@ -2,6 +2,11 @@
{{#if info}} {{#if info}}
<div class="info_title">{{info.title}}</div> <div class="info_title">{{info.title}}</div>
<div class="info_description markdown">{{{info.description}}}</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.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.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}} {{#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'> <div class='heading'>
<h3> <h3>
<span class='http_method'> <span class='http_method'>
<a href='#!/{{parentId}}/{{nickname}}' class="toggleOperation">{{method}}</a> <a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation">{{method}}</a>
</span> </span>
<span class='path'> <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> </span>
</h3> </h3>
<ul class='options'> <ul class='options'>
<li> <li>
<a href='#!/{{parentId}}/{{nickname}}' class="toggleOperation">{{{summary}}}</a> <a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation">{{{summary}}}</a>
</li> </li>
</ul> </ul>
</div> </div>
@@ -22,7 +22,7 @@
{{/if}} {{/if}}
{{#if description}} {{#if description}}
<h4>Implementation Notes</h4> <h4>Implementation Notes</h4>
<p class="markdown">{{{description}}}</p> <div class="markdown">{{{description}}}</div>
{{/if}} {{/if}}
{{#oauth}} {{#oauth}}
<div class="auth"> <div class="auth">
@@ -74,6 +74,7 @@
<th>HTTP Status Code</th> <th>HTTP Status Code</th>
<th>Reason</th> <th>Reason</th>
<th>Response Model</th> <th>Response Model</th>
<th>Headers</th>
</tr> </tr>
</thead> </thead>
<tbody class="operation-status"> <tbody class="operation-status">

View File

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

View File

@@ -1,3 +1,16 @@
<td width='15%' class='code'>{{code}}</td> <td width='15%' class='code'>{{code}}</td>
<td>{{{message}}}</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
}
}

View File

@@ -1,6 +1,7 @@
/* /*
* Web driver manager * Web driver manager
*/ */
'use strict';
var webdriver = require('selenium-webdriver'); var webdriver = require('selenium-webdriver');

View File

@@ -1,7 +1,9 @@
/* /*
* Swagger UI and Specs Servers * Swagger UI and Specs Servers
*/ */
var path = require('path') 'use strict';
var path = require('path');
var createServer = require('http-server').createServer; var createServer = require('http-server').createServer;
var dist = path.join(__dirname, '..', '..', 'dist'); var dist = path.join(__dirname, '..', '..', 'dist');
@@ -11,22 +13,17 @@ var SPEC_SERVER_PORT = 8081;
var driver = require('./driver'); var driver = require('./driver');
var headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
};
var swaggerUI; var swaggerUI;
var specServer; var specServer;
module.exports.start = function (specsLocation, done) { module.exports.start = function (specsLocation, done) {
swaggerUI = createServer({ root: dist, headers: headers }); swaggerUI = createServer({ root: dist, cors: true });
specServer = createServer({ root: specs, headers: headers }); specServer = createServer({ root: specs, cors: true });
swaggerUI.listen(DOCS_PORT); swaggerUI.listen(DOCS_PORT);
specServer.listen(SPEC_SERVER_PORT); specServer.listen(SPEC_SERVER_PORT);
var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + specsLocation) var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + specsLocation);
var url = 'http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation; var url = 'http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation;
setTimeout(function(){ setTimeout(function(){
@@ -38,4 +35,4 @@ module.exports.start = function (specsLocation, done) {
module.exports.close = function() { module.exports.close = function() {
swaggerUI.close(); swaggerUI.close();
specServer.close(); specServer.close();
} };

View File

@@ -1,3 +1,5 @@
'use strict';
var expect = require('chai').expect; var expect = require('chai').expect;
var driver = require('./driver'); var driver = require('./driver');
var servers = require('./servers'); var servers = require('./servers');
@@ -13,7 +15,7 @@ var elements = [
'header' 'header'
]; ];
describe('swagger 1.x spec tests', function (done) { describe('swagger 1.x spec tests', function () {
this.timeout(10 * 1000); this.timeout(10 * 1000);
before(function (done) { before(function (done) {
@@ -27,8 +29,9 @@ describe('swagger 1.x spec tests', function (done) {
var errors = []; var errors = [];
browserLogs.forEach(function(log){ browserLogs.forEach(function(log){
// 900 and above is "error" level. Console should not have any errors // 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); console.log('browser error message:', log.message); errors.push(log);
}
}); });
expect(errors).to.be.empty; expect(errors).to.be.empty;
done(); done();
@@ -46,7 +49,7 @@ describe('swagger 1.x spec tests', function (done) {
elements.forEach(function (id) { elements.forEach(function (id) {
it('should render element: ' + id, function (done) { 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) { driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true; expect(isPresent).to.be.true;
done(); done();
@@ -63,7 +66,7 @@ describe('swagger 1.x spec tests', function (done) {
}); });
it('should find the pet link', 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) { driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true; expect(isPresent).to.be.true;
done(); done();
@@ -71,7 +74,7 @@ describe('swagger 1.x spec tests', function (done) {
}); });
it('should find the pet resource description', function(done){ it('should find the pet resource description', function(done){
var locator = webdriver.By.xpath("//div[contains(., 'Operations about pets')]"); var locator = webdriver.By.xpath('//div[contains(., "Operations about pets")]');
driver.findElements(locator).then(function (elements) { driver.findElements(locator).then(function (elements) {
expect(elements.length).to.not.equal(0); expect(elements.length).to.not.equal(0);
done(); done();
@@ -79,7 +82,7 @@ describe('swagger 1.x spec tests', function (done) {
}); });
it('should find the user link', 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) { driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true; expect(isPresent).to.be.true;
done(); done();
@@ -87,7 +90,7 @@ describe('swagger 1.x spec tests', function (done) {
}); });
it('should find the store link', 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) { driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true; expect(isPresent).to.be.true;
done(); done();

View File

@@ -1,3 +1,5 @@
'use strict';
var expect = require('chai').expect; var expect = require('chai').expect;
var webdriver = require('selenium-webdriver'); var webdriver = require('selenium-webdriver');
var driver = require('./driver'); var driver = require('./driver');
@@ -14,7 +16,7 @@ var elements = [
'header' 'header'
]; ];
describe('swagger 2.0 spec tests', function (done) { describe('swagger 2.0 spec tests', function () {
this.timeout(10 * 1000); this.timeout(10 * 1000);
before(function (done) { before(function (done) {
@@ -28,8 +30,9 @@ describe('swagger 2.0 spec tests', function (done) {
var errors = []; var errors = [];
browserLogs.forEach(function(log){ browserLogs.forEach(function(log){
// 900 and above is "error" level. Console should not have any errors // 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); console.log('browser error message:', log.message); errors.push(log);
}
}); });
expect(errors).to.be.empty; expect(errors).to.be.empty;
done(); done();
@@ -80,7 +83,7 @@ describe('swagger 2.0 spec tests', function (done) {
}); });
it('should find the pet link', 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) { driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true; expect(isPresent).to.be.true;
done(); done();
@@ -88,7 +91,7 @@ describe('swagger 2.0 spec tests', function (done) {
}); });
it('should find the pet resource description', 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) { driver.findElements(locator).then(function (elements) {
expect(elements.length).to.not.equal(0); expect(elements.length).to.not.equal(0);
done(); done();
@@ -96,7 +99,7 @@ describe('swagger 2.0 spec tests', function (done) {
}); });
it('should find the user link', 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) { driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true; expect(isPresent).to.be.true;
done(); done();
@@ -104,7 +107,7 @@ describe('swagger 2.0 spec tests', function (done) {
}); });
it('should find the store link', 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) { driver.isElementPresent(locator).then(function (isPresent) {
expect(isPresent).to.be.true; expect(isPresent).to.be.true;
done(); done();
@@ -121,6 +124,8 @@ describe('swagger 2.0 spec tests', function (done) {
}); });
}); });
// TODO JSonEditor Tests for POST/PUT
after(function() { after(function() {
servers.close(); servers.close();
}); });