├── PROCFILE ├── src └── main │ ├── template │ ├── sidebar_header.handlebars │ ├── sidebar_item.handlebars │ ├── resource.handlebars │ ├── status_code.handlebars │ ├── content_type.handlebars │ ├── parameter_content_type.handlebars │ ├── apikey_button_view.handlebars │ ├── signature.handlebars │ ├── basic_auth_button_view.handlebars │ ├── response_content_type.handlebars │ ├── param_readonly.handlebars │ ├── param_readonly_required.handlebars │ ├── param_list.handlebars │ ├── param_required.handlebars │ ├── param.handlebars │ ├── main.handlebars │ └── operation.handlebars │ ├── html │ ├── images │ │ ├── favicon.ico │ │ ├── senodio.png │ │ ├── throbber.gif │ │ ├── logo_small.png │ │ ├── wordnik_api.png │ │ ├── explorer_icons.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── pet_store_api.png │ │ ├── Swagger_explorer.png │ │ ├── Swagger_explorer_min.png │ │ └── json_editor_integration.png │ ├── fonts │ │ ├── droid-sans-v6-latin-700.eot │ │ ├── droid-sans-v6-latin-700.ttf │ │ ├── droid-sans-v6-latin-700.woff │ │ ├── droid-sans-v6-latin-700.woff2 │ │ ├── droid-sans-v6-latin-regular.eot │ │ ├── droid-sans-v6-latin-regular.ttf │ │ ├── droid-sans-v6-latin-regular.woff │ │ └── droid-sans-v6-latin-regular.woff2 │ ├── css │ │ ├── screen.css │ │ ├── typography.css │ │ ├── reset.css │ │ └── standalone.css │ ├── o2c.html │ └── index.html │ ├── less │ ├── screen.less │ ├── print.less │ ├── reset.less │ ├── auth.less │ └── highlight_default.less │ └── javascript │ ├── helpers │ └── handlebars.js │ ├── view │ ├── ContentTypeView.js │ ├── SidebarItemView.js │ ├── ResponseContentTypeView.js │ ├── ParameterContentTypeView.js │ ├── StatusCodeView.js │ ├── SignatureView.js │ ├── ApiKeyButton.js │ ├── BasicAuthButton.js │ ├── HeaderView.js │ ├── ResourceView.js │ ├── SidebarHeaderView.js │ ├── ParameterView.js │ └── MainView.js │ ├── doc.js │ └── SwaggerUi.js ├── test ├── mocha.opts ├── .jshintrc ├── e2e │ ├── driver.js │ ├── servers.js │ ├── v1.js │ └── v2.js └── specs │ └── v1.2 │ └── petstore │ ├── api-docs.json │ ├── store.json │ ├── user.json │ └── pet.json ├── .dockerignore ├── .jshintignore ├── dist ├── images │ ├── favicon.ico │ ├── senodio.png │ ├── throbber.gif │ ├── logo_small.png │ ├── wordnik_api.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── pet_store_api.png │ ├── Swagger_explorer.png │ ├── explorer_icons.png │ ├── Swagger_explorer_min.png │ └── json_editor_integration.png ├── fonts │ ├── droid-sans-v6-latin-700.eot │ ├── droid-sans-v6-latin-700.ttf │ ├── droid-sans-v6-latin-700.woff │ ├── droid-sans-v6-latin-700.woff2 │ ├── droid-sans-v6-latin-regular.eot │ ├── droid-sans-v6-latin-regular.ttf │ ├── droid-sans-v6-latin-regular.woff │ └── droid-sans-v6-latin-regular.woff2 ├── css │ ├── screen.css │ ├── typography.css │ ├── reset.css │ └── standalone.css ├── lib │ ├── jquery.slideto.min.js │ ├── jquery.wiggle.min.js │ ├── jquery.ba-bbq.min.js │ ├── highlight.7.3.pack.js │ └── swagger-oauth.js ├── o2c.html └── index.html ├── .npmignore ├── index.js ├── .gitignore ├── .travis.yml ├── .gitattributes ├── lib ├── jquery.slideto.min.js ├── jquery.wiggle.min.js ├── jquery.ba-bbq.min.js ├── highlight.7.3.pack.js └── swagger-oauth.js ├── server.js ├── bower.json ├── CONTRIBUTING.md ├── LICENSE ├── Dockerfile ├── .jshintrc ├── package.json ├── README.md └── gulpfile.js /PROCFILE: -------------------------------------------------------------------------------- 1 | web: node server.js -------------------------------------------------------------------------------- /src/main/template/sidebar_header.handlebars: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive --timeout 5000 -------------------------------------------------------------------------------- /src/main/template/sidebar_item.handlebars: -------------------------------------------------------------------------------- 1 | {{summary}} 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | bower_components 4 | *.swp 5 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src/main/javascript/doc.js 3 | dist 4 | lib 5 | .log -------------------------------------------------------------------------------- /src/main/template/resource.handlebars: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/favicon.ico -------------------------------------------------------------------------------- /dist/images/senodio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/senodio.png -------------------------------------------------------------------------------- /dist/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/throbber.gif -------------------------------------------------------------------------------- /dist/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/logo_small.png -------------------------------------------------------------------------------- /dist/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/wordnik_api.png -------------------------------------------------------------------------------- /dist/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/favicon-16x16.png -------------------------------------------------------------------------------- /dist/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/favicon-32x32.png -------------------------------------------------------------------------------- /dist/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/pet_store_api.png -------------------------------------------------------------------------------- /dist/images/Swagger_explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/Swagger_explorer.png -------------------------------------------------------------------------------- /dist/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/explorer_icons.png -------------------------------------------------------------------------------- /src/main/html/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/favicon.ico -------------------------------------------------------------------------------- /src/main/html/images/senodio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/senodio.png -------------------------------------------------------------------------------- /src/main/html/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/throbber.gif -------------------------------------------------------------------------------- /src/main/template/status_code.handlebars: -------------------------------------------------------------------------------- 1 | {{code}} 2 | {{{message}}} 3 | -------------------------------------------------------------------------------- /dist/images/Swagger_explorer_min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/Swagger_explorer_min.png -------------------------------------------------------------------------------- /src/main/html/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/logo_small.png -------------------------------------------------------------------------------- /src/main/html/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/wordnik_api.png -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-700.eot -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-700.ttf -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-700.woff -------------------------------------------------------------------------------- /dist/images/json_editor_integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/images/json_editor_integration.png -------------------------------------------------------------------------------- /src/main/html/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/explorer_icons.png -------------------------------------------------------------------------------- /src/main/html/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/favicon-16x16.png -------------------------------------------------------------------------------- /src/main/html/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/favicon-32x32.png -------------------------------------------------------------------------------- /src/main/html/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/pet_store_api.png -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-700.woff2 -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-regular.eot -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-regular.ttf -------------------------------------------------------------------------------- /src/main/html/images/Swagger_explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/Swagger_explorer.png -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-regular.woff -------------------------------------------------------------------------------- /dist/fonts/droid-sans-v6-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/dist/fonts/droid-sans-v6-latin-regular.woff2 -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-700.eot -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-700.ttf -------------------------------------------------------------------------------- /src/main/html/images/Swagger_explorer_min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/Swagger_explorer_min.png -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-700.woff -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-700.woff2 -------------------------------------------------------------------------------- /src/main/html/images/json_editor_integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/images/json_editor_integration.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | example.html 3 | *.tgz 4 | .classpath 5 | .project 6 | .npmignore 7 | dist/sample.html 8 | dist/spec.js 9 | node_modules 10 | -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-regular.eot -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-regular.ttf -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-regular.woff -------------------------------------------------------------------------------- /src/main/html/fonts/droid-sans-v6-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinSahlen/swagger-ui/HEAD/src/main/html/fonts/droid-sans-v6-latin-regular.woff2 -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var pack = require('./package'); 2 | var path = require('path'); 3 | 4 | module.exports = { 5 | version: pack.version, 6 | dist: path.resolve(__dirname, 'dist') 7 | }; -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.jshintrc", 3 | "expr": true, 4 | "jasmine": true, 5 | "globals": { 6 | "before": false, 7 | "after": false, 8 | "expect": true 9 | } 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | *.ipr 3 | *.iml 4 | *.iws 5 | web/ 6 | lib/*.zip 7 | version.properties 8 | .sass-cache 9 | swagger-ui.sublime-workspace 10 | .idea 11 | .project 12 | node_modules/* 13 | /nbproject/private/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '0.10' 5 | - '0.12' 6 | install: 7 | - export DISPLAY=:99.0 8 | - sh -e /etc/init.d/xvfb start 9 | - npm i -g jshint 10 | - npm install 11 | -------------------------------------------------------------------------------- /dist/css/screen.css: -------------------------------------------------------------------------------- 1 | .swagger-section .swagger-collapse:before { 2 | content: "-"; 3 | } 4 | .swagger-section .swagger-expand:before { 5 | content: "+"; 6 | } 7 | .swagger-section .property-selector input[type="checkbox"] { 8 | margin: 3px; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/html/css/screen.css: -------------------------------------------------------------------------------- 1 | .swagger-section .swagger-collapse:before { 2 | content: "-"; 3 | } 4 | .swagger-section .swagger-expand:before { 5 | content: "+"; 6 | } 7 | .swagger-section .property-selector input[type="checkbox"] { 8 | margin: 3px; 9 | } 10 | -------------------------------------------------------------------------------- /test/e2e/driver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Web driver manager 3 | */ 4 | 'use strict'; 5 | 6 | var webdriver = require('selenium-webdriver'); 7 | 8 | var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.firefox()).build(); 9 | 10 | module.exports = driver; -------------------------------------------------------------------------------- /src/main/less/screen.less: -------------------------------------------------------------------------------- 1 | .swagger-section { 2 | .swagger-collapse:before { 3 | content: "-"; 4 | } 5 | 6 | .swagger-expand:before { 7 | content: "+"; 8 | } 9 | 10 | .property-selector input[type="checkbox"] { 11 | margin: 3px; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | 3 | dist/**/*.js binary 4 | dist/**/*.map binary 5 | dist/**/*.eot binary 6 | dist/**/*.svg binary 7 | dist/**/*.ttf binary 8 | dist/**/*.woff binary 9 | dist/**/*.woff2 binary 10 | dist/**/*.png binary 11 | dist/*.html text 12 | 13 | src/main/html/images/*.png binary 14 | -------------------------------------------------------------------------------- /src/main/template/content_type.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /src/main/javascript/helpers/handlebars.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Handlebars.registerHelper('sanitize', function(html) { 4 | // Strip the script tags from the html, and return it as a Handlebars.SafeString 5 | html = html.replace(/)<[^<]*)*<\/script>/gi, ''); 6 | return new Handlebars.SafeString(html); 7 | }); -------------------------------------------------------------------------------- /lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /src/main/javascript/view/ContentTypeView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.ContentTypeView = Backbone.View.extend({ 4 | initialize: function() {}, 5 | 6 | render: function(){ 7 | $(this.el).html(Handlebars.templates.content_type(this.model)); 8 | 9 | $('label[for=contentType]', $(this.el)).text('Response Content Type'); 10 | 11 | return this; 12 | } 13 | }); -------------------------------------------------------------------------------- /dist/lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /src/main/javascript/view/SidebarItemView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.SidebarItemView = Backbone.View.extend({ 4 | 5 | initialize: function (opts) { 6 | this.options = opts || {}; 7 | this.router = this.options.router; 8 | }, 9 | 10 | render: function () { 11 | $(this.el).html(Handlebars.templates.sidebar_item(this.model)); 12 | return this; 13 | } 14 | 15 | }); -------------------------------------------------------------------------------- /src/main/javascript/view/ResponseContentTypeView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.ResponseContentTypeView = Backbone.View.extend({ 4 | initialize: function(){}, 5 | 6 | render: function(){ 7 | $(this.el).html(Handlebars.templates.response_content_type(this.model)); 8 | 9 | //$('label[for=responseContentType]', $(this.el)).text('Response Content Type'); 10 | 11 | return this; 12 | } 13 | }); -------------------------------------------------------------------------------- /src/main/javascript/view/ParameterContentTypeView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.ParameterContentTypeView = Backbone.View.extend({ 4 | initialize: function () {}, 5 | 6 | render: function(){ 7 | $(this.el).html(Handlebars.templates.parameter_content_type(this.model)); 8 | 9 | //$('label[for=parameterContentType]', $(this.el)).text('Parameter content type:'); 10 | 11 | return this; 12 | } 13 | 14 | }); -------------------------------------------------------------------------------- /src/main/template/parameter_content_type.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 3 | 12 |
13 | -------------------------------------------------------------------------------- /dist/o2c.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/html/o2c.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/template/apikey_button_view.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
{{keyName}}
7 | 8 | 9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var express = require('express'); 5 | var cluster = require('cluster'); 6 | 7 | var WORKERS = process.env.WEB_CONCURRENCY || 1; 8 | 9 | if (cluster.isMaster) { 10 | 11 | for (var i = 0; i < WORKERS; i++) { 12 | cluster.fork(); 13 | } 14 | cluster.on('exit', function(worker) { 15 | console.log('worker ' + worker.process.pid + ' died'); 16 | }); 17 | } else { 18 | var app = express(); 19 | app.use(express.static(path.join(__dirname, '/dist'))); 20 | app.listen(process.env.PORT || 8080); 21 | } -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-ui", 3 | "main": "dist/index.html", 4 | "version": "2.1.8-M1", 5 | "authors": [ 6 | "Mohsen Azimi " 7 | ], 8 | "description": "Swagger UI", 9 | "moduleType": [ 10 | "globals" 11 | ], 12 | "keywords": [ 13 | "Swagger", 14 | "API" 15 | ], 16 | "license": "Copyright 2015 Reverb Technologies, Inc.", 17 | "homepage": "http://swagger.io", 18 | "private": true, 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "tests" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Pull Requests 2 | Plase make your pull requests are made to the [**`develop_2.0`**](https://github.com/swagger-api/swagger-ui/tree/develop_2.0) branch at this time. 3 | 4 | ## Issues 5 | SwaggerUI uses [SwaggerJS](https://github.com/swagger-api/swagger-js) library for many internal operations. If you see errors in 6 | [`swagger-client.js`](lib/swagger-client.js) file, you should probably open the issue in [SwaggerJS](https://github.com/swagger-api/swagger-js) repository. 7 | 8 | Please open issues related to Swagger specifications in [Swagger Specs](https://github.com/swagger-api/swagger-spec) repository. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Reverb Technologies, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /src/main/template/signature.handlebars: -------------------------------------------------------------------------------- 1 |

{{type}} Sample

3 |
4 |
5 |
{{sampleJSON}}
6 |
7 |
8 | 9 | {{#if signature}} 10 | 12 |
13 |
14 | {{{signature}}} 15 |
16 |
17 | {{/if}} 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ### 2 | # swagger-ui-builder - https://github.com/wordnik/swagger-ui/ 3 | # Container for building the swagger-ui static site 4 | # 5 | # Build: docker build -t swagger-ui-builder . 6 | # Run: docker run -v $PWD/dist:/build/dist swagger-ui-builder 7 | # 8 | ### 9 | 10 | FROM ubuntu:14.04 11 | MAINTAINER dnephin@gmail.com 12 | 13 | ENV DEBIAN_FRONTEND noninteractive 14 | 15 | RUN apt-get update && apt-get install -y git npm nodejs openjdk-7-jre 16 | RUN ln -s /usr/bin/nodejs /usr/local/bin/node 17 | 18 | WORKDIR /build 19 | ADD package.json /build/package.json 20 | RUN npm install 21 | ADD . /build 22 | CMD ./node_modules/gulp/bin/gulp.js serve 23 | -------------------------------------------------------------------------------- /src/main/template/basic_auth_button_view.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Username
5 | 6 |
Password
7 | 8 | 9 |
10 |
11 | 12 | -------------------------------------------------------------------------------- /src/main/template/response_content_type.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
Response Type
3 |
4 |
5 | 14 |
15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /dist/lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": false, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "validthis": true, 21 | "globals": { 22 | 23 | // Libraries 24 | "_": false, 25 | "$": false, 26 | "Backbone": false, 27 | "Handlebars": false, 28 | "jQuery": false, 29 | "marked": false, 30 | "SwaggerClient": false, 31 | "hljs": false, 32 | "SwaggerUi": false, 33 | "define": false, 34 | 35 | // Global object 36 | // TODO: remove these 37 | "Docs": false 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/template/param_readonly.handlebars: -------------------------------------------------------------------------------- 1 |
{{name}}
2 |
3 |
4 | {{#if isBody}} 5 | 6 |
7 | {{else}} 8 | {{#if default}} 9 | {{default}} 10 | {{else}} 11 | (empty) 12 | {{/if}} 13 | {{/if}} 14 |
15 | {{#if type}} 16 | {{type}} 17 | {{/if}} 18 |
{{description}}
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /src/main/less/print.less: -------------------------------------------------------------------------------- 1 | @import 'src/main/less/highlight_default.less'; 2 | @import 'src/main/less/specs.less'; 3 | @import 'src/main/less/auth.less'; 4 | 5 | #header { 6 | display: none; 7 | } 8 | 9 | .swagger-section { 10 | 11 | .swagger-ui-wrap { 12 | 13 | .model-signature pre { 14 | max-height: none; 15 | } 16 | 17 | .body-textarea { 18 | width: 100px; 19 | } 20 | 21 | input.parameter { 22 | width: 100px; 23 | } 24 | 25 | ul#resources { 26 | li.resource { 27 | div.heading ul.options { 28 | display: none; 29 | } 30 | ul.endpoints { 31 | display: block !important; 32 | li.endpoint ul.operations li.operation div.content { 33 | display: block !important; 34 | } 35 | } 36 | } 37 | } 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/main/template/param_readonly_required.handlebars: -------------------------------------------------------------------------------- 1 |
{{name}}
2 |
3 |
4 | {{#if isBody}} 5 | 6 |
7 | {{else}} 8 | {{#if default}} 9 | {{default}} 10 | {{else}} 11 | (empty) 12 | {{/if}} 13 | {{/if}} 14 |
15 | {{#if type}} 16 | {{type}} 17 | {{/if}} 18 |
{{description}}
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /src/main/javascript/view/StatusCodeView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.StatusCodeView = Backbone.View.extend({ 4 | initialize: function (opts) { 5 | this.options = opts || {}; 6 | this.router = this.options.router; 7 | }, 8 | 9 | render: function(){ 10 | $(this.el).html(Handlebars.templates.status_code(this.model)); 11 | 12 | if (this.router.api.models.hasOwnProperty(this.model.responseModel)) { 13 | var responseModel = { 14 | sampleJSON: JSON.stringify(this.router.api.models[this.model.responseModel].createJSONSample(), null, 2), 15 | isParam: false, 16 | signature: this.router.api.models[this.model.responseModel].getMockSignature(), 17 | }; 18 | 19 | var responseModelView = new SwaggerUi.Views.SignatureView({model: responseModel, tagName: 'div'}); 20 | $('.model-signature', this.$el).append(responseModelView.render().el); 21 | } else { 22 | $('.model-signature', this.$el).html(''); 23 | } 24 | return this; 25 | } 26 | }); -------------------------------------------------------------------------------- /src/main/javascript/view/SignatureView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.SignatureView = Backbone.View.extend({ 4 | events: { 5 | 'mousedown .snippet': 'snippetToTextArea' 6 | }, 7 | 8 | initialize: function () { 9 | }, 10 | 11 | render: function () { 12 | $(this.el).html(Handlebars.templates.signature(this.model)); 13 | this.isParam = this.model.isParam; 14 | return this; 15 | }, 16 | 17 | // handler for snippet to text area 18 | snippetToTextArea: function (e) { 19 | if (this.isParam) { 20 | if (e) { 21 | e.preventDefault(); 22 | } 23 | 24 | var textArea = $('textarea', $(this.el.parentNode.parentNode.parentNode)); 25 | if ($.trim(textArea.val()) === '') { 26 | textArea.val(this.model.sampleJSON); 27 | // TODO move this code outside of the view and expose an event instead 28 | if( this.model.jsonEditor && this.model.jsonEditor.isEnabled()){ 29 | this.model.jsonEditor.setValue(JSON.parse(this.model.sampleJSON)); 30 | } 31 | } 32 | } 33 | } 34 | }); -------------------------------------------------------------------------------- /test/e2e/servers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Swagger UI and Specs Servers 3 | */ 4 | 'use strict'; 5 | 6 | var path = require('path'); 7 | var createServer = require('http-server').createServer; 8 | 9 | var dist = path.join(__dirname, '..', '..', 'dist'); 10 | var specs = path.join(__dirname, '..', '..', 'test', 'specs'); 11 | var DOCS_PORT = 8080; 12 | var SPEC_SERVER_PORT = 8081; 13 | 14 | var driver = require('./driver'); 15 | 16 | var swaggerUI; 17 | var specServer; 18 | 19 | module.exports.start = function (specsLocation, done) { 20 | swaggerUI = createServer({ root: dist, cors: true }); 21 | specServer = createServer({ root: specs, cors: true }); 22 | 23 | swaggerUI.listen(DOCS_PORT); 24 | specServer.listen(SPEC_SERVER_PORT); 25 | 26 | var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + specsLocation); 27 | var url = 'http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation; 28 | 29 | setTimeout(function(){ 30 | driver.get(url); 31 | done(); 32 | }, process.env.TRAVIS ? 20000 : 3000); 33 | }; 34 | 35 | module.exports.close = function() { 36 | swaggerUI.close(); 37 | specServer.close(); 38 | }; 39 | -------------------------------------------------------------------------------- /src/main/template/param_list.handlebars: -------------------------------------------------------------------------------- 1 |
{{name}}
2 |
3 |
4 | 20 |
21 | {{#if type}} 22 | {{type}} 23 | {{/if}} 24 |
{{description}}
25 |
26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /src/main/less/reset.less: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | 3 | html, body, div, span, applet, object, iframe, 4 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 5 | a, abbr, acronym, address, big, cite, code, 6 | del, dfn, em, img, ins, kbd, q, s, samp, 7 | small, strike, strong, sub, sup, tt, var, 8 | b, u, i, center, 9 | dl, dt, dd, ol, ul, li, 10 | fieldset, form, label, legend, 11 | table, caption, tbody, tfoot, thead, tr, th, td, 12 | article, aside, canvas, details, embed, 13 | figure, figcaption, footer, header, hgroup, 14 | menu, nav, output, ruby, section, summary, 15 | time, mark, audio, video { 16 | margin: 0; 17 | padding: 0; 18 | border: 0; 19 | font-size: 100%; 20 | font: inherit; 21 | vertical-align: baseline; 22 | } 23 | 24 | /* HTML5 display-role reset for older browsers */ 25 | article, aside, details, figcaption, figure, 26 | footer, header, hgroup, menu, nav, section { 27 | display: block; 28 | } 29 | 30 | body { 31 | line-height: 1; 32 | } 33 | 34 | ol, ul { 35 | list-style: none; 36 | } 37 | 38 | blockquote, q { 39 | quotes: none; 40 | } 41 | 42 | blockquote:before, blockquote:after, 43 | q:before, q:after { 44 | content: ''; 45 | content: none; 46 | } 47 | 48 | table { 49 | border-collapse: collapse; 50 | border-spacing: 0; 51 | } 52 | -------------------------------------------------------------------------------- /src/main/javascript/view/ApiKeyButton.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.ApiKeyButton = Backbone.View.extend({ // TODO: append this to global SwaggerUi 4 | 5 | events:{ 6 | 'click #apikey_button' : 'toggleApiKeyContainer', 7 | 'click #apply_api_key' : 'applyApiKey' 8 | }, 9 | 10 | initialize: function(opts){ 11 | this.options = opts || {}; 12 | this.router = this.options.router; 13 | }, 14 | 15 | render: function(){ 16 | var template = this.template(); 17 | $(this.el).html(template(this.model)); 18 | 19 | return this; 20 | }, 21 | 22 | 23 | applyApiKey: function(){ 24 | var keyAuth = new SwaggerClient.ApiKeyAuthorization( 25 | this.model.name, 26 | $('#input_apiKey_entry').val(), 27 | this.model.in 28 | ); 29 | this.router.api.clientAuthorizations.add(this.model.name, keyAuth); 30 | this.router.load(); 31 | $('#apikey_container').show(); 32 | }, 33 | 34 | toggleApiKeyContainer: function(){ 35 | if ($('#apikey_container').length) { 36 | 37 | var elem = $('#apikey_container').first(); 38 | 39 | if (elem.is(':visible')){ 40 | elem.hide(); 41 | } else { 42 | 43 | // hide others 44 | $('.auth_container').hide(); 45 | elem.show(); 46 | } 47 | } 48 | }, 49 | 50 | template: function(){ 51 | return Handlebars.templates.apikey_button_view; 52 | } 53 | 54 | }); -------------------------------------------------------------------------------- /src/main/javascript/view/BasicAuthButton.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.BasicAuthButton = Backbone.View.extend({ 4 | 5 | 6 | initialize: function (opts) { 7 | this.options = opts || {}; 8 | this.router = this.options.router; 9 | }, 10 | 11 | render: function(){ 12 | var template = this.template(); 13 | $(this.el).html(template(this.model)); 14 | 15 | return this; 16 | }, 17 | 18 | events: { 19 | 'click #basic_auth_button' : 'togglePasswordContainer', 20 | 'click #apply_basic_auth' : 'applyPassword' 21 | }, 22 | 23 | applyPassword: function(){ 24 | var username = $('.input_username').val(); 25 | var password = $('.input_password').val(); 26 | var basicAuth = new SwaggerClient.PasswordAuthorization('basic', username, password); 27 | this.router.api.clientAuthorizations.add(this.model.type, basicAuth); 28 | this.router.load(); 29 | $('#basic_auth_container').hide(); 30 | }, 31 | 32 | togglePasswordContainer: function(){ 33 | if ($('#basic_auth_container').length) { 34 | var elem = $('#basic_auth_container').show(); 35 | if (elem.is(':visible')){ 36 | elem.slideUp(); 37 | } else { 38 | // hide others 39 | $('.auth_container').hide(); 40 | elem.show(); 41 | } 42 | } 43 | }, 44 | 45 | template: function(){ 46 | return Handlebars.templates.basic_auth_button_view; 47 | } 48 | 49 | }); -------------------------------------------------------------------------------- /src/main/less/auth.less: -------------------------------------------------------------------------------- 1 | .swagger-section { 2 | 3 | .title { 4 | font-style: bold; 5 | } 6 | 7 | .secondary_form { 8 | display: none; 9 | } 10 | 11 | .main_image { 12 | display: block; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | .oauth_body { 18 | margin-left: 100px; 19 | margin-right: 100px; 20 | } 21 | 22 | .oauth_submit { 23 | text-align: center; 24 | } 25 | 26 | .api-popup-dialog { 27 | z-index: 10000; 28 | position: absolute; 29 | width: 500px; 30 | background: #FFF; 31 | padding: 20px; 32 | border: 1px solid #ccc; 33 | border-radius: 5px; 34 | display: none; 35 | font-size: 13px; 36 | color: #777; 37 | 38 | .api-popup-title{ 39 | font-size: 24px; 40 | padding: 10px 0; 41 | } 42 | 43 | .api-popup-title{ 44 | font-size: 24px; 45 | padding: 10px 0; 46 | } 47 | 48 | 49 | p.error-msg { 50 | padding-left: 5px; 51 | padding-bottom: 5px; 52 | } 53 | 54 | button.api-popup-authbtn { 55 | height: 30px; 56 | } 57 | button.api-popup-cancel { 58 | height: 30px; 59 | } 60 | } 61 | 62 | .api-popup-scopes { 63 | padding: 10px 20px; 64 | 65 | li { 66 | padding: 5px 0; 67 | line-height: 20px; 68 | } 69 | 70 | .api-scope-desc { 71 | padding-left: 20px; 72 | font-style: italic; 73 | } 74 | li input { 75 | position: relative; 76 | top: 2px; 77 | } 78 | } 79 | .api-popup-actions { 80 | padding-top: 10px; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/javascript/view/HeaderView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.HeaderView = Backbone.View.extend({ 4 | events: { 5 | 'click #show-pet-store-icon' : 'showPetStore', 6 | 'click #show-wordnik-dev-icon' : 'showWordnikDev', 7 | 'click #explore' : 'showCustom', 8 | 'keyup #input_baseUrl' : 'showCustomOnKeyup', 9 | 'keyup #input_apiKey' : 'showCustomOnKeyup' 10 | }, 11 | 12 | initialize: function(){}, 13 | 14 | showPetStore: function(){ 15 | this.trigger('update-swagger-ui', { 16 | url:'http://petstore.swagger.io/v2/swagger.json' 17 | }); 18 | }, 19 | 20 | showWordnikDev: function(){ 21 | this.trigger('update-swagger-ui', { 22 | url: 'http://api.wordnik.com/v4/resources.json' 23 | }); 24 | }, 25 | 26 | showCustomOnKeyup: function(e){ 27 | if (e.keyCode === 13) { 28 | this.showCustom(); 29 | } 30 | }, 31 | 32 | showCustom: function(e){ 33 | if (e) { 34 | e.preventDefault(); 35 | } 36 | 37 | this.trigger('update-swagger-ui', { 38 | url: $('#input_baseUrl').val(), 39 | apiKey: $('#input_apiKey').val() 40 | }); 41 | }, 42 | 43 | update: function(url, apiKey, trigger){ 44 | if (trigger === undefined) { 45 | trigger = false; 46 | } 47 | 48 | $('#input_baseUrl').val(url); 49 | 50 | $('#input_apiKey').val(apiKey); 51 | if (trigger) { 52 | this.trigger('update-swagger-ui', {url:url}); 53 | } 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /dist/css/typography.css: -------------------------------------------------------------------------------- 1 | /* droid-sans-regular - latin */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url('../fonts/droid-sans-v6-latin-regular.eot'); /* IE9 Compat Modes */ 7 | src: local('Droid Sans'), local('DroidSans'), 8 | url('../fonts/droid-sans-v6-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 9 | url('../fonts/droid-sans-v6-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ 10 | url('../fonts/droid-sans-v6-latin-regular.woff') format('woff'), /* Modern Browsers */ 11 | url('../fonts/droid-sans-v6-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ 12 | url('../fonts/droid-sans-v6-latin-regular.svg#DroidSans') format('svg'); /* Legacy iOS */ 13 | } 14 | /* droid-sans-700 - latin */ 15 | @font-face { 16 | font-family: 'Droid Sans'; 17 | font-style: normal; 18 | font-weight: 700; 19 | src: url('../fonts/droid-sans-v6-latin-700.eot'); /* IE9 Compat Modes */ 20 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), 21 | url('../fonts/droid-sans-v6-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 22 | url('../fonts/droid-sans-v6-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ 23 | url('../fonts/droid-sans-v6-latin-700.woff') format('woff'), /* Modern Browsers */ 24 | url('../fonts/droid-sans-v6-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */ 25 | url('../fonts/droid-sans-v6-latin-700.svg#DroidSans') format('svg'); /* Legacy iOS */ 26 | } 27 | -------------------------------------------------------------------------------- /src/main/html/css/typography.css: -------------------------------------------------------------------------------- 1 | /* droid-sans-regular - latin */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url('../fonts/droid-sans-v6-latin-regular.eot'); /* IE9 Compat Modes */ 7 | src: local('Droid Sans'), local('DroidSans'), 8 | url('../fonts/droid-sans-v6-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 9 | url('../fonts/droid-sans-v6-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ 10 | url('../fonts/droid-sans-v6-latin-regular.woff') format('woff'), /* Modern Browsers */ 11 | url('../fonts/droid-sans-v6-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */ 12 | url('../fonts/droid-sans-v6-latin-regular.svg#DroidSans') format('svg'); /* Legacy iOS */ 13 | } 14 | /* droid-sans-700 - latin */ 15 | @font-face { 16 | font-family: 'Droid Sans'; 17 | font-style: normal; 18 | font-weight: 700; 19 | src: url('../fonts/droid-sans-v6-latin-700.eot'); /* IE9 Compat Modes */ 20 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), 21 | url('../fonts/droid-sans-v6-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 22 | url('../fonts/droid-sans-v6-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ 23 | url('../fonts/droid-sans-v6-latin-700.woff') format('woff'), /* Modern Browsers */ 24 | url('../fonts/droid-sans-v6-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */ 25 | url('../fonts/droid-sans-v6-latin-700.svg#DroidSans') format('svg'); /* Legacy iOS */ 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-ui", 3 | "engines": { 4 | "node": "4.2.1", 5 | "npm": "3.3.10" 6 | }, 7 | "author": "Tony Tam ", 8 | "contributors": [{ 9 | "name": "Mohsen Azimi", 10 | "email": "me@azimi.me" 11 | }, 12 | { 13 | "name": "Fares Droubi", 14 | "email": "fares.droubi@gmail.com" 15 | }], 16 | "description": "Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API", 17 | "version": "2.1.1-M2", 18 | "homepage": "http://swagger.io", 19 | "license": "Apache 2.0", 20 | "scripts": { 21 | "build": "gulp", 22 | "serve": "gulp serve", 23 | "prejshint": "gulp", 24 | "jshint": "jshint .", 25 | "pretest": "npm run jshint", 26 | "test": "mocha" 27 | }, 28 | "dependencies": { 29 | "express": "^4.12.0" 30 | }, 31 | "devDependencies": { 32 | "chai": "^2.1.0", 33 | "cors": "^2.5.3", 34 | "docco": "^0.7.0", 35 | "event-stream": "^3.2.2", 36 | "gulp": "^3.8.11", 37 | "gulp-clean": "^0.3.1", 38 | "gulp-concat": "^2.5.2", 39 | "gulp-connect": "^2.2.0", 40 | "gulp-declare": "^0.3.0", 41 | "gulp-handlebars": "^3.0.1", 42 | "gulp-header": "^1.2.2", 43 | "gulp-less": "^3.0.1", 44 | "gulp-order": "^1.1.1", 45 | "gulp-rename": "^1.2.0", 46 | "gulp-uglify": "^1.1.0", 47 | "gulp-watch": "^4.1.1", 48 | "gulp-wrap": "^0.11.0", 49 | "http-server": "git+https://github.com/nodeapps/http-server.git", 50 | "less": "^2.4.0", 51 | "mocha": "^2.1.0", 52 | "selenium-webdriver": "^2.45.0", 53 | "swagger-client": "git+https://github.com/jensoleg/swagger-js.git" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /dist/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | body { 105 | line-height: 1; 106 | } 107 | ol, 108 | ul { 109 | list-style: none; 110 | } 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | blockquote:before, 116 | blockquote:after, 117 | q:before, 118 | q:after { 119 | content: ''; 120 | content: none; 121 | } 122 | table { 123 | border-collapse: collapse; 124 | border-spacing: 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/main/html/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | body { 105 | line-height: 1; 106 | } 107 | ol, 108 | ul { 109 | list-style: none; 110 | } 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | blockquote:before, 116 | blockquote:after, 117 | q:before, 118 | q:after { 119 | content: ''; 120 | content: none; 121 | } 122 | table { 123 | border-collapse: collapse; 124 | border-spacing: 0; 125 | } 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swagger UI responsive theme 2 | 3 | With inspiration from the [Auth0 api explorer](https://auth0.com/docs/apiv2) the original Swagger UI repo is modified to use a responsive layout. 4 | 5 | [![Swagger Theme example](dist/images/Swagger_explorer.png)](http://ecs.bobbytech.dk/api) 6 | 7 | The theme will adapt to screen size and works on tablets and mobile phones. 8 | 9 | [![Swagger Theme example](dist/images/Swagger_explorer_min.png)](http://ecs.bobbytech.dk/api) 10 | 11 | A JSON editor for the request body (disabled by default). 12 | 13 | ![Swagger JSON editor example](dist/images/json_editor_integration.png) 14 | 15 | JSON editor configuration example: 16 | 17 | ```javascript 18 | jsonEditor: true, 19 | jsonEditorOptions: { 20 | disableProperties:false, 21 | disableEditJson:false, 22 | removeEmptyProperties:true, 23 | noDefaultProperties:true 24 | }, 25 | docExpansion: "none", 26 | sorter: "alpha", 27 | ``` 28 | 29 | Give it a [try](http://swaggerui.herokuapp.com/?url=http://petstore.swagger.io/v2/swagger.json) and enter your own swagger definition. 30 | 31 | 32 | ## Disclaimer 33 | 34 | This is not a fully polished implementation and should be used with care. 35 | 36 | ## License 37 | 38 | Copyright 2011-2015 Reverb technologies, Inc. 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 43 | 44 | Unless required by applicable law or agreed to in writing, software 45 | distributed under the License is distributed on an "AS IS" BASIS, 46 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 47 | See the License for the specific language governing permissions and 48 | limitations under the License. 49 | -------------------------------------------------------------------------------- /src/main/template/param_required.handlebars: -------------------------------------------------------------------------------- 1 |
{{name}}
2 |
3 | {{#if isBody}} 4 | {{#if isFile}} 5 | 6 | {{else}} 7 | {{#if default}} 8 |
9 | 10 |
11 | {{else}} 12 | 13 |
14 |
15 |
16 | {{/if}} 17 | {{/if}} 18 | {{else}} 19 | {{#if isFile}} 20 | 21 | {{else}} 22 | {{#if default}} 23 | 24 | {{else}} 25 | 26 | {{/if}} 27 | {{/if}} 28 | {{/if}} 29 |
30 | {{#if type}} 31 | {{type}} 32 | {{/if}} 33 |
{{description}}
34 |
35 |
36 | 37 | -------------------------------------------------------------------------------- /src/main/javascript/view/ResourceView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.ResourceView = Backbone.View.extend({ 4 | initialize: function (opts) { 5 | opts = opts || {}; 6 | this.router = opts.router; 7 | this.auths = opts.auths; 8 | if ('' === this.model.description) { 9 | this.model.description = null; 10 | } 11 | if (this.model.description) { 12 | this.model.summary = this.model.description; 13 | } 14 | }, 15 | 16 | render: function () { 17 | var methods = {}; 18 | 19 | 20 | $(this.el).html(Handlebars.templates.resource(this.model)); 21 | // Render each operation 22 | for (var i = 0; i < this.model.operationsArray.length; i++) { 23 | var operation = this.model.operationsArray[i]; 24 | var counter = 0; 25 | var id = operation.nickname; 26 | 27 | while (typeof methods[id] !== 'undefined') { 28 | id = id + '_' + counter; 29 | counter += 1; 30 | } 31 | 32 | methods[id] = operation; 33 | 34 | operation.nickname = id; 35 | operation.parentId = this.model.id; 36 | operation.definitions = this.model.definitions; // make Json Schema available for JSonEditor in this operation 37 | 38 | this.addOperation(operation); 39 | } 40 | 41 | return this; 42 | }, 43 | 44 | addOperation: function (operation) { 45 | 46 | operation.number = this.number; 47 | 48 | // Render an operation and add it to operations li 49 | var operationView = new SwaggerUi.Views.OperationView({ 50 | model: operation, 51 | router: this.router, 52 | tagName: 'li', 53 | className: 'endpoint', 54 | swaggerOptions: this.options.swaggerOptions, 55 | auths: this.auths 56 | }); 57 | 58 | $('.endpoints', $(this.el)).append(operationView.render().el); 59 | 60 | this.number++; 61 | 62 | }, 63 | 64 | // Generic Event handler (`Docs` is global) 65 | 66 | 67 | callDocs: function (fnName, e) { 68 | e.preventDefault(); 69 | Docs[fnName](e.currentTarget.getAttribute('data-id')); 70 | } 71 | }); -------------------------------------------------------------------------------- /test/specs/v1.2/petstore/api-docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "1.0.0", 3 | "swaggerVersion": "1.2", 4 | "apis": [ 5 | { 6 | "path": "http://localhost:8081/v1.2/petstore/pet.json", 7 | "description": "Operations about pets" 8 | }, 9 | { 10 | "path": "http://localhost:8081/v1.2/petstore/user.json", 11 | "description": "Operations about user" 12 | }, 13 | { 14 | "path": "http://localhost:8081/v1.2/petstore/store.json", 15 | "description": "Operations about store" 16 | } 17 | ], 18 | "authorizations": { 19 | "oauth2": { 20 | "type": "oauth2", 21 | "scopes": [ 22 | { 23 | "scope": "email", 24 | "description": "Access to your email address" 25 | }, 26 | { 27 | "scope": "pets", 28 | "description": "Access to your pets" 29 | } 30 | ], 31 | "grantTypes": { 32 | "implicit": { 33 | "loginEndpoint": { 34 | "url": "http://petstore.swagger.io/oauth/dialog" 35 | }, 36 | "tokenName": "access_token" 37 | }, 38 | "authorization_code": { 39 | "tokenRequestEndpoint": { 40 | "url": "http://petstore.swagger.io/oauth/requestToken", 41 | "clientIdName": "client_id", 42 | "clientSecretName": "client_secret" 43 | }, 44 | "tokenEndpoint": { 45 | "url": "http://petstore.swagger.io/oauth/token", 46 | "tokenName": "access_code" 47 | } 48 | } 49 | } 50 | } 51 | }, 52 | "info": { 53 | "title": "Swagger Sample App", 54 | "description": "This is a sample server Petstore server. You can find out more about Swagger \n at http://swagger.io or on irc.freenode.net, #swagger. For this sample,\n you can use the api key \"special-key\" to test the authorization filters", 55 | "termsOfServiceUrl": "http://helloreverb.com/terms/", 56 | "contact": "apiteam@swagger.io", 57 | "license": "Apache 2.0", 58 | "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html" 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/template/param.handlebars: -------------------------------------------------------------------------------- 1 |
{{name}}
2 |
3 | {{#if isBody}} 4 | {{#if isFile}} 5 | 6 | 7 |
8 | {{else}} 9 | {{#if default}} 10 |
11 | 12 | 13 |
14 | {{else}} 15 | 16 |
17 |
18 | {{/if}} 19 | {{/if}} 20 |
21 | {{#if type}} 22 | {{type}} 23 | {{/if}} 24 |
{{description}}
25 |
26 | {{else}} 27 | {{#if isFile}} 28 | 29 | 33 |
34 | {{else}} 35 | {{#if default}} 36 | 37 | {{else}} 38 | 39 | {{/if}} 40 | {{/if}} 41 |
42 | {{#if type}} 43 | {{type}} 44 | {{/if}} 45 |
{{description}}
46 |
47 | {{/if}} 48 |
-------------------------------------------------------------------------------- /src/main/template/main.handlebars: -------------------------------------------------------------------------------- 1 |
2 | 61 |
62 | 63 |
64 |
    65 |
    66 | -------------------------------------------------------------------------------- /src/main/javascript/view/SidebarHeaderView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.SidebarHeaderView = Backbone.View.extend({ 4 | initialize: function (opts) { 5 | this.options = opts || {}; 6 | this.router = this.options.router; 7 | }, 8 | 9 | events: { 10 | 'click [data-endpoint]': 'clickSidebarItem' 11 | }, 12 | 13 | render: function () { 14 | $(this.el).html(Handlebars.templates.sidebar_header(this.model)); 15 | 16 | for (var i = 0; i < this.model.operationsArray.length; i++) { 17 | var item = this.model.operationsArray[i].operation; 18 | item.nickname = this.model.operationsArray[i].nickname; 19 | item.parentId = this.model.operation.parentId; 20 | this.addSidebarItem(item, i); 21 | } 22 | 23 | return this; 24 | }, 25 | 26 | addSidebarItem: function (item, i) { 27 | var sidebarItemView = new SwaggerUi.Views.SidebarItemView({ 28 | model: item, 29 | tagName: 'div', 30 | className : 'item', 31 | attributes: { 32 | "data-endpoint": item.parentId + '_' + item.nickname 33 | }, 34 | router: this.router, 35 | swaggerOptions: this.options.swaggerOptions 36 | }); 37 | $(this.el).append(sidebarItemView.render().el); 38 | }, 39 | 40 | clickSidebarItem: function (e) { 41 | 42 | var elem = $(e.target); 43 | var eln = $("#" + elem.attr("data-endpoint")); 44 | 45 | if (elem.is(".item")) { 46 | scroll(elem.attr("data-endpoint")); 47 | setSelected(elem); 48 | updateUrl(eln.find(".path a").first().attr("href")) 49 | } 50 | 51 | /* scroll */ 52 | function scroll(elem) { 53 | var i = $(".sticky-nav").outerHeight(); 54 | var r = $("#" + elem).offset().top - i - 10; 55 | matchMedia() && (r = $("#" + elem).offset().top - 10); 56 | scrollT(r) 57 | } 58 | 59 | /set selected value and select operation (class) */ 60 | function setSelected(element) { 61 | { 62 | var nav = $(".sticky-nav [data-navigator]"); 63 | $("#" + element.attr("data-endpoint")) 64 | } 65 | nav.find("[data-resource]").removeClass("active"); 66 | nav.find("[data-selected]").removeAttr("data-selected"); 67 | element.closest("[data-resource]").addClass("active"); 68 | element.attr("data-selected", ""); 69 | $(".sticky-nav").find("[data-selected-value]").html(element.text()) 70 | } 71 | 72 | /* update navigation */ 73 | function updateUrl(element) { 74 | history.pushState && history.pushState(null, null, element) 75 | } 76 | 77 | function matchMedia() { 78 | return window.matchMedia("(min-width: 992px)").matches 79 | } 80 | 81 | function scrollT(e) { 82 | if ("self" === e) { 83 | var n = $(window).scrollTop(); 84 | return $(window).scrollTop(n) 85 | } 86 | 87 | return $(window).scrollTop(e) 88 | } 89 | } 90 | 91 | }); -------------------------------------------------------------------------------- /src/main/less/highlight_default.less: -------------------------------------------------------------------------------- 1 | /* Original style from softwaremaniacs.org (c) Ivan Sagalaev */ 2 | 3 | .swagger-section { 4 | 5 | pre code { 6 | display: block; padding: 0.5em; 7 | background: #F0F0F0; 8 | } 9 | 10 | pre code, 11 | pre .subst, 12 | pre .tag .title, 13 | pre .lisp .title, 14 | pre .clojure .built_in, 15 | pre .nginx .title { 16 | color: black; 17 | } 18 | 19 | pre .string, 20 | pre .title, 21 | pre .constant, 22 | pre .parent, 23 | pre .tag .value, 24 | pre .rules .value, 25 | pre .rules .value .number, 26 | pre .preprocessor, 27 | pre .ruby .symbol, 28 | pre .ruby .symbol .string, 29 | pre .aggregate, 30 | pre .template_tag, 31 | pre .django .variable, 32 | pre .smalltalk .class, 33 | pre .addition, 34 | pre .flow, 35 | pre .stream, 36 | pre .bash .variable, 37 | pre .apache .tag, 38 | pre .apache .cbracket, 39 | pre .tex .command, 40 | pre .tex .special, 41 | pre .erlang_repl .function_or_atom, 42 | pre .markdown .header { 43 | color: #800; 44 | } 45 | 46 | pre .comment, 47 | pre .annotation, 48 | pre .template_comment, 49 | pre .diff .header, 50 | pre .chunk, 51 | pre .markdown .blockquote { 52 | color: #888; 53 | } 54 | 55 | pre .number, 56 | pre .date, 57 | pre .regexp, 58 | pre .literal, 59 | pre .smalltalk .symbol, 60 | pre .smalltalk .char, 61 | pre .go .constant, 62 | pre .change, 63 | pre .markdown .bullet, 64 | pre .markdown .link_url { 65 | color: #080; 66 | } 67 | 68 | pre .label, 69 | pre .javadoc, 70 | pre .ruby .string, 71 | pre .decorator, 72 | pre .filter .argument, 73 | pre .localvars, 74 | pre .array, 75 | pre .attr_selector, 76 | pre .important, 77 | pre .pseudo, 78 | pre .pi, 79 | pre .doctype, 80 | pre .deletion, 81 | pre .envvar, 82 | pre .shebang, 83 | pre .apache .sqbracket, 84 | pre .nginx .built_in, 85 | pre .tex .formula, 86 | pre .erlang_repl .reserved, 87 | pre .prompt, 88 | pre .markdown .link_label, 89 | pre .vhdl .attribute, 90 | pre .clojure .attribute, 91 | pre .coffeescript .property { 92 | color: #88F 93 | } 94 | 95 | pre .keyword, 96 | pre .id, 97 | pre .phpdoc, 98 | pre .title, 99 | pre .built_in, 100 | pre .aggregate, 101 | pre .css .tag, 102 | pre .javadoctag, 103 | pre .phpdoc, 104 | pre .yardoctag, 105 | pre .smalltalk .class, 106 | pre .winutils, 107 | pre .bash .variable, 108 | pre .apache .tag, 109 | pre .go .typename, 110 | pre .tex .command, 111 | pre .markdown .strong, 112 | pre .request, 113 | pre .status { 114 | font-weight: bold; 115 | } 116 | 117 | pre .markdown .emphasis { 118 | font-style: italic; 119 | } 120 | 121 | pre .nginx .built_in { 122 | font-weight: normal; 123 | } 124 | 125 | pre .coffeescript .javascript, 126 | pre .javascript .xml, 127 | pre .tex .formula, 128 | pre .xml .javascript, 129 | pre .xml .vbscript, 130 | pre .xml .css, 131 | pre .xml .cdata { 132 | opacity: 0.5; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /test/e2e/v1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('chai').expect; 4 | var driver = require('./driver'); 5 | var servers = require('./servers'); 6 | var webdriver = require('selenium-webdriver'); 7 | 8 | var elements = [ 9 | 'swagger-ui-container', 10 | 'resources_container', 11 | 'api_info', 12 | 'resource_pet', 13 | 'resource_store', 14 | 'resource_user', 15 | 'header' 16 | ]; 17 | 18 | describe('swagger 1.x spec tests', function () { 19 | this.timeout(10 * 1000); 20 | 21 | before(function (done) { 22 | this.timeout(25 * 1000); 23 | servers.start('/v1.2/petstore/api-docs.json', done); 24 | }); 25 | 26 | afterEach(function(){ 27 | it('should not have any console errors', function (done) { 28 | driver.manage().logs().get('browser').then(function(browserLogs) { 29 | var errors = []; 30 | browserLogs.forEach(function(log){ 31 | // 900 and above is "error" level. Console should not have any errors 32 | if (log.level.value > 900) { 33 | console.log('browser error message:', log.message); errors.push(log); 34 | } 35 | }); 36 | expect(errors).to.be.empty; 37 | done(); 38 | }); 39 | }); 40 | }); 41 | 42 | it('should have "Swagger UI" in title', function (done) { 43 | driver.sleep(200); 44 | driver.getTitle().then(function(title) { 45 | expect(title).to.contain('Swagger UI'); 46 | done(); 47 | }); 48 | }); 49 | 50 | elements.forEach(function (id) { 51 | it('should render element: ' + id, function (done) { 52 | var locator = webdriver.By.id(id); 53 | driver.isElementPresent(locator).then(function (isPresent) { 54 | expect(isPresent).to.be.true; 55 | done(); 56 | }); 57 | }); 58 | }); 59 | 60 | // TODO: enable me 61 | xit('should find the contact name element', function(done){ 62 | var locator = webdriver.By.css('.info_name'); 63 | driver.isElementPresent(locator).then(function (isPresent) { 64 | expect(isPresent).to.be.true; 65 | done(); 66 | }); 67 | }); 68 | 69 | it('should find the pet link', function(done){ 70 | var locator = webdriver.By.xpath('//*[@data-id="pet"]'); 71 | driver.isElementPresent(locator).then(function (isPresent) { 72 | expect(isPresent).to.be.true; 73 | done(); 74 | }); 75 | }); 76 | 77 | // TODO: enable me 78 | xit('should find the pet resource description', function(done){ 79 | var locator = webdriver.By.xpath('//div[contains(., "Operations about pets")]'); 80 | driver.findElements(locator).then(function (elements) { 81 | expect(elements.length).to.not.equal(0); 82 | done(); 83 | }); 84 | }); 85 | 86 | it('should find the user link', function(done){ 87 | var locator = webdriver.By.xpath('//*[@data-id="user"]'); 88 | driver.isElementPresent(locator).then(function (isPresent) { 89 | expect(isPresent).to.be.true; 90 | done(); 91 | }); 92 | }); 93 | 94 | it('should find the store link', function(done){ 95 | var locator = webdriver.By.xpath('//*[@data-id="store"]'); 96 | driver.isElementPresent(locator).then(function (isPresent) { 97 | expect(isPresent).to.be.true; 98 | done(); 99 | }); 100 | }); 101 | 102 | after(function(){ 103 | servers.close(); 104 | }); 105 | }); -------------------------------------------------------------------------------- /src/main/javascript/doc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | $(function () { 5 | 6 | // Helper function for vertically aligning DOM elements 7 | // http://www.seodenver.com/simple-vertical-align-plugin-for-jquery/ 8 | $.fn.vAlign = function () { 9 | return this.each(function () { 10 | var ah = $(this).height(); 11 | var ph = $(this).parent().height(); 12 | var mh = (ph - ah) / 2; 13 | $(this).css('margin-top', mh); 14 | }); 15 | }; 16 | 17 | $.fn.stretchFormtasticInputWidthToParent = function () { 18 | return this.each(function () { 19 | var p_width = $(this).closest("form").innerWidth(); 20 | var p_padding = parseInt($(this).closest("form").css('padding-left'), 10) + parseInt($(this).closest('form').css('padding-right'), 10); 21 | var this_padding = parseInt($(this).css('padding-left'), 10) + parseInt($(this).css('padding-right'), 10); 22 | $(this).css('width', p_width - p_padding - this_padding); 23 | }); 24 | }; 25 | 26 | $('form.formtastic li.string input, form.formtastic textarea').stretchFormtasticInputWidthToParent(); 27 | 28 | // Vertically center these paragraphs 29 | // Parent may need a min-height for this to work.. 30 | $('ul.downplayed li div.content p').vAlign(); 31 | 32 | // When a sandbox form is submitted.. 33 | $("form.sandbox").submit(function () { 34 | 35 | var error_free = true; 36 | 37 | // Cycle through the forms required inputs 38 | $(this).find("input.required").each(function () { 39 | 40 | // Remove any existing error styles from the input 41 | $(this).removeClass('error'); 42 | 43 | // Tack the error style on if the input is empty.. 44 | if ($(this).val() === '') { 45 | $(this).addClass('error'); 46 | $(this).wiggle(); 47 | error_free = false; 48 | } 49 | 50 | }); 51 | 52 | return error_free; 53 | }); 54 | 55 | }); 56 | 57 | function clippyCopiedCallback() { 58 | $('#api_key_copied').fadeIn().delay(1000).fadeOut(); 59 | 60 | // var b = $("#clippy_tooltip_" + a); 61 | // b.length != 0 && (b.attr("title", "copied!").trigger("tipsy.reload"), setTimeout(function() { 62 | // b.attr("title", "copy to clipboard") 63 | // }, 64 | // 500)) 65 | } 66 | 67 | // Logging function that accounts for browsers that don't have window.console 68 | function log() { 69 | log.history = log.history || []; 70 | log.history.push(arguments); 71 | if (this.console) { 72 | console.log(Array.prototype.slice.call(arguments)[0]); 73 | } 74 | } 75 | 76 | // Handle browsers that do console incorrectly (IE9 and below, see http://stackoverflow.com/a/5539378/7913) 77 | if (Function.prototype.bind && console && typeof console.log === "object") { 78 | [ 79 | "log", "info", "warn", "error", "assert", "dir", "clear", "profile", "profileEnd" 80 | ].forEach(function (method) { 81 | console[method] = this.bind(console[method], console); 82 | }, Function.prototype.call); 83 | } 84 | 85 | window.Docs = { 86 | 87 | shebang: function () { 88 | 89 | // If shebang has an operation nickname in it.. 90 | // e.g. /docs/#!/words/get_search 91 | var fragments = $.param.fragment().split('/'); 92 | fragments.shift(); // get rid of the bang 93 | 94 | switch (fragments.length) { 95 | case 1: 96 | break; 97 | case 2: 98 | var target = '#resources_nav [data-resource] [data-endpoint=' + fragments[0] + '_' + fragments[1] + ']', 99 | n = $('#swagger_sidebar').find(target), 100 | attr = n.attr('data-selected'); 101 | 102 | if (typeof attr == typeof undefined) { 103 | n.trigger("click"); 104 | } 105 | break; 106 | } 107 | 108 | } 109 | 110 | }; 111 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var es = require('event-stream'); 5 | var clean = require('gulp-clean'); 6 | var concat = require('gulp-concat'); 7 | var uglify = require('gulp-uglify'); 8 | var rename = require('gulp-rename'); 9 | var less = require('gulp-less'); 10 | var handlebars = require('gulp-handlebars'); 11 | var wrap = require('gulp-wrap'); 12 | var declare = require('gulp-declare'); 13 | var watch = require('gulp-watch'); 14 | var connect = require('gulp-connect'); 15 | var header = require('gulp-header'); 16 | var pkg = require('./package.json'); 17 | var order = require('gulp-order'); 18 | var banner = ['/**', 19 | ' * <%= pkg.name %> - <%= pkg.description %>', 20 | ' * @version v<%= pkg.version %>', 21 | ' * @link <%= pkg.homepage %>', 22 | ' * @license <%= pkg.license %>', 23 | ' */', 24 | ''].join('\n'); 25 | 26 | /** 27 | * Clean ups ./dist folder 28 | */ 29 | gulp.task('clean', function() { 30 | return gulp 31 | .src('./dist', {read: false}) 32 | .pipe(clean({force: true})) 33 | .on('error', log); 34 | }); 35 | 36 | /** 37 | * Processes Handlebars templates 38 | */ 39 | function templates() { 40 | return gulp 41 | .src(['./src/main/template/**/*']) 42 | .pipe(handlebars()) 43 | .pipe(wrap('Handlebars.template(<%= contents %>)')) 44 | .pipe(declare({ 45 | namespace: 'Handlebars.templates', 46 | noRedeclare: true, // Avoid duplicate declarations 47 | })) 48 | .on('error', log); 49 | } 50 | 51 | /** 52 | * Build a distribution 53 | */ 54 | gulp.task('dist', ['clean'], function() { 55 | 56 | return es.merge( 57 | gulp.src([ 58 | './src/main/javascript/**/*.js', 59 | './node_modules/swagger-client/browser/swagger-client.js' 60 | ]), 61 | templates() 62 | ) 63 | .pipe(order(['scripts.js', 'templates.js'])) 64 | .pipe(concat('swagger-ui.js')) 65 | .pipe(wrap('(function(){<%= contents %>}).call(this);')) 66 | .pipe(header(banner, { pkg: pkg } )) 67 | .pipe(gulp.dest('./dist')) 68 | .pipe(uglify()) 69 | .on('error', log) 70 | .pipe(rename({extname: '.min.js'})) 71 | .on('error', log) 72 | .pipe(gulp.dest('./dist')) 73 | .pipe(connect.reload()); 74 | }); 75 | 76 | /** 77 | * Processes less files into CSS files 78 | */ 79 | gulp.task('less', ['clean'], function() { 80 | 81 | return gulp 82 | .src([ 83 | './src/main/less/screen.less', 84 | './src/main/less/print.less', 85 | './src/main/less/reset.less' 86 | ]) 87 | .pipe(less()) 88 | .on('error', log) 89 | .pipe(gulp.dest('./src/main/html/css/')) 90 | .pipe(connect.reload()); 91 | }); 92 | 93 | 94 | /** 95 | * Copy lib and html folders 96 | */ 97 | gulp.task('copy', ['less'], function() { 98 | 99 | // copy JavaScript files inside lib folder 100 | gulp 101 | .src(['./lib/**/*.{js,map}']) 102 | .pipe(gulp.dest('./dist/lib')) 103 | .on('error', log); 104 | 105 | // copy all files inside html folder 106 | gulp 107 | .src(['./src/main/html/**/*']) 108 | .pipe(gulp.dest('./dist')) 109 | .on('error', log); 110 | }); 111 | 112 | /** 113 | * Watch for changes and recompile 114 | */ 115 | gulp.task('watch', function() { 116 | return watch(['./src/**/*.{js,less,handlebars}'], function() { 117 | gulp.start('default'); 118 | }); 119 | }); 120 | 121 | /** 122 | * Live reload web server of `dist` 123 | */ 124 | gulp.task('connect', function() { 125 | connect.server({ 126 | root: 'dist', 127 | livereload: true 128 | }); 129 | }); 130 | 131 | function log(error) { 132 | console.error(error.toString && error.toString()); 133 | } 134 | 135 | 136 | gulp.task('default', ['dist', 'copy']); 137 | gulp.task('serve', ['connect', 'watch']); 138 | -------------------------------------------------------------------------------- /test/e2e/v2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('chai').expect; 4 | var webdriver = require('selenium-webdriver'); 5 | var driver = require('./driver'); 6 | var servers = require('./servers'); 7 | 8 | 9 | var elements = [ 10 | 'swagger-ui-container', 11 | 'resources_container', 12 | 'api_info', 13 | 'resource_pet', 14 | 'resource_store', 15 | 'resource_user', 16 | 'header' 17 | ]; 18 | 19 | describe('swagger 2.0 spec tests', function () { 20 | this.timeout(10 * 1000); 21 | 22 | before(function (done) { 23 | this.timeout(25 * 1000); 24 | servers.start('/v2/petstore.json', done); 25 | }); 26 | 27 | afterEach(function(){ 28 | it('should not have any console errors', function (done) { 29 | driver.manage().logs().get('browser').then(function(browserLogs) { 30 | var errors = []; 31 | browserLogs.forEach(function(log){ 32 | // 900 and above is "error" level. Console should not have any errors 33 | if (log.level.value > 900) { 34 | console.log('browser error message:', log.message); errors.push(log); 35 | } 36 | }); 37 | expect(errors).to.be.empty; 38 | done(); 39 | }); 40 | }); 41 | }); 42 | 43 | it('should have "Swagger UI" in title', function (done) { 44 | driver.sleep(200); 45 | driver.getTitle().then(function(title) { 46 | expect(title).to.contain('Swagger UI'); 47 | done(); 48 | }); 49 | }); 50 | 51 | elements.forEach(function (id) { 52 | it('should render element: ' + id, function (done) { 53 | var locator = webdriver.By.id(id); 54 | driver.isElementPresent(locator).then(function (isPresent) { 55 | expect(isPresent).to.be.true; 56 | done(); 57 | }); 58 | }); 59 | }); 60 | 61 | it('should find the contact name element', function(done){ 62 | var locator = webdriver.By.css('.info_name'); 63 | driver.isElementPresent(locator).then(function (isPresent) { 64 | expect(isPresent).to.be.true; 65 | done(); 66 | }); 67 | }); 68 | 69 | it('should find the contact email element', function(done){ 70 | var locator = webdriver.By.css('.info_email'); 71 | driver.isElementPresent(locator).then(function (isPresent) { 72 | expect(isPresent).to.be.true; 73 | done(); 74 | }); 75 | }); 76 | 77 | it('should find the contact url element', function(done){ 78 | var locator = webdriver.By.css('.info_url'); 79 | driver.isElementPresent(locator).then(function (isPresent) { 80 | expect(isPresent).to.be.true; 81 | done(); 82 | }); 83 | }); 84 | 85 | it('should find the pet link', function(done){ 86 | var locator = webdriver.By.xpath('//*[@data-id="pet"]'); 87 | driver.isElementPresent(locator).then(function (isPresent) { 88 | expect(isPresent).to.be.true; 89 | done(); 90 | }); 91 | }); 92 | 93 | it('should find the pet resource description', function(done){ 94 | var locator = webdriver.By.xpath('//div[contains(., "Everything about your Pets")]'); 95 | driver.findElements(locator).then(function (elements) { 96 | expect(elements.length).to.not.equal(0); 97 | done(); 98 | }); 99 | }); 100 | 101 | it('should find the user link', function(done){ 102 | var locator = webdriver.By.xpath('//*[@data-id="user"]'); 103 | driver.isElementPresent(locator).then(function (isPresent) { 104 | expect(isPresent).to.be.true; 105 | done(); 106 | }); 107 | }); 108 | 109 | it('should find the store link', function(done){ 110 | var locator = webdriver.By.xpath('//*[@data-id="store"]'); 111 | driver.isElementPresent(locator).then(function (isPresent) { 112 | expect(isPresent).to.be.true; 113 | done(); 114 | }); 115 | }); 116 | 117 | after(function() { 118 | servers.close(); 119 | }); 120 | }); -------------------------------------------------------------------------------- /lib/jquery.ba-bbq.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010 3 | * http://benalman.com/projects/jquery-bbq-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this); -------------------------------------------------------------------------------- /dist/lib/jquery.ba-bbq.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010 3 | * http://benalman.com/projects/jquery-bbq-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this); -------------------------------------------------------------------------------- /test/specs/v1.2/petstore/store.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "1.0.0", 3 | "swaggerVersion": "1.2", 4 | "basePath": "http://petstore.swagger.io/api", 5 | "resourcePath": "/store", 6 | "produces": [ 7 | "application/json" 8 | ], 9 | "apis": [ 10 | { 11 | "path": "/store/order/{orderId}", 12 | "operations": [ 13 | { 14 | "method": "GET", 15 | "summary": "Find purchase order by ID", 16 | "notes": "For valid response try integer IDs with value <= 5. Anything above 5 or nonintegers will generate API errors", 17 | "type": "Order", 18 | "nickname": "getOrderById", 19 | "authorizations": {}, 20 | "parameters": [ 21 | { 22 | "name": "orderId", 23 | "description": "ID of pet that needs to be fetched", 24 | "required": true, 25 | "type": "string", 26 | "paramType": "path" 27 | } 28 | ], 29 | "responseMessages": [ 30 | { 31 | "code": 400, 32 | "message": "Invalid ID supplied" 33 | }, 34 | { 35 | "code": 404, 36 | "message": "Order not found" 37 | } 38 | ] 39 | }, 40 | { 41 | "method": "DELETE", 42 | "summary": "Delete purchase order by ID", 43 | "notes": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", 44 | "type": "void", 45 | "nickname": "deleteOrder", 46 | "authorizations": { 47 | "oauth2": [ 48 | { 49 | "scope": "test:anything", 50 | "description": "anything" 51 | } 52 | ] 53 | }, 54 | "parameters": [ 55 | { 56 | "name": "orderId", 57 | "description": "ID of the order that needs to be deleted", 58 | "required": true, 59 | "type": "string", 60 | "paramType": "path" 61 | } 62 | ], 63 | "responseMessages": [ 64 | { 65 | "code": 400, 66 | "message": "Invalid ID supplied" 67 | }, 68 | { 69 | "code": 404, 70 | "message": "Order not found" 71 | } 72 | ] 73 | } 74 | ] 75 | }, 76 | { 77 | "path": "/store/order", 78 | "operations": [ 79 | { 80 | "method": "POST", 81 | "summary": "Place an order for a pet", 82 | "notes": "", 83 | "type": "void", 84 | "nickname": "placeOrder", 85 | "authorizations": { 86 | "oauth2": [ 87 | { 88 | "scope": "test:anything", 89 | "description": "anything" 90 | } 91 | ] 92 | }, 93 | "parameters": [ 94 | { 95 | "name": "body", 96 | "description": "order placed for purchasing the pet", 97 | "required": true, 98 | "type": "Order", 99 | "paramType": "body" 100 | } 101 | ], 102 | "responseMessages": [ 103 | { 104 | "code": 400, 105 | "message": "Invalid order" 106 | } 107 | ] 108 | } 109 | ] 110 | } 111 | ], 112 | "models": { 113 | "Order": { 114 | "id": "Order", 115 | "description": "an order in the system", 116 | "properties": { 117 | "id": { 118 | "type": "integer", 119 | "format": "int64" 120 | }, 121 | "petId": { 122 | "type": "integer", 123 | "format": "int64" 124 | }, 125 | "quantity": { 126 | "type": "integer", 127 | "format": "int32" 128 | }, 129 | "status": { 130 | "type": "string", 131 | "description": "Order Status", 132 | "enum": [ 133 | "placed", 134 | " approved", 135 | " delivered" 136 | ] 137 | }, 138 | "shipDate": { 139 | "type": "string", 140 | "format": "date-time" 141 | } 142 | } 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /src/main/javascript/view/ParameterView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * [TODO] defaultProperties is not take in the required properties into consideration, this implementation respects the specs of JSON Editor v0.7.22 4 | { 5 | { 6 | "type": "object", 7 | "properties": { 8 | "name": {"type": "string"}, 9 | "age": {"type": "integer"} 10 | }, 11 | defaultProperties": ["name"] 12 | } 13 | */ 14 | function setDefaultProperties(obj) { 15 | if (obj instanceof Object) { 16 | for (var k in obj){ 17 | if(obj.hasOwnProperty("type") && obj.type == "object") { 18 | obj.defaultProperties = obj.required ? obj.required : []; 19 | } 20 | // recursive call to setDefaultProperties 21 | setDefaultProperties( obj[k] ); 22 | } 23 | } else { 24 | // not an Object, break the recursion. 25 | }; 26 | } 27 | 28 | SwaggerUi.Views.ParameterView = Backbone.View.extend({ 29 | initialize: function(){ 30 | Handlebars.registerHelper('isArray', function(param, opts) { 31 | if (param.type.toLowerCase() === 'array' || param.allowMultiple) { 32 | opts.fn(this); 33 | } else { 34 | opts.inverse(this); 35 | } 36 | }); 37 | }, 38 | 39 | render: function() { 40 | var type = this.model.type || this.model.dataType; 41 | 42 | if (typeof type === 'undefined') { 43 | var schema = this.model.schema; 44 | if (schema && schema.$ref) { 45 | var ref = schema.$ref; 46 | if (ref.indexOf('#/definitions/') === 0) { 47 | type = ref.substring('#/definitions/'.length); 48 | } else { 49 | type = ref; 50 | } 51 | } 52 | } 53 | 54 | this.model.type = type; 55 | this.model.paramType = this.model.in || this.model.paramType; 56 | this.model.isBody = this.model.paramType === 'body' || this.model.in === 'body'; 57 | this.model.isFile = type && type.toLowerCase() === 'file'; 58 | this.model.default = (this.model.default || this.model.defaultValue); 59 | 60 | if(this.model.format === 'password') { 61 | this.model.inputType = 'password'; 62 | } else { 63 | this.model.inputType = 'text'; 64 | } 65 | 66 | if (this.model.allowableValues) { 67 | this.model.isList = true; 68 | } 69 | 70 | var template = this.template(); 71 | $(this.el).html(template(this.model)); 72 | 73 | var signatureModel = { 74 | sampleJSON: this.model.sampleJSON, 75 | isParam: true, 76 | signature: this.model.signature, 77 | defaultRendering: this.model.defaultRendering 78 | }; 79 | 80 | var isParam = false; 81 | 82 | if( this.options.swaggerOptions.jsonEditor && this.model.isBody && this.model.schema){ 83 | var jsonEditorOptions = this.options.swaggerOptions.jsonEditorOptions; 84 | var $self = $(this.el); 85 | if (jsonEditorOptions && jsonEditorOptions.noDefaultProperties) setDefaultProperties(this.model.schema); 86 | this.model.jsonEditor = 87 | /* global JSONEditor */ 88 | new JSONEditor($('.editor_holder', $self)[0], 89 | {schema: this.model.schema, startval : this.model.default, 90 | ajax:true, 91 | disable_properties:jsonEditorOptions && jsonEditorOptions.disableProperties, 92 | disable_edit_json:jsonEditorOptions && jsonEditorOptions.disableEditJson, 93 | remove_empty_properties:jsonEditorOptions && jsonEditorOptions.removeEmptyProperties, 94 | iconlib: 'swagger' }); 95 | // This is so that the signature can send back the sample to the json editor 96 | // TODO: SignatureView should expose an event "onSampleClicked" instead 97 | signatureModel.jsonEditor = this.model.jsonEditor; 98 | $('.body-textarea', $self).hide(); 99 | $('.editor_holder', $self).show(); 100 | $('.parameter-content-type', $self) 101 | .change(function(e){ 102 | if(e.target.value === 'application/xml'){ 103 | $('.body-textarea', $self).show(); 104 | $('.editor_holder', $self).hide(); 105 | this.model.jsonEditor.disable(); 106 | } 107 | else { 108 | $('.body-textarea', $self).hide(); 109 | $('.editor_holder', $self).show(); 110 | this.model.jsonEditor.enable(); 111 | } 112 | }); 113 | } 114 | 115 | if (this.model.isBody) { 116 | isParam = true; 117 | } 118 | 119 | var contentTypeModel = { 120 | isParam: isParam 121 | }; 122 | 123 | contentTypeModel.consumes = this.model.consumes; 124 | 125 | if (isParam) { 126 | var parameterContentTypeView = new SwaggerUi.Views.ParameterContentTypeView({model: contentTypeModel}); 127 | $('.parameter-content-type', $(this.el)).append(parameterContentTypeView.render().el); 128 | } 129 | 130 | else { 131 | var responseContentTypeView = new SwaggerUi.Views.ResponseContentTypeView({model: contentTypeModel}); 132 | $('.response-content-type', $(this.el)).append(responseContentTypeView.render().el); 133 | } 134 | 135 | return this; 136 | }, 137 | 138 | // Return an appropriate template based on if the parameter is a list, readonly, required 139 | template: function(){ 140 | if (this.model.isList) { 141 | return Handlebars.templates.param_list; 142 | } else { 143 | if (this.options.readOnly) { 144 | if (this.model.required) { 145 | return Handlebars.templates.param_readonly_required; 146 | } else { 147 | return Handlebars.templates.param_readonly; 148 | } 149 | } else { 150 | if (this.model.required) { 151 | return Handlebars.templates.param_required; 152 | } else { 153 | return Handlebars.templates.param; 154 | } 155 | } 156 | } 157 | } 158 | }); -------------------------------------------------------------------------------- /src/main/template/operation.handlebars: -------------------------------------------------------------------------------- 1 |
      2 |
    • 3 |
      4 |
      5 |

      {{summary}}

      6 | 7 |

      8 | 9 | {{method}} 10 | 11 | 12 | {{path}} 14 | 15 |

      16 |
      17 | 18 | 20 | Show samples 21 | 22 | 23 | {{#if deprecated}} 24 |

      Warning: Deprecated

      25 | {{/if}} 26 | {{#if description}} 27 |
      {{{description}}}
      28 | {{/if}} 29 | 30 | 31 |
      32 | 33 | {{#if parameters}} 34 |

      36 | Parameters 37 |

      38 | 39 |
      40 |
      41 | 42 | {{/if}} 43 | 44 |

      45 | Test this endpoint 46 |

      47 | 48 |
      49 |
      50 | 52 | Hide Response 53 | 54 | 55 | 56 | {{#oauth}} 57 |
      58 | {{/oauth}} 59 | 60 | {{#each oauth}} 61 | 66 | {{/each}} 67 | 68 | {{#oauth}} 69 | 70 |
      71 | {{/oauth}} 72 | 73 |
      74 | 75 | {{#if type}} 76 |
      77 | {{/if}} 78 | 79 |
      80 | 81 | {{#if responseMessages}} 82 |
      83 |

      84 | Response Messages 85 |

      86 | 87 |
      88 | 89 | 90 | 91 |
      92 |
      93 | {{/if}} 94 | 95 | 96 |
      97 | 98 |
      99 | 100 | 101 |
      102 | 103 | 138 |
    • 139 |
    -------------------------------------------------------------------------------- /lib/highlight.7.3.pack.js: -------------------------------------------------------------------------------- 1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
    ")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs); -------------------------------------------------------------------------------- /dist/lib/highlight.7.3.pack.js: -------------------------------------------------------------------------------- 1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
    ")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs); -------------------------------------------------------------------------------- /src/main/javascript/SwaggerUi.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | window.SwaggerUi = Backbone.Router.extend({ 4 | 5 | dom_id: 'swagger_ui', 6 | 7 | // Attributes 8 | options: null, 9 | api: null, 10 | headerView: null, 11 | mainView: null, 12 | 13 | // SwaggerUi accepts all the same options as SwaggerApi 14 | initialize: function(options) { 15 | options = options || {}; 16 | 17 | // Allow dom_id to be overridden 18 | if (options.dom_id) { 19 | this.dom_id = options.dom_id; 20 | delete options.dom_id; 21 | } 22 | 23 | if (!options.supportedSubmitMethods){ 24 | options.supportedSubmitMethods = [ 25 | 'get', 26 | 'put', 27 | 'post', 28 | 'delete', 29 | 'head', 30 | 'options', 31 | 'patch' 32 | ]; 33 | } 34 | 35 | if (typeof options.oauth2RedirectUrl === 'string') { 36 | window.oAuthRedirectUrl = options.redirectUrl; 37 | } 38 | 39 | // Create an empty div which contains the dom_id 40 | if (! $('#' + this.dom_id).length){ 41 | $('body').append('
    ') ; 42 | } 43 | 44 | this.options = options; 45 | 46 | // set marked options 47 | marked.setOptions({gfm: true}); 48 | 49 | // Set the callbacks 50 | var that = this; 51 | this.options.success = function() { return that.render(); }; 52 | this.options.progress = function(d) { return that.showMessage(d); }; 53 | this.options.failure = function(d) { return that.onLoadFailure(d); }; 54 | 55 | // Create view to handle the header inputs 56 | //this.headerView = new SwaggerUi.Views.HeaderView({el: $('#header')}); 57 | 58 | // Event handler for when the baseUrl/apiKey is entered by user 59 | /* 60 | this.headerView.on('update-swagger-ui', function(data) { 61 | return that.updateSwaggerUi(data); 62 | }); 63 | */ 64 | //JSon Editor custom theming 65 | JSONEditor.defaults.iconlibs.swagger = JSONEditor.AbstractIconLib.extend({ 66 | mapping: { 67 | collapse: 'collapse', 68 | expand: 'expand' 69 | }, 70 | icon_prefix: 'swagger-' 71 | }); 72 | }, 73 | 74 | // Set an option after initializing 75 | setOption: function(option, value) { 76 | this.options[option] = value; 77 | }, 78 | 79 | // Get the value of a previously set option 80 | getOption: function(option) { 81 | return this.options[option]; 82 | }, 83 | 84 | // Event handler for when url/key is received from user 85 | updateSwaggerUi: function(data){ 86 | this.options.url = data.url; 87 | this.options.apiKey = data.apiKey; 88 | this.load(); 89 | }, 90 | 91 | // Create an api and render 92 | load: function(){ 93 | 94 | // Initialize the API object 95 | if (this.mainView) { 96 | this.mainView.undelegateEvents(); 97 | this.mainView.clear(); 98 | } 99 | 100 | var url = this.options.url; 101 | if (url && url.indexOf('http') !== 0) { 102 | url = this.buildUrl(window.location.href.toString(), url); 103 | } 104 | 105 | this.options.url = url; 106 | 107 | this.api = new SwaggerClient(this.options); 108 | 109 | }, 110 | 111 | // collapse all sections 112 | collapseAll: function(){ 113 | Docs.collapseEndpointListForResource(''); 114 | }, 115 | 116 | // list operations for all sections 117 | listAll: function(){ 118 | Docs.collapseOperationsForResource(''); 119 | }, 120 | 121 | // expand operations for all sections 122 | expandAll: function(){ 123 | Docs.expandOperationsForResource(''); 124 | }, 125 | 126 | // This is bound to success handler for SwaggerApi 127 | // so it gets called when SwaggerApi completes loading 128 | render: function(){ 129 | this.showMessage('Finished Loading Resource Information. Rendering Swagger UI...'); 130 | this.mainView = new SwaggerUi.Views.MainView({ 131 | model: this.api, 132 | el: $('#' + this.dom_id), 133 | swaggerOptions: this.options, 134 | router: this 135 | }).render(); 136 | this.showMessage(); 137 | switch (this.options.docExpansion) { 138 | case 'full': 139 | this.expandAll(); break; 140 | case 'list': 141 | this.listAll(); break; 142 | default: 143 | break; 144 | } 145 | this.renderGFM(); 146 | 147 | if (this.options.onComplete){ 148 | this.options.onComplete(this.api, this); 149 | } 150 | 151 | var that= this; 152 | 153 | // Event handler for when the baseUrl/apiKey is entered by user 154 | this.mainView.on('update-swagger-ui', function(data) { 155 | return that.updateSwaggerUi(data); 156 | }); 157 | 158 | setTimeout(Docs.shebang.bind(this), 100); 159 | }, 160 | 161 | buildUrl: function(base, url){ 162 | if (url.indexOf('/') === 0) { 163 | var parts = base.split('/'); 164 | base = parts[0] + '//' + parts[2]; 165 | return base + url; 166 | } else { 167 | var endOfPath = base.length; 168 | 169 | if (base.indexOf('?') > -1){ 170 | endOfPath = Math.min(endOfPath, base.indexOf('?')); 171 | } 172 | 173 | if (base.indexOf('#') > -1){ 174 | endOfPath = Math.min(endOfPath, base.indexOf('#')); 175 | } 176 | 177 | base = base.substring(0, endOfPath); 178 | 179 | if (base.indexOf('/', base.length - 1 ) !== -1){ 180 | return base + url; 181 | } 182 | 183 | return base + '/' + url; 184 | } 185 | }, 186 | 187 | // Shows message on topbar of the ui 188 | showMessage: function(data){ 189 | if (data === undefined) { 190 | data = ''; 191 | } 192 | $('#message-bar').removeClass('message-fail'); 193 | $('#message-bar').addClass('message-success'); 194 | $('#message-bar').html(data); 195 | }, 196 | 197 | // shows message in red 198 | onLoadFailure: function(data){ 199 | if (data === undefined) { 200 | data = ''; 201 | } 202 | $('#message-bar').removeClass('message-success'); 203 | $('#message-bar').addClass('message-fail'); 204 | 205 | var val = $('#message-bar').html(data); 206 | 207 | if (this.options.onFailure) { 208 | this.options.onFailure(data); 209 | } 210 | 211 | return val; 212 | }, 213 | 214 | // Renders GFM for elements with 'markdown' class 215 | renderGFM: function(){ 216 | $('.markdown').each(function(){ 217 | $(this).html(marked($(this).html())); 218 | }); 219 | } 220 | 221 | }); 222 | 223 | window.SwaggerUi.Views = {}; 224 | 225 | // don't break backward compatibility with previous versions and warn users to upgrade their code 226 | (function(){ 227 | window.authorizations = { 228 | add: function() { 229 | warn('Using window.authorizations is deprecated. Please use SwaggerUi.api.clientAuthorizations.add().'); 230 | 231 | if (typeof window.swaggerUi === 'undefined') { 232 | throw new TypeError('window.swaggerUi is not defined'); 233 | } 234 | 235 | if (window.swaggerUi instanceof SwaggerUi) { 236 | window.swaggerUi.api.clientAuthorizations.add.apply(window.swaggerUi.api.clientAuthorizations, arguments); 237 | } 238 | } 239 | }; 240 | 241 | window.ApiKeyAuthorization = function() { 242 | warn('window.ApiKeyAuthorization is deprecated. Please use SwaggerClient.ApiKeyAuthorization.'); 243 | SwaggerClient.ApiKeyAuthorization.apply(window, arguments); 244 | }; 245 | 246 | window.PasswordAuthorization = function() { 247 | warn('window.PasswordAuthorization is deprecated. Please use SwaggerClient.PasswordAuthorization.'); 248 | SwaggerClient.PasswordAuthorization.apply(window, arguments); 249 | }; 250 | 251 | function warn(message) { 252 | if ('console' in window && typeof window.console.warn === 'function') { 253 | console.warn(message); 254 | } 255 | } 256 | })(); 257 | 258 | 259 | // UMD 260 | (function (root, factory) { 261 | if (typeof define === 'function' && define.amd) { 262 | // AMD. Register as an anonymous module. 263 | define(['b'], function (b) { 264 | return (root.SwaggerUi = factory(b)); 265 | }); 266 | } else if (typeof exports === 'object') { 267 | // Node. Does not work with strict CommonJS, but 268 | // only CommonJS-like enviroments that support module.exports, 269 | // like Node. 270 | module.exports = factory(require('b')); 271 | } else { 272 | // Browser globals 273 | root.SwaggerUi = factory(root.b); 274 | } 275 | }(this, function () { 276 | return SwaggerUi; 277 | })); -------------------------------------------------------------------------------- /src/main/javascript/view/MainView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | SwaggerUi.Views.MainView = Backbone.View.extend({ 4 | 5 | events: { 6 | 'click .mobile-nav, [data-navigator]': 'clickSidebarNav', 7 | 'click [data-resource]': 'clickResource', 8 | 'click [data-tg-switch]': 'toggleToken', 9 | 'click [data-close]': 'closeToken', 10 | 'click #explore' : 'showCustom' 11 | }, 12 | 13 | apisSorter: { 14 | alpha: function (a, b) { 15 | return a.name.localeCompare(b.name); 16 | } 17 | }, 18 | operationsSorters: { 19 | alpha: function (a, b) { 20 | return a.path.localeCompare(b.path); 21 | }, 22 | method: function (a, b) { 23 | return a.method.localeCompare(b.method); 24 | } 25 | }, 26 | initialize: function (opts) { 27 | var sorterOption, sorterFn, key, value; 28 | opts = opts || {}; 29 | 30 | this.router = opts.router; 31 | 32 | // Sort APIs 33 | if (opts.swaggerOptions.apisSorter) { 34 | sorterOption = opts.swaggerOptions.apisSorter; 35 | if (_.isFunction(sorterOption)) { 36 | sorterFn = sorterOption; 37 | } else { 38 | sorterFn = this.apisSorter[sorterOption]; 39 | } 40 | if (_.isFunction(sorterFn)) { 41 | this.model.apisArray.sort(sorterFn); 42 | } 43 | } 44 | // Sort operations of each API 45 | if (opts.swaggerOptions.operationsSorter) { 46 | sorterOption = opts.swaggerOptions.operationsSorter; 47 | if (_.isFunction(sorterOption)) { 48 | sorterFn = sorterOption; 49 | } else { 50 | sorterFn = this.operationsSorters[sorterOption]; 51 | } 52 | if (_.isFunction(sorterFn)) { 53 | for (key in this.model.apisArray) { 54 | this.model.apisArray[key].operationsArray.sort(sorterFn); 55 | } 56 | } 57 | } 58 | 59 | // set up the UI for input 60 | this.model.auths = []; 61 | 62 | for (key in this.model.securityDefinitions) { 63 | value = this.model.securityDefinitions[key]; 64 | 65 | this.model.auths.push({ 66 | name: key, 67 | type: value.type, 68 | value: value 69 | }); 70 | } 71 | 72 | if (this.model.swaggerVersion === '2.0') { 73 | if ('validatorUrl' in opts.swaggerOptions) { 74 | 75 | // Validator URL specified explicitly 76 | this.model.validatorUrl = opts.swaggerOptions.validatorUrl; 77 | 78 | } else if (this.model.url.indexOf('localhost') > 0) { 79 | 80 | // Localhost override 81 | this.model.validatorUrl = null; 82 | 83 | } else { 84 | 85 | // Default validator 86 | this.model.validatorUrl = window.location.protocol + '//online.swagger.io/validator'; 87 | } 88 | } 89 | // JSonEditor requires type='object' to be present on defined types, we add it if it's missing 90 | // is there any valid case were it should not be added ? 91 | var def; 92 | for(def in this.model.definitions){ 93 | if (!this.model.definitions[def].type){ 94 | this.model.definitions[def].type = 'object'; 95 | } 96 | } 97 | }, 98 | 99 | render: function () { 100 | if (this.model.securityDefinitions) { 101 | for (var name in this.model.securityDefinitions) { 102 | var auth = this.model.securityDefinitions[name]; 103 | var button; 104 | 105 | if (auth.type === 'apiKey' && $('#apikey_button').length === 0) { 106 | button = new SwaggerUi.Views.ApiKeyButton({model: auth, router: this.router}).render().el; 107 | $('.auth_main_container').append(button); 108 | } 109 | 110 | if (auth.type === 'basicAuth' && $('#basic_auth_button').length === 0) { 111 | button = new SwaggerUi.Views.BasicAuthButton({model: auth, router: this.router}).render().el; 112 | $('.auth_main_container').append(button); 113 | } 114 | } 115 | } 116 | 117 | // Render the outer container for resources 118 | $(this.el).html(Handlebars.templates.main(this.model)); 119 | 120 | // Render each resource 121 | 122 | var resources = {}; 123 | var counter = 0; 124 | for (var i = 0; i < this.model.apisArray.length; i++) { 125 | var resource = this.model.apisArray[i]; 126 | var id = resource.name; 127 | while (typeof resources[id] !== 'undefined') { 128 | id = id + '_' + counter; 129 | counter += 1; 130 | } 131 | resource.id = id; 132 | resources[id] = resource; 133 | resource.nmbr = i; 134 | 135 | this.addResource(resource, this.model.auths); 136 | this.addSidebarHeader(resource, i); 137 | } 138 | 139 | $('.propWrap').hover(function onHover() { 140 | $('.optionsWrapper', $(this)).show(); 141 | }, function offhover() { 142 | $('.optionsWrapper', $(this)).hide(); 143 | }); 144 | 145 | if (window.location.hash.length === 0 ) { 146 | var n = $(this.el).find("#resources_nav [data-resource]").first(); 147 | n.trigger("click"); 148 | $(window).scrollTop(0) 149 | } 150 | 151 | return this; 152 | }, 153 | 154 | addResource: function (resource, auths) { 155 | // Render a resource and add it to resources li 156 | resource.id = resource.id.replace(/\s/g, '_'); 157 | 158 | // Make all definitions available at the root of the resource so that they can 159 | // be loaded by the JSonEditor 160 | resource.definitions = this.model.definitions; 161 | var resourceView = new SwaggerUi.Views.ResourceView({ 162 | model: resource, 163 | router: this.router, 164 | tagName: 'li', 165 | id: 'resource_' + resource.id, 166 | className: 'resource', 167 | auths: auths, 168 | swaggerOptions: this.options.swaggerOptions 169 | }); 170 | $('#resources').append(resourceView.render().el); 171 | }, 172 | 173 | addSidebarToken: function (resource, i) { 174 | resource.id = resource.id.replace(/\s/g, '_'); 175 | var sidebarView = new SwaggerUi.Views.SidebarHeaderView({ 176 | model: resource, 177 | tagName: 'div', 178 | className: function () { 179 | return i == 0 ? 'active' : '' 180 | }, 181 | attributes: { 182 | "data-resource": 'resource_' + resource.name, 183 | "label": resource.name 184 | }, 185 | router: this.router, 186 | swaggerOptions: this.options.swaggerOptions 187 | }); 188 | $('#token-generator', $(this.el)).append(sidebarView.render().el); 189 | }, 190 | 191 | 192 | addSidebarHeader: function (resource, i) { 193 | resource.id = resource.id.replace(/\s/g, '_'); 194 | var sidebarView = new SwaggerUi.Views.SidebarHeaderView({ 195 | model: resource, 196 | tagName: 'div', 197 | className: function () { 198 | return i == 0 ? 'active' : '' 199 | }, 200 | attributes: { 201 | "data-resource": 'resource_' + resource.name, 202 | "label": resource.name 203 | }, 204 | router: this.router, 205 | swaggerOptions: this.options.swaggerOptions 206 | }); 207 | $('#resources_nav', $(this.el)).append(sidebarView.render().el); 208 | }, 209 | 210 | clear: function () { 211 | $(this.el).html(''); 212 | }, 213 | 214 | clickSidebarNav: function (e) { 215 | $('.sticky-nav').toggleClass("nav-open") 216 | }, 217 | 218 | clickResource: function (e) { 219 | if (!$(e.target).is(".item")) { 220 | var n = $(e.target).find(".item").first(); 221 | $('.sticky-nav').find("[data-resource].active").removeClass("active"); 222 | $(e.target).find("[data-resource]").first().addClass("active"); 223 | n.trigger("click") 224 | } 225 | }, 226 | 227 | toggleToken: function (e) { 228 | var t = $(".token-generator"), 229 | tg = $("[data-tg-switch]"); 230 | 231 | t.toggleClass("hide"); 232 | t.hasClass("hide") ? tg.removeClass("active") : tg.addClass("active"); 233 | t.parents(".sticky-nav").trigger("mobile_nav:update") 234 | }, 235 | 236 | closeToken: function (e) { 237 | var t = $(".token-generator"), 238 | tg = $("[data-tg-switch]"); 239 | 240 | t.addClass("hide"); 241 | tg.removeClass("active"); 242 | t.parents(".sticky-nav").trigger("mobile_nav:update") 243 | }, 244 | 245 | openToken: function (e) { 246 | var t = $(".token-generator"), 247 | tg = $("[data-tg-switch]"); 248 | 249 | t.removeClass("hide"); 250 | tg.removeClass("active"); 251 | t.parents(".sticky-nav").trigger("mobile_nav:update") 252 | }, 253 | 254 | showCustom: function(e){ 255 | if (e) { 256 | e.preventDefault(); 257 | } 258 | this.trigger('update-swagger-ui', { 259 | url: $('#input_baseUrl').val(), 260 | apiKey: $('#input_apiKey').val() 261 | }); 262 | } 263 | }); 264 | -------------------------------------------------------------------------------- /dist/css/standalone.css: -------------------------------------------------------------------------------- 1 | /*========== Header ==========*/ 2 | header.site-header { 3 | z-index: 1000; 4 | background: transparent; 5 | font-size: 14px; 6 | position: static; 7 | padding: 20px 0 20px 0; 8 | } 9 | header.site-header.header-fixed { 10 | background: rgba(255,255,255,0.8); 11 | position: fixed; 12 | } 13 | header.site-header a { 14 | color: #333; 15 | } 16 | header.site-header li * { 17 | -webkit-backface-visibility: hidden !important; 18 | } 19 | header.site-header nav { 20 | background: none; 21 | border: 0; 22 | margin: 0; 23 | } 24 | header.site-header nav li.active a { 25 | background: none; 26 | color: #000; 27 | } 28 | header.site-header nav ul, 29 | header.site-header nav li { 30 | margin: 0; 31 | } 32 | header.site-header nav .navbar-brand { 33 | font-weight: bold; 34 | margin: 0; 35 | display: inline-block; 36 | float: left; 37 | font-size: 24px; 38 | margin-right: 20px; 39 | color: #000; 40 | padding: 0 15px 0 15px; 41 | margin: 0; 42 | width: 120px; 43 | } 44 | header.site-header nav .navbar-brand a { 45 | background: url("../images/senodio.png") center left no-repeat; 46 | width: 130px; 47 | height: 40px; 48 | display: block; 49 | margin-top: 5px; 50 | background-size: 48%; 51 | } 52 | header.site-header nav .navbar-brand img { 53 | position: relative; 54 | height: 36px; 55 | margin-right: 10px; 56 | top: 2px; 57 | display: none; 58 | } 59 | header.site-header nav .navbar-brand span { 60 | display: none; 61 | } 62 | header.site-header .navbar-default .navbar-collapse { 63 | border-color: transparent; 64 | } 65 | header.site-header .navbar-default .navbar-nav>.active>a, 66 | header.site-header .navbar-default .navbar-default .navbar-nav>.active>a:hover, 67 | header.site-header .navbar-default .navbar-default .navbar-nav>.active>a:focus { 68 | background: transparent; 69 | } 70 | header.site-header .navbar-default .navbar-nav>.open>a { 71 | background: none; 72 | } 73 | header.site-header .navbar-toggle { 74 | border: 0; 75 | } 76 | header.site-header .navbar-toggle:hover { 77 | background: none; 78 | } 79 | header.site-header .navbar-toggle:hover .icon-bar { 80 | background: #000; 81 | } 82 | header.site-header .btn-dro { 83 | padding: 15px; 84 | line-height: 20px; 85 | background: none; 86 | display: inline-block; 87 | color: #777; 88 | cursor: pointer; 89 | } 90 | header.site-header .btn-dro:hover { 91 | color: #666; 92 | } 93 | header.site-header .btn-dro i { 94 | position: relative; 95 | top: 1px; 96 | margin-left: 5px; 97 | width: 6px; 98 | display: inline-block; 99 | font-size: 10px; 100 | } 101 | header.site-header .dropdown .dropdown-menu>.active>a, 102 | header.site-header .dropdown .dropdown-menu>.active>a:hover, 103 | header.site-header .dropdown .dropdown-menu>.active>a:focus { 104 | background-color: none; 105 | } 106 | header.site-header .dropdown .dropdown-menu { 107 | min-width: 130px; 108 | background: #fff; 109 | -webkit-transform: scale(0.8) translateY(-10%); 110 | transform: scale(0.8) translateY(-10%); 111 | transition: 0.1s cubic-bezier(0.3, 0, 0, 1.3); 112 | opacity: 0; 113 | pointer-events: none; 114 | display: block; 115 | left: 0; 116 | border: 0; 117 | border-radius: 3px; 118 | } 119 | header.site-header .dropdown .dropdown-menu ::after { 120 | position: absolute; 121 | top: -6px; 122 | left: 26px; 123 | display: inline-block; 124 | border-right: 6px solid transparent; 125 | border-bottom: 6px solid #fff; 126 | border-left: 6px solid transparent; 127 | content: ''; 128 | } 129 | header.site-header .dropdown .dropdown-menu .divider { 130 | background-color: rgba(0,0,0,0.1); 131 | } 132 | header.site-header .dropdown.open .dropdown-menu { 133 | opacity: 1; 134 | pointer-events: auto; 135 | -webkit-transform: none; 136 | transform: none; 137 | } 138 | header.site-header a.login { 139 | color: #666; 140 | border: 1px solid #f1f1f1; 141 | border-radius: 100px; 142 | margin-left: 10px; 143 | text-transform: uppercase; 144 | letter-spacing: 1px; 145 | font-weight: bold; 146 | padding: 0; 147 | line-height: 30px; 148 | display: inline-block; 149 | padding-left: 15px; 150 | padding-right: 14px; 151 | margin-top: 10px; 152 | font-size: 12px; 153 | } 154 | header.site-header a.login:hover { 155 | background: #f1f1f1; 156 | } 157 | /*========== Header with dark background behind ==========*/ 158 | @media screen and (min-width: 990px) { 159 | .header-dark header.site-header { 160 | position: absolute; 161 | left: 0; 162 | right: 0; 163 | top: 0; 164 | border-bottom: 1px solid rgba(255,255,255,0.1); 165 | } 166 | .header-dark header.site-header h1.navbar-brand a { 167 | background-image: url("/auth0-styleguide/img/logo-grey-horizontal.png"); 168 | } 169 | .header-dark header.site-header .navbar-default .navbar-nav>.active>a, 170 | .header-dark header.site-header .navbar-default .navbar-nav>.active>a:hover, 171 | .header-dark header.site-header .navbar-default .navbar-nav>.active>a:focus { 172 | color: #fff; 173 | } 174 | .header-dark header.site-header .dropdown a { 175 | color: #333; 176 | } 177 | .header-dark header.site-header .navbar-default .navbar-nav>li>a { 178 | color: #d0d2d3; 179 | } 180 | .header-dark header.site-header .navbar-default .navbar-nav>li>a:hover { 181 | color: #fff; 182 | background: none; 183 | } 184 | .header-dark header.site-header nav li .btn-dro { 185 | color: #d0d2d3; 186 | } 187 | .header-dark header.site-header nav li .btn-dro:hover { 188 | color: #fff; 189 | } 190 | .header-dark header.site-header a.btn { 191 | border-color: #fff; 192 | } 193 | .header-dark header.site-header a.btn:hover { 194 | border-color: #fff; 195 | color: #000; 196 | } 197 | .site-header.navbar-fixed-top, 198 | .site-header.navbar-fixed-bottom { 199 | background: #fff; 200 | position: fixed !important; 201 | right: 0; 202 | left: 0; 203 | z-index: 1030; 204 | transform: translate3d(0, 0, 0); 205 | -webkit-transform: translate3d(0, 0, 0); 206 | background: rgba(255,255,255,0.9); 207 | } 208 | } 209 | .navbar-header { 210 | position: relative; 211 | } 212 | a.hiring { 213 | background: #44c7f4; 214 | text-transform: uppercase; 215 | position: absolute; 216 | top: 16px; 217 | left: -104px; 218 | font-weight: bold; 219 | padding: 2px 6px; 220 | border-radius: 3px; 221 | color: #fff !important; 222 | display: inline-block; 223 | font-size: 11px; 224 | } 225 | a.hiring::after { 226 | position: absolute; 227 | content: ""; 228 | right: -4px; 229 | top: 5px; 230 | width: 0; 231 | height: 0; 232 | border-top: 5px solid transparent; 233 | border-bottom: 5px solid transparent; 234 | border-left: 5px solid #44c7f4; 235 | } 236 | /*========== Responsive ==========*/ 237 | @media screen and (max-width: 990px) { 238 | header.site-header { 239 | position: static; 240 | text-align: left; 241 | padding: 10px 0; 242 | } 243 | header.site-header .navbar-left { 244 | border-top: 1px solid #f1f1f1; 245 | } 246 | header.site-header li { 247 | padding: 8px 0; 248 | border-bottom: 1px solid #f1f1f1; 249 | } 250 | header.site-header li:last-child { 251 | border-bottom: 0; 252 | } 253 | header.site-header li a { 254 | display: block; 255 | } 256 | header.site-header li.dropdown { 257 | display: block; 258 | padding: 0; 259 | opacity: 1; 260 | overflow: hidden; 261 | } 262 | header.site-header li.dropdown .btn-dro { 263 | display: none; 264 | } 265 | header.site-header li.dropdown .divider { 266 | display: none; 267 | } 268 | header.site-header li.dropdown ul.dropdown-menu { 269 | min-width: 0; 270 | position: static; 271 | margin: 0; 272 | box-shadow: none; 273 | border-left: 4px solid #ccc; 274 | opacity: 1; 275 | transform: none; 276 | transition: none; 277 | border-radius: 0; 278 | margin: 0; 279 | padding: 0; 280 | width: 100%; 281 | background: #f8f8f8; 282 | pointer-events: all; 283 | } 284 | header.site-header li.dropdown ul.dropdown-menu li:last-child { 285 | border-bottom: 0; 286 | } 287 | header.site-header nav a.login { 288 | margin-bottom: 20px; 289 | font-size: 14px; 290 | padding: 10px; 291 | display: block; 292 | text-align: center; 293 | } 294 | } -------------------------------------------------------------------------------- /src/main/html/css/standalone.css: -------------------------------------------------------------------------------- 1 | /*========== Header ==========*/ 2 | header.site-header { 3 | z-index: 1000; 4 | background: transparent; 5 | font-size: 14px; 6 | position: static; 7 | padding: 20px 0 20px 0; 8 | } 9 | header.site-header.header-fixed { 10 | background: rgba(255,255,255,0.8); 11 | position: fixed; 12 | } 13 | header.site-header a { 14 | color: #333; 15 | } 16 | header.site-header li * { 17 | -webkit-backface-visibility: hidden !important; 18 | } 19 | header.site-header nav { 20 | background: none; 21 | border: 0; 22 | margin: 0; 23 | } 24 | header.site-header nav li.active a { 25 | background: none; 26 | color: #000; 27 | } 28 | header.site-header nav ul, 29 | header.site-header nav li { 30 | margin: 0; 31 | } 32 | header.site-header nav .navbar-brand { 33 | font-weight: bold; 34 | margin: 0; 35 | display: inline-block; 36 | float: left; 37 | font-size: 24px; 38 | margin-right: 20px; 39 | color: #000; 40 | padding: 0 15px 0 15px; 41 | margin: 0; 42 | width: 120px; 43 | } 44 | header.site-header nav .navbar-brand a { 45 | background: url("../images/senodio.png") center left no-repeat; 46 | width: 130px; 47 | height: 40px; 48 | display: block; 49 | margin-top: 5px; 50 | background-size: 48%; 51 | } 52 | header.site-header nav .navbar-brand img { 53 | position: relative; 54 | height: 36px; 55 | margin-right: 10px; 56 | top: 2px; 57 | display: none; 58 | } 59 | header.site-header nav .navbar-brand span { 60 | display: none; 61 | } 62 | header.site-header .navbar-default .navbar-collapse { 63 | border-color: transparent; 64 | } 65 | header.site-header .navbar-default .navbar-nav>.active>a, 66 | header.site-header .navbar-default .navbar-default .navbar-nav>.active>a:hover, 67 | header.site-header .navbar-default .navbar-default .navbar-nav>.active>a:focus { 68 | background: transparent; 69 | } 70 | header.site-header .navbar-default .navbar-nav>.open>a { 71 | background: none; 72 | } 73 | header.site-header .navbar-toggle { 74 | border: 0; 75 | } 76 | header.site-header .navbar-toggle:hover { 77 | background: none; 78 | } 79 | header.site-header .navbar-toggle:hover .icon-bar { 80 | background: #000; 81 | } 82 | header.site-header .btn-dro { 83 | padding: 15px; 84 | line-height: 20px; 85 | background: none; 86 | display: inline-block; 87 | color: #777; 88 | cursor: pointer; 89 | } 90 | header.site-header .btn-dro:hover { 91 | color: #666; 92 | } 93 | header.site-header .btn-dro i { 94 | position: relative; 95 | top: 1px; 96 | margin-left: 5px; 97 | width: 6px; 98 | display: inline-block; 99 | font-size: 10px; 100 | } 101 | header.site-header .dropdown .dropdown-menu>.active>a, 102 | header.site-header .dropdown .dropdown-menu>.active>a:hover, 103 | header.site-header .dropdown .dropdown-menu>.active>a:focus { 104 | background-color: none; 105 | } 106 | header.site-header .dropdown .dropdown-menu { 107 | min-width: 130px; 108 | background: #fff; 109 | -webkit-transform: scale(0.8) translateY(-10%); 110 | transform: scale(0.8) translateY(-10%); 111 | transition: 0.1s cubic-bezier(0.3, 0, 0, 1.3); 112 | opacity: 0; 113 | pointer-events: none; 114 | display: block; 115 | left: 0; 116 | border: 0; 117 | border-radius: 3px; 118 | } 119 | header.site-header .dropdown .dropdown-menu ::after { 120 | position: absolute; 121 | top: -6px; 122 | left: 26px; 123 | display: inline-block; 124 | border-right: 6px solid transparent; 125 | border-bottom: 6px solid #fff; 126 | border-left: 6px solid transparent; 127 | content: ''; 128 | } 129 | header.site-header .dropdown .dropdown-menu .divider { 130 | background-color: rgba(0,0,0,0.1); 131 | } 132 | header.site-header .dropdown.open .dropdown-menu { 133 | opacity: 1; 134 | pointer-events: auto; 135 | -webkit-transform: none; 136 | transform: none; 137 | } 138 | header.site-header a.login { 139 | color: #666; 140 | border: 1px solid #f1f1f1; 141 | border-radius: 100px; 142 | margin-left: 10px; 143 | text-transform: uppercase; 144 | letter-spacing: 1px; 145 | font-weight: bold; 146 | padding: 0; 147 | line-height: 30px; 148 | display: inline-block; 149 | padding-left: 15px; 150 | padding-right: 14px; 151 | margin-top: 10px; 152 | font-size: 12px; 153 | } 154 | header.site-header a.login:hover { 155 | background: #f1f1f1; 156 | } 157 | /*========== Header with dark background behind ==========*/ 158 | @media screen and (min-width: 990px) { 159 | .header-dark header.site-header { 160 | position: absolute; 161 | left: 0; 162 | right: 0; 163 | top: 0; 164 | border-bottom: 1px solid rgba(255,255,255,0.1); 165 | } 166 | .header-dark header.site-header h1.navbar-brand a { 167 | background-image: url("/auth0-styleguide/img/logo-grey-horizontal.png"); 168 | } 169 | .header-dark header.site-header .navbar-default .navbar-nav>.active>a, 170 | .header-dark header.site-header .navbar-default .navbar-nav>.active>a:hover, 171 | .header-dark header.site-header .navbar-default .navbar-nav>.active>a:focus { 172 | color: #fff; 173 | } 174 | .header-dark header.site-header .dropdown a { 175 | color: #333; 176 | } 177 | .header-dark header.site-header .navbar-default .navbar-nav>li>a { 178 | color: #d0d2d3; 179 | } 180 | .header-dark header.site-header .navbar-default .navbar-nav>li>a:hover { 181 | color: #fff; 182 | background: none; 183 | } 184 | .header-dark header.site-header nav li .btn-dro { 185 | color: #d0d2d3; 186 | } 187 | .header-dark header.site-header nav li .btn-dro:hover { 188 | color: #fff; 189 | } 190 | .header-dark header.site-header a.btn { 191 | border-color: #fff; 192 | } 193 | .header-dark header.site-header a.btn:hover { 194 | border-color: #fff; 195 | color: #000; 196 | } 197 | .site-header.navbar-fixed-top, 198 | .site-header.navbar-fixed-bottom { 199 | background: #fff; 200 | position: fixed !important; 201 | right: 0; 202 | left: 0; 203 | z-index: 1030; 204 | transform: translate3d(0, 0, 0); 205 | -webkit-transform: translate3d(0, 0, 0); 206 | background: rgba(255,255,255,0.9); 207 | } 208 | } 209 | .navbar-header { 210 | position: relative; 211 | } 212 | a.hiring { 213 | background: #44c7f4; 214 | text-transform: uppercase; 215 | position: absolute; 216 | top: 16px; 217 | left: -104px; 218 | font-weight: bold; 219 | padding: 2px 6px; 220 | border-radius: 3px; 221 | color: #fff !important; 222 | display: inline-block; 223 | font-size: 11px; 224 | } 225 | a.hiring::after { 226 | position: absolute; 227 | content: ""; 228 | right: -4px; 229 | top: 5px; 230 | width: 0; 231 | height: 0; 232 | border-top: 5px solid transparent; 233 | border-bottom: 5px solid transparent; 234 | border-left: 5px solid #44c7f4; 235 | } 236 | /*========== Responsive ==========*/ 237 | @media screen and (max-width: 990px) { 238 | header.site-header { 239 | position: static; 240 | text-align: left; 241 | padding: 10px 0; 242 | } 243 | header.site-header .navbar-left { 244 | border-top: 1px solid #f1f1f1; 245 | } 246 | header.site-header li { 247 | padding: 8px 0; 248 | border-bottom: 1px solid #f1f1f1; 249 | } 250 | header.site-header li:last-child { 251 | border-bottom: 0; 252 | } 253 | header.site-header li a { 254 | display: block; 255 | } 256 | header.site-header li.dropdown { 257 | display: block; 258 | padding: 0; 259 | opacity: 1; 260 | overflow: hidden; 261 | } 262 | header.site-header li.dropdown .btn-dro { 263 | display: none; 264 | } 265 | header.site-header li.dropdown .divider { 266 | display: none; 267 | } 268 | header.site-header li.dropdown ul.dropdown-menu { 269 | min-width: 0; 270 | position: static; 271 | margin: 0; 272 | box-shadow: none; 273 | border-left: 4px solid #ccc; 274 | opacity: 1; 275 | transform: none; 276 | transition: none; 277 | border-radius: 0; 278 | margin: 0; 279 | padding: 0; 280 | width: 100%; 281 | background: #f8f8f8; 282 | pointer-events: all; 283 | } 284 | header.site-header li.dropdown ul.dropdown-menu li:last-child { 285 | border-bottom: 0; 286 | } 287 | header.site-header nav a.login { 288 | margin-bottom: 20px; 289 | font-size: 14px; 290 | padding: 10px; 291 | display: block; 292 | text-align: center; 293 | } 294 | } -------------------------------------------------------------------------------- /test/specs/v1.2/petstore/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "1.0.0", 3 | "swaggerVersion": "1.2", 4 | "basePath": "http://petstore.swagger.io/api", 5 | "resourcePath": "/user", 6 | "produces": [ 7 | "application/json" 8 | ], 9 | "apis": [ 10 | { 11 | "path": "/user", 12 | "operations": [ 13 | { 14 | "method": "POST", 15 | "summary": "Create user", 16 | "notes": "This can only be done by the logged in user.", 17 | "type": "void", 18 | "nickname": "createUser", 19 | "authorizations": { 20 | "oauth2": [ 21 | { 22 | "scope": "test:anything", 23 | "description": "anything" 24 | } 25 | ] 26 | }, 27 | "parameters": [ 28 | { 29 | "name": "body", 30 | "description": "Created user object", 31 | "required": true, 32 | "type": "User", 33 | "paramType": "body" 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | { 40 | "path": "/user/logout", 41 | "operations": [ 42 | { 43 | "method": "GET", 44 | "summary": "Logs out current logged in user session", 45 | "notes": "", 46 | "type": "void", 47 | "nickname": "logoutUser", 48 | "authorizations": {}, 49 | "parameters": [] 50 | } 51 | ] 52 | }, 53 | { 54 | "path": "/user/createWithArray", 55 | "operations": [ 56 | { 57 | "method": "POST", 58 | "summary": "Creates list of users with given input array", 59 | "notes": "", 60 | "type": "void", 61 | "nickname": "createUsersWithArrayInput", 62 | "authorizations": { 63 | "oauth2": [ 64 | { 65 | "scope": "test:anything", 66 | "description": "anything" 67 | } 68 | ] 69 | }, 70 | "parameters": [ 71 | { 72 | "name": "body", 73 | "description": "List of user object", 74 | "required": true, 75 | "type": "array", 76 | "items": { 77 | "$ref": "User" 78 | }, 79 | "paramType": "body" 80 | } 81 | ] 82 | } 83 | ] 84 | }, 85 | { 86 | "path": "/user/createWithList", 87 | "operations": [ 88 | { 89 | "method": "POST", 90 | "summary": "Creates list of users with given list input", 91 | "notes": "", 92 | "type": "void", 93 | "nickname": "createUsersWithListInput", 94 | "authorizations": { 95 | "oauth2": [ 96 | { 97 | "scope": "test:anything", 98 | "description": "anything" 99 | } 100 | ] 101 | }, 102 | "parameters": [ 103 | { 104 | "name": "body", 105 | "description": "List of user object", 106 | "required": true, 107 | "type": "array", 108 | "items": { 109 | "$ref": "User" 110 | }, 111 | "paramType": "body" 112 | } 113 | ] 114 | } 115 | ] 116 | }, 117 | { 118 | "path": "/user/{username}", 119 | "operations": [ 120 | { 121 | "method": "PUT", 122 | "summary": "Updated user", 123 | "notes": "This can only be done by the logged in user.", 124 | "type": "void", 125 | "nickname": "updateUser", 126 | "authorizations": { 127 | "oauth2": [ 128 | { 129 | "scope": "test:anything", 130 | "description": "anything" 131 | } 132 | ] 133 | }, 134 | "parameters": [ 135 | { 136 | "name": "username", 137 | "description": "name that need to be deleted", 138 | "required": true, 139 | "type": "string", 140 | "paramType": "path" 141 | }, 142 | { 143 | "name": "body", 144 | "description": "Updated user object", 145 | "required": true, 146 | "type": "User", 147 | "paramType": "body" 148 | } 149 | ], 150 | "responseMessages": [ 151 | { 152 | "code": 400, 153 | "message": "Invalid username supplied" 154 | }, 155 | { 156 | "code": 404, 157 | "message": "User not found" 158 | } 159 | ] 160 | }, 161 | { 162 | "method": "DELETE", 163 | "summary": "Delete user", 164 | "notes": "This can only be done by the logged in user.", 165 | "type": "void", 166 | "nickname": "deleteUser", 167 | "authorizations": { 168 | "oauth2": [ 169 | { 170 | "scope": "test:anything", 171 | "description": "anything" 172 | } 173 | ] 174 | }, 175 | "parameters": [ 176 | { 177 | "name": "username", 178 | "description": "The name that needs to be deleted", 179 | "required": true, 180 | "type": "string", 181 | "paramType": "path" 182 | } 183 | ], 184 | "responseMessages": [ 185 | { 186 | "code": 400, 187 | "message": "Invalid username supplied" 188 | }, 189 | { 190 | "code": 404, 191 | "message": "User not found" 192 | } 193 | ] 194 | }, 195 | { 196 | "method": "GET", 197 | "summary": "Get user by user name", 198 | "notes": "", 199 | "type": "User", 200 | "nickname": "getUserByName", 201 | "authorizations": {}, 202 | "parameters": [ 203 | { 204 | "name": "username", 205 | "description": "The name that needs to be fetched. Use user1 for testing.", 206 | "required": true, 207 | "type": "string", 208 | "paramType": "path" 209 | } 210 | ], 211 | "responseMessages": [ 212 | { 213 | "code": 400, 214 | "message": "Invalid username supplied" 215 | }, 216 | { 217 | "code": 404, 218 | "message": "User not found" 219 | } 220 | ] 221 | } 222 | ] 223 | }, 224 | { 225 | "path": "/user/login", 226 | "operations": [ 227 | { 228 | "method": "GET", 229 | "summary": "Logs user into the system", 230 | "notes": "", 231 | "type": "string", 232 | "nickname": "loginUser", 233 | "authorizations": {}, 234 | "parameters": [ 235 | { 236 | "name": "username", 237 | "description": "The user name for login", 238 | "required": true, 239 | "type": "string", 240 | "paramType": "query" 241 | }, 242 | { 243 | "name": "password", 244 | "description": "The password for login in clear text", 245 | "required": true, 246 | "type": "string", 247 | "paramType": "query" 248 | } 249 | ], 250 | "responseMessages": [ 251 | { 252 | "code": 400, 253 | "message": "Invalid username and password combination" 254 | } 255 | ] 256 | } 257 | ] 258 | } 259 | ], 260 | "models": { 261 | "User": { 262 | "id": "User", 263 | "properties": { 264 | "id": { 265 | "type": "integer", 266 | "format": "int64" 267 | }, 268 | "firstName": { 269 | "type": "string" 270 | }, 271 | "username": { 272 | "type": "string" 273 | }, 274 | "lastName": { 275 | "type": "string" 276 | }, 277 | "email": { 278 | "type": "string" 279 | }, 280 | "password": { 281 | "type": "string" 282 | }, 283 | "phone": { 284 | "type": "string" 285 | }, 286 | "userStatus": { 287 | "type": "integer", 288 | "format": "int32", 289 | "description": "User Status", 290 | "enum": [ 291 | "1-registered", 292 | "2-active", 293 | "3-closed" 294 | ] 295 | } 296 | } 297 | } 298 | } 299 | } -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swagger explorer 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 42 | 43 | 114 | 115 | 195 | 196 | 201 | 202 | 203 | 204 | 205 | 224 | 225 |
    226 |
    227 |
    228 |
    229 |
    230 |
    231 |
    232 |
    233 |
    234 |
    235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /src/main/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swagger explorer 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 42 | 43 | 114 | 115 | 195 | 196 | 201 | 202 | 203 | 204 | 205 | 224 | 225 |
    226 |
    227 |
    228 |
    229 |
    230 |
    231 |
    232 |
    233 |
    234 |
    235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /lib/swagger-oauth.js: -------------------------------------------------------------------------------- 1 | var appName; 2 | var popupMask; 3 | var popupDialog; 4 | var clientId; 5 | var realm; 6 | var oauth2KeyName; 7 | var redirect_uri; 8 | 9 | function handleLogin() { 10 | var scopes = []; 11 | 12 | var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions; 13 | if (auths) { 14 | var key; 15 | var defs = auths; 16 | for (key in defs) { 17 | var auth = defs[key]; 18 | if (auth.type === 'oauth2' && auth.scopes) { 19 | oauth2KeyName = key; 20 | var scope; 21 | if (Array.isArray(auth.scopes)) { 22 | // 1.2 support 23 | var i; 24 | for (i = 0; i < auth.scopes.length; i++) { 25 | scopes.push(auth.scopes[i]); 26 | } 27 | } 28 | else { 29 | // 2.0 support 30 | for (scope in auth.scopes) { 31 | scopes.push({scope: scope, description: auth.scopes[scope]}); 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | if (window.swaggerUi.api 39 | && window.swaggerUi.api.info) { 40 | appName = window.swaggerUi.api.info.title; 41 | } 42 | 43 | popupDialog = $( 44 | [ 45 | ''].join('')); 71 | $(document.body).append(popupDialog); 72 | 73 | popup = popupDialog.find('.scopes').empty(); 74 | for (i = 0; i < scopes.length; i++) { 75 | scope = scopes[i]; 76 | str = '' + scope.scope + ''; 77 | popup.append(str); 78 | } 79 | 80 | popupDialog.find('scopes').click(function () { 81 | popupMask.hide(); 82 | popupDialog.hide(); 83 | popupDialog.empty(); 84 | popupDialog = []; 85 | }); 86 | 87 | popupDialog.find('[data-toggle-scope]').click(function () { 88 | $(this).hasClass("active") ? $(this).removeClass('active') : $(this).addClass('active'); 89 | }); 90 | 91 | popupDialog.find('button.api-popup-cancel').click(function () { 92 | popupMask.hide(); 93 | popupDialog.hide(); 94 | popupDialog.empty(); 95 | popupDialog = []; 96 | }); 97 | 98 | $('button.api-popup-authbtn').unbind(); 99 | popupDialog.find('button.api-popup-authbtn').click(function () { 100 | popupMask.hide(); 101 | popupDialog.hide(); 102 | 103 | var authSchemes = window.swaggerUi.api.authSchemes; 104 | var host = window.location; 105 | var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/")); 106 | var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html'; 107 | var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl; 108 | var url = null; 109 | 110 | for (var key in authSchemes) { 111 | if (authSchemes.hasOwnProperty(key)) { 112 | var flow = authSchemes[key].flow; 113 | 114 | if (authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) { 115 | var dets = authSchemes[key]; 116 | url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code'); 117 | window.swaggerUi.tokenName = dets.tokenName || 'access_token'; 118 | window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null); 119 | } 120 | else if (authSchemes[key].grantTypes) { 121 | // 1.2 support 122 | var o = authSchemes[key].grantTypes; 123 | for (var t in o) { 124 | if (o.hasOwnProperty(t) && t === 'implicit') { 125 | var dets = o[t]; 126 | var ep = dets.loginEndpoint.url; 127 | url = dets.loginEndpoint.url + '?response_type=token'; 128 | window.swaggerUi.tokenName = dets.tokenName; 129 | } 130 | else if (o.hasOwnProperty(t) && t === 'accessCode') { 131 | var dets = o[t]; 132 | var ep = dets.tokenRequestEndpoint.url; 133 | url = dets.tokenRequestEndpoint.url + '?response_type=code'; 134 | window.swaggerUi.tokenName = dets.tokenName; 135 | } 136 | } 137 | } 138 | } 139 | } 140 | var scopes = []; 141 | var o = $('.scopes').find('.active'); 142 | for (k = 0; k < o.length; k++) { 143 | var scope = $(o[k]).attr('data-toggle-scope'); 144 | if (scopes.indexOf(scope) === -1) 145 | scopes.push(scope); 146 | } 147 | 148 | // Implicit auth recommends a state parameter. 149 | var state = Math.random(); 150 | 151 | window.enabledScopes = scopes; 152 | 153 | redirect_uri = redirectUrl; 154 | 155 | url += '&redirect_uri=' + encodeURIComponent(redirectUrl); 156 | url += '&realm=' + encodeURIComponent(realm); 157 | url += '&client_id=' + encodeURIComponent(clientId); 158 | url += '&scope=' + encodeURIComponent(scopes.join(' ')); 159 | url += '&state=' + encodeURIComponent(state); 160 | 161 | window.open(url); 162 | }); 163 | 164 | popupMask.show(); 165 | popupDialog.show(); 166 | } 167 | 168 | function handleLogout() { 169 | for (key in window.authorizations.authz) { 170 | window.authorizations.remove(key) 171 | } 172 | window.enabledScopes = null; 173 | var oauthBtn = $('.api-ic'); 174 | oauthBtn.addClass('btn-default'); 175 | oauthBtn.removeClass('btn-success'); 176 | oauthBtn.removeClass('btn-warning'); 177 | 178 | oauthBtn.text('oauth'); 179 | 180 | $('#input_apiKey').val(''); 181 | } 182 | 183 | function initOAuth(opts) { 184 | var o = (opts || {}); 185 | var errors = []; 186 | 187 | appName = (o.appName || errors.push('missing appName')); 188 | popupMask = (o.popupMask || $('#api-common-mask')); 189 | popupDialog = (o.popupDialog || $('.api-popup-dialog')); 190 | clientId = (o.clientId || errors.push('missing client id')); 191 | realm = (o.realm || errors.push('missing realm')); 192 | 193 | if (errors.length > 0) { 194 | log('auth unable initialize oauth: ' + errors); 195 | return; 196 | } 197 | 198 | $('pre code').each(function (i, e) { 199 | hljs.highlightBlock(e) 200 | }); 201 | 202 | var oauthBtn = $('.api-ic'); 203 | oauthBtn.unbind(); 204 | oauthBtn.click(function (s) { 205 | if ($(s.target).hasClass('btn-default')) 206 | handleLogin(); 207 | else { 208 | handleLogout(); 209 | } 210 | }); 211 | } 212 | 213 | window.processOAuthCode = function processOAuthCode(data) { 214 | var params = { 215 | 'client_id': clientId, 216 | 'code': data.code, 217 | 'grant_type': 'authorization_code', 218 | 'redirect_uri': redirect_uri 219 | }; 220 | $.ajax( 221 | { 222 | url: window.swaggerUi.tokenUrl, 223 | type: "POST", 224 | data: params, 225 | success: function (data, textStatus, jqXHR) { 226 | onOAuthComplete(data); 227 | }, 228 | error: function (jqXHR, textStatus, errorThrown) { 229 | onOAuthComplete(""); 230 | } 231 | }); 232 | }; 233 | 234 | window.onOAuthComplete = function onOAuthComplete(token) { 235 | if (token) { 236 | if (token.error) { 237 | var checkbox = $('input[type=checkbox],.secured') 238 | checkbox.each(function (pos) { 239 | checkbox[pos].checked = false; 240 | }); 241 | alert(token.error); 242 | } 243 | else { 244 | var b = token[window.swaggerUi.tokenName]; 245 | if (b) { 246 | // if all roles are satisfied 247 | var o = null; 248 | $.each($('.auth #api_information_panel'), function (k, v) { 249 | var children = v; 250 | if (children && children.childNodes) { 251 | var requiredScopes = []; 252 | $.each((children.childNodes), function (k1, v1) { 253 | var inner = v1.innerHTML; 254 | if (inner) 255 | requiredScopes.push(inner); 256 | }); 257 | var diff = []; 258 | for (var i = 0; i < requiredScopes.length; i++) { 259 | var s = requiredScopes[i]; 260 | if (window.enabledScopes && window.enabledScopes.indexOf(s) == -1) { 261 | diff.push(s); 262 | } 263 | } 264 | if (diff.length > 0) { 265 | o = v.parentNode; 266 | // sorry, not all scopes are satisfied 267 | $(o).find('.api-ic').addClass('btn-warning'); 268 | $(o).find('.api-ic').removeClass('btn-default'); 269 | $(o).find('.api-ic').removeClass('btn-success'); 270 | } 271 | else { 272 | o = v.parentNode; 273 | // all scopes are satisfied 274 | $(o).find('.api-ic').addClass('btn-success'); 275 | $(o).find('.api-ic').removeClass('btn-default'); 276 | $(o).find('.api-ic').removeClass('btn-warning'); 277 | } 278 | } 279 | }); 280 | $('.api-ic').text('signout'); 281 | $('#input_apiKey').val(b); 282 | window.swaggerUi.api.clientAuthorizations.add(oauth2KeyName, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header')); 283 | } 284 | } 285 | } 286 | }; -------------------------------------------------------------------------------- /dist/lib/swagger-oauth.js: -------------------------------------------------------------------------------- 1 | var appName; 2 | var popupMask; 3 | var popupDialog; 4 | var clientId; 5 | var realm; 6 | var oauth2KeyName; 7 | var redirect_uri; 8 | 9 | function handleLogin() { 10 | var scopes = []; 11 | 12 | var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions; 13 | if (auths) { 14 | var key; 15 | var defs = auths; 16 | for (key in defs) { 17 | var auth = defs[key]; 18 | if (auth.type === 'oauth2' && auth.scopes) { 19 | oauth2KeyName = key; 20 | var scope; 21 | if (Array.isArray(auth.scopes)) { 22 | // 1.2 support 23 | var i; 24 | for (i = 0; i < auth.scopes.length; i++) { 25 | scopes.push(auth.scopes[i]); 26 | } 27 | } 28 | else { 29 | // 2.0 support 30 | for (scope in auth.scopes) { 31 | scopes.push({scope: scope, description: auth.scopes[scope]}); 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | if (window.swaggerUi.api 39 | && window.swaggerUi.api.info) { 40 | appName = window.swaggerUi.api.info.title; 41 | } 42 | 43 | popupDialog = $( 44 | [ 45 | ''].join('')); 71 | $(document.body).append(popupDialog); 72 | 73 | popup = popupDialog.find('.scopes').empty(); 74 | for (i = 0; i < scopes.length; i++) { 75 | scope = scopes[i]; 76 | str = '' + scope.scope + ''; 77 | popup.append(str); 78 | } 79 | 80 | popupDialog.find('scopes').click(function () { 81 | popupMask.hide(); 82 | popupDialog.hide(); 83 | popupDialog.empty(); 84 | popupDialog = []; 85 | }); 86 | 87 | popupDialog.find('[data-toggle-scope]').click(function () { 88 | $(this).hasClass("active") ? $(this).removeClass('active') : $(this).addClass('active'); 89 | }); 90 | 91 | popupDialog.find('button.api-popup-cancel').click(function () { 92 | popupMask.hide(); 93 | popupDialog.hide(); 94 | popupDialog.empty(); 95 | popupDialog = []; 96 | }); 97 | 98 | $('button.api-popup-authbtn').unbind(); 99 | popupDialog.find('button.api-popup-authbtn').click(function () { 100 | popupMask.hide(); 101 | popupDialog.hide(); 102 | 103 | var authSchemes = window.swaggerUi.api.authSchemes; 104 | var host = window.location; 105 | var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/")); 106 | var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html'; 107 | var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl; 108 | var url = null; 109 | 110 | for (var key in authSchemes) { 111 | if (authSchemes.hasOwnProperty(key)) { 112 | var flow = authSchemes[key].flow; 113 | 114 | if (authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) { 115 | var dets = authSchemes[key]; 116 | url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code'); 117 | window.swaggerUi.tokenName = dets.tokenName || 'access_token'; 118 | window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null); 119 | } 120 | else if (authSchemes[key].grantTypes) { 121 | // 1.2 support 122 | var o = authSchemes[key].grantTypes; 123 | for (var t in o) { 124 | if (o.hasOwnProperty(t) && t === 'implicit') { 125 | var dets = o[t]; 126 | var ep = dets.loginEndpoint.url; 127 | url = dets.loginEndpoint.url + '?response_type=token'; 128 | window.swaggerUi.tokenName = dets.tokenName; 129 | } 130 | else if (o.hasOwnProperty(t) && t === 'accessCode') { 131 | var dets = o[t]; 132 | var ep = dets.tokenRequestEndpoint.url; 133 | url = dets.tokenRequestEndpoint.url + '?response_type=code'; 134 | window.swaggerUi.tokenName = dets.tokenName; 135 | } 136 | } 137 | } 138 | } 139 | } 140 | var scopes = []; 141 | var o = $('.scopes').find('.active'); 142 | for (k = 0; k < o.length; k++) { 143 | var scope = $(o[k]).attr('data-toggle-scope'); 144 | if (scopes.indexOf(scope) === -1) 145 | scopes.push(scope); 146 | } 147 | 148 | // Implicit auth recommends a state parameter. 149 | var state = Math.random(); 150 | 151 | window.enabledScopes = scopes; 152 | 153 | redirect_uri = redirectUrl; 154 | 155 | url += '&redirect_uri=' + encodeURIComponent(redirectUrl); 156 | url += '&realm=' + encodeURIComponent(realm); 157 | url += '&client_id=' + encodeURIComponent(clientId); 158 | url += '&scope=' + encodeURIComponent(scopes.join(' ')); 159 | url += '&state=' + encodeURIComponent(state); 160 | 161 | window.open(url); 162 | }); 163 | 164 | popupMask.show(); 165 | popupDialog.show(); 166 | } 167 | 168 | function handleLogout() { 169 | for (key in window.authorizations.authz) { 170 | window.authorizations.remove(key) 171 | } 172 | window.enabledScopes = null; 173 | var oauthBtn = $('.api-ic'); 174 | oauthBtn.addClass('btn-default'); 175 | oauthBtn.removeClass('btn-success'); 176 | oauthBtn.removeClass('btn-warning'); 177 | 178 | oauthBtn.text('oauth'); 179 | 180 | $('#input_apiKey').val(''); 181 | } 182 | 183 | function initOAuth(opts) { 184 | var o = (opts || {}); 185 | var errors = []; 186 | 187 | appName = (o.appName || errors.push('missing appName')); 188 | popupMask = (o.popupMask || $('#api-common-mask')); 189 | popupDialog = (o.popupDialog || $('.api-popup-dialog')); 190 | clientId = (o.clientId || errors.push('missing client id')); 191 | realm = (o.realm || errors.push('missing realm')); 192 | 193 | if (errors.length > 0) { 194 | log('auth unable initialize oauth: ' + errors); 195 | return; 196 | } 197 | 198 | $('pre code').each(function (i, e) { 199 | hljs.highlightBlock(e) 200 | }); 201 | 202 | var oauthBtn = $('.api-ic'); 203 | oauthBtn.unbind(); 204 | oauthBtn.click(function (s) { 205 | if ($(s.target).hasClass('btn-default')) 206 | handleLogin(); 207 | else { 208 | handleLogout(); 209 | } 210 | }); 211 | } 212 | 213 | window.processOAuthCode = function processOAuthCode(data) { 214 | var params = { 215 | 'client_id': clientId, 216 | 'code': data.code, 217 | 'grant_type': 'authorization_code', 218 | 'redirect_uri': redirect_uri 219 | }; 220 | $.ajax( 221 | { 222 | url: window.swaggerUi.tokenUrl, 223 | type: "POST", 224 | data: params, 225 | success: function (data, textStatus, jqXHR) { 226 | onOAuthComplete(data); 227 | }, 228 | error: function (jqXHR, textStatus, errorThrown) { 229 | onOAuthComplete(""); 230 | } 231 | }); 232 | }; 233 | 234 | window.onOAuthComplete = function onOAuthComplete(token) { 235 | if (token) { 236 | if (token.error) { 237 | var checkbox = $('input[type=checkbox],.secured') 238 | checkbox.each(function (pos) { 239 | checkbox[pos].checked = false; 240 | }); 241 | alert(token.error); 242 | } 243 | else { 244 | var b = token[window.swaggerUi.tokenName]; 245 | if (b) { 246 | // if all roles are satisfied 247 | var o = null; 248 | $.each($('.auth #api_information_panel'), function (k, v) { 249 | var children = v; 250 | if (children && children.childNodes) { 251 | var requiredScopes = []; 252 | $.each((children.childNodes), function (k1, v1) { 253 | var inner = v1.innerHTML; 254 | if (inner) 255 | requiredScopes.push(inner); 256 | }); 257 | var diff = []; 258 | for (var i = 0; i < requiredScopes.length; i++) { 259 | var s = requiredScopes[i]; 260 | if (window.enabledScopes && window.enabledScopes.indexOf(s) == -1) { 261 | diff.push(s); 262 | } 263 | } 264 | if (diff.length > 0) { 265 | o = v.parentNode; 266 | // sorry, not all scopes are satisfied 267 | $(o).find('.api-ic').addClass('btn-warning'); 268 | $(o).find('.api-ic').removeClass('btn-default'); 269 | $(o).find('.api-ic').removeClass('btn-success'); 270 | } 271 | else { 272 | o = v.parentNode; 273 | // all scopes are satisfied 274 | $(o).find('.api-ic').addClass('btn-success'); 275 | $(o).find('.api-ic').removeClass('btn-default'); 276 | $(o).find('.api-ic').removeClass('btn-warning'); 277 | } 278 | } 279 | }); 280 | $('.api-ic').text('signout'); 281 | $('#input_apiKey').val(b); 282 | window.swaggerUi.api.clientAuthorizations.add(oauth2KeyName, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header')); 283 | } 284 | } 285 | } 286 | }; -------------------------------------------------------------------------------- /test/specs/v1.2/petstore/pet.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "1.0.0", 3 | "swaggerVersion": "1.2", 4 | "basePath": "http://petstore.swagger.io/api", 5 | "resourcePath": "/pet", 6 | "produces": [ 7 | "application/json", 8 | "application/xml", 9 | "text/plain", 10 | "text/html" 11 | ], 12 | "apis": [ 13 | { 14 | "path": "/pet/{petId}", 15 | "operations": [ 16 | { 17 | "method": "GET", 18 | "summary": "Find pet by ID", 19 | "notes": "Returns a pet based on ID", 20 | "type": "Pet", 21 | "nickname": "getPetById", 22 | "authorizations": {}, 23 | "parameters": [ 24 | { 25 | "name": "petId", 26 | "description": "ID of pet that needs to be fetched", 27 | "required": true, 28 | "type": "integer", 29 | "format": "int64", 30 | "paramType": "path", 31 | "minimum": "1.0", 32 | "maximum": "100000.0" 33 | } 34 | ], 35 | "responseMessages": [ 36 | { 37 | "code": 400, 38 | "message": "Invalid ID supplied" 39 | }, 40 | { 41 | "code": 404, 42 | "message": "Pet not found" 43 | } 44 | ] 45 | }, 46 | { 47 | "method": "PATCH", 48 | "summary": "partial updates to a pet", 49 | "notes": "", 50 | "type": "array", 51 | "items": { 52 | "$ref": "Pet" 53 | }, 54 | "nickname": "partialUpdate", 55 | "produces": [ 56 | "application/json", 57 | "application/xml" 58 | ], 59 | "consumes": [ 60 | "application/json", 61 | "application/xml" 62 | ], 63 | "authorizations": { 64 | "oauth2": [ 65 | { 66 | "scope": "test:anything", 67 | "description": "anything" 68 | } 69 | ] 70 | }, 71 | "parameters": [ 72 | { 73 | "name": "petId", 74 | "description": "ID of pet that needs to be fetched", 75 | "required": true, 76 | "type": "string", 77 | "paramType": "path" 78 | }, 79 | { 80 | "name": "body", 81 | "description": "Pet object that needs to be added to the store", 82 | "required": true, 83 | "type": "Pet", 84 | "paramType": "body" 85 | } 86 | ], 87 | "responseMessages": [ 88 | { 89 | "code": 400, 90 | "message": "Invalid tag value" 91 | } 92 | ] 93 | }, 94 | { 95 | "method": "POST", 96 | "summary": "Updates a pet in the store with form data", 97 | "notes": "", 98 | "type": "void", 99 | "nickname": "updatePetWithForm", 100 | "consumes": [ 101 | "application/x-www-form-urlencoded" 102 | ], 103 | "authorizations": { 104 | "oauth2": [ 105 | { 106 | "scope": "test:anything", 107 | "description": "anything" 108 | } 109 | ] 110 | }, 111 | "parameters": [ 112 | { 113 | "name": "petId", 114 | "description": "ID of pet that needs to be updated", 115 | "required": true, 116 | "type": "string", 117 | "paramType": "path" 118 | }, 119 | { 120 | "name": "name", 121 | "description": "Updated name of the pet", 122 | "required": false, 123 | "type": "string", 124 | "paramType": "form" 125 | }, 126 | { 127 | "name": "status", 128 | "description": "Updated status of the pet", 129 | "required": false, 130 | "type": "string", 131 | "paramType": "form" 132 | } 133 | ], 134 | "responseMessages": [ 135 | { 136 | "code": 405, 137 | "message": "Invalid input" 138 | } 139 | ] 140 | }, 141 | { 142 | "method": "DELETE", 143 | "summary": "Deletes a pet", 144 | "notes": "", 145 | "type": "void", 146 | "nickname": "deletePet", 147 | "authorizations": { 148 | "oauth2": [ 149 | { 150 | "scope": "test:anything", 151 | "description": "anything" 152 | } 153 | ] 154 | }, 155 | "parameters": [ 156 | { 157 | "name": "petId", 158 | "description": "Pet id to delete", 159 | "required": true, 160 | "type": "string", 161 | "paramType": "path" 162 | } 163 | ], 164 | "responseMessages": [ 165 | { 166 | "code": 400, 167 | "message": "Invalid pet value" 168 | } 169 | ] 170 | } 171 | ] 172 | }, 173 | { 174 | "path": "/pet", 175 | "operations": [ 176 | { 177 | "method": "PUT", 178 | "summary": "Update an existing pet", 179 | "notes": "", 180 | "type": "void", 181 | "nickname": "updatePet", 182 | "authorizations": {}, 183 | "parameters": [ 184 | { 185 | "name": "body", 186 | "description": "Pet object that needs to be updated in the store", 187 | "required": true, 188 | "type": "Pet", 189 | "paramType": "body" 190 | } 191 | ], 192 | "responseMessages": [ 193 | { 194 | "code": 400, 195 | "message": "Invalid ID supplied" 196 | }, 197 | { 198 | "code": 404, 199 | "message": "Pet not found" 200 | }, 201 | { 202 | "code": 405, 203 | "message": "Validation exception" 204 | } 205 | ] 206 | }, 207 | { 208 | "method": "POST", 209 | "summary": "Add a new pet to the store", 210 | "notes": "", 211 | "type": "void", 212 | "nickname": "addPet", 213 | "consumes": [ 214 | "application/json", 215 | "application/xml" 216 | ], 217 | "authorizations": { 218 | "oauth2": [ 219 | { 220 | "scope": "test:anything", 221 | "description": "anything" 222 | } 223 | ] 224 | }, 225 | "parameters": [ 226 | { 227 | "name": "body", 228 | "description": "Pet object that needs to be added to the store", 229 | "required": true, 230 | "type": "Pet", 231 | "paramType": "body" 232 | } 233 | ], 234 | "responseMessages": [ 235 | { 236 | "code": 405, 237 | "message": "Invalid input" 238 | } 239 | ] 240 | } 241 | ] 242 | }, 243 | { 244 | "path": "/pet/findByStatus", 245 | "operations": [ 246 | { 247 | "method": "GET", 248 | "summary": "Finds Pets by status", 249 | "notes": "Multiple status values can be provided with comma seperated strings", 250 | "type": "array", 251 | "items": { 252 | "$ref": "Pet" 253 | }, 254 | "nickname": "findPetsByStatus", 255 | "authorizations": {}, 256 | "parameters": [ 257 | { 258 | "name": "status", 259 | "description": "Status values that need to be considered for filter", 260 | "defaultValue": "available", 261 | "required": true, 262 | "type": "string", 263 | "paramType": "query", 264 | "enum": [ 265 | "available", 266 | "pending", 267 | "sold" 268 | ] 269 | } 270 | ], 271 | "responseMessages": [ 272 | { 273 | "code": 400, 274 | "message": "Invalid status value" 275 | } 276 | ] 277 | } 278 | ] 279 | }, 280 | { 281 | "path": "/pet/findByTags", 282 | "operations": [ 283 | { 284 | "method": "GET", 285 | "summary": "Finds Pets by tags", 286 | "notes": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", 287 | "type": "array", 288 | "items": { 289 | "$ref": "Pet" 290 | }, 291 | "nickname": "findPetsByTags", 292 | "authorizations": {}, 293 | "parameters": [ 294 | { 295 | "name": "tags", 296 | "description": "Tags to filter by", 297 | "required": true, 298 | "type": "string", 299 | "paramType": "query" 300 | } 301 | ], 302 | "responseMessages": [ 303 | { 304 | "code": 400, 305 | "message": "Invalid tag value" 306 | } 307 | ], 308 | "deprecated": "true" 309 | } 310 | ] 311 | }, 312 | { 313 | "path": "/pet/uploadImage", 314 | "operations": [ 315 | { 316 | "method": "POST", 317 | "summary": "uploads an image", 318 | "notes": "", 319 | "type": "void", 320 | "nickname": "uploadFile", 321 | "consumes": [ 322 | "multipart/form-data" 323 | ], 324 | "authorizations": { 325 | "oauth2": [ 326 | { 327 | "scope": "test:anything", 328 | "description": "anything" 329 | }, 330 | { 331 | "scope": "test:nothing", 332 | "description": "nothing" 333 | } 334 | ] 335 | }, 336 | "parameters": [ 337 | { 338 | "name": "additionalMetadata", 339 | "description": "Additional data to pass to server", 340 | "required": false, 341 | "type": "string", 342 | "paramType": "form" 343 | }, 344 | { 345 | "name": "file", 346 | "description": "file to upload", 347 | "required": false, 348 | "type": "File", 349 | "paramType": "body" 350 | } 351 | ] 352 | } 353 | ] 354 | } 355 | ], 356 | "models": { 357 | "Tag": { 358 | "id": "Tag", 359 | "properties": { 360 | "id": { 361 | "type": "integer", 362 | "format": "int64" 363 | }, 364 | "name": { 365 | "type": "string" 366 | } 367 | } 368 | }, 369 | "Pet": { 370 | "id": "Pet", 371 | "required": [ 372 | "id", 373 | "name" 374 | ], 375 | "properties": { 376 | "id": { 377 | "type": "integer", 378 | "format": "int64", 379 | "description": "unique identifier for the pet", 380 | "minimum": "0.0", 381 | "maximum": "100.0" 382 | }, 383 | "category": { 384 | "$ref": "Category" 385 | }, 386 | "name": { 387 | "type": "string" 388 | }, 389 | "photoUrls": { 390 | "type": "array", 391 | "items": { 392 | "type": "string" 393 | } 394 | }, 395 | "tags": { 396 | "type": "array", 397 | "items": { 398 | "$ref": "Tag" 399 | } 400 | }, 401 | "status": { 402 | "type": "string", 403 | "description": "pet status in the store", 404 | "enum": [ 405 | "available", 406 | "pending", 407 | "sold" 408 | ] 409 | } 410 | } 411 | }, 412 | "Category": { 413 | "id": "Category", 414 | "properties": { 415 | "id": { 416 | "type": "integer", 417 | "format": "int64" 418 | }, 419 | "name": { 420 | "type": "string" 421 | } 422 | } 423 | } 424 | } 425 | } --------------------------------------------------------------------------------