From 846d589a7eb5c7c8b603bdd53a65ed7dbaefea42 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 10:05:04 -0700 Subject: [PATCH 01/25] Convert SwaggerUI.js and introduce JSHint --- .jshintignore | 2 + .jshintrc | 30 +++++ src/main/coffeescript/SwaggerUi.js | 190 +++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 .jshintignore create mode 100644 .jshintrc create mode 100644 src/main/coffeescript/SwaggerUi.js diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 00000000..90d45b85 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,2 @@ +dist +lib \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..8aa7d8a0 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,30 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": false, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "globals": { + "Backbone": false, + "_": false, + "$": false, + "marked": false, + "HeaderView": false, + "MainView": false, + "Docs": false, + "SwaggerClient": false + } +} \ No newline at end of file diff --git a/src/main/coffeescript/SwaggerUi.js b/src/main/coffeescript/SwaggerUi.js new file mode 100644 index 00000000..23cca68e --- /dev/null +++ b/src/main/coffeescript/SwaggerUi.js @@ -0,0 +1,190 @@ +'use strict'; + +var SwaggerUi = Backbone.Router.extend({ + + dom_id: 'swagger_ui', + + // Attributes + options: null, + api: null, + headerView: null, + mainView: null, + + // SwaggerUi accepts all the same options as SwaggerApi + initialize: function(options) { + options = options || {}; + + // Allow dom_id to be overridden + if (options.dom_id) { + this.dom_id = options.dom_id; + delete options.dom_id; + } + + if (!options.supportedSubmitMethods){ + options.supportedSubmitMethods = [ + 'get', + 'put', + 'post', + 'delete', + 'head', + 'options', + 'patch' + ]; + } + + // Create an empty div which contains the dom_id + if (! $('#' + this.dom_id)){ + $('body').append('
') ; + } + + this.options = options; + + // set marked options + marked.setOptions({gfm: true}); + + // Set the callbacks + this.options.success = function() { return this.render(); }; + this.options.progress = function(d) { return this.showMessage(d); }; + this.options.failure = function(d) { return this.onLoadFailure(d); }; + + // Create view to handle the header inputs + this.headerView = new HeaderView({el: $('#header')}); + + // Event handler for when the baseUrl/apiKey is entered by user + this.headerView.on('update-swagger-ui', function(data) { + return this.updateSwaggerUi(data); + }); + }, + + // Set an option after initializing + setOption: function(option, value) { + this.options[option] = value; + }, + + // Get the value of a previously set option + getOption(option) { + return this.options[option]; + }, + + // Event handler for when url/key is received from user + updateSwaggerUi: function(data){ + this.options.url = data.url; + this.load(); + }, + + // Create an api and render + load: function(){ + // Initialize the API object + if (this.mainView) { + this.mainView.clear(); + } + var url = this.options.url; + if (url && url.indexOf('http') !== 0) { + url = this.buildUrl(window.location.href.toString(), url); + } + + this.options.url = url; + this.headerView.update(url); + + this.api = new SwaggerClient(this.options); + }, + + // collapse all sections + collapseAll: function(){ + Docs.collapseEndpointListForResource(''); + }, + + // list operations for all sections + listAll: function(){ + Docs.collapseOperationsForResource(''); + }, + + // expand operations for all sections + expandAll: function(){ + Docs.expandOperationsForResource(''); + }, + + // This is bound to success handler for SwaggerApi + // so it gets called when SwaggerApi completes loading + render: function(){ + this.showMessage('Finished Loading Resource Information. Rendering Swagger UI...'); + this.mainView = new MainView({ + model: this.api, + el: $('#' + this.dom_id), + swaggerOptions: this.options + }).render(); + this.showMessage(); + switch (this.options.docExpansion) { + case 'full': + this.expandAll(); break; + case 'list': + this.listAll(); break; + default: + break; + } + this.renderGFM(); + + if (this.options.onComplete){ + this.options.onComplete(this.api, this); + } + + setTimeout(Docs.shebang.bind(this), 100); + }, + + buildUrl: function(base, url){ + if (url.indexOf('/') === 0) { + var parts = base.split('/'); + base = parts[0] + '//' + parts[2]; + return base + url; + } else { + var endOfPath = base.length; + + if (base.indexOf('?') > -1){ + endOfPath = Math.min(endOfPath, base.indexOf('?')); + } + + if (base.indexOf('#') > -1){ + endOfPath = Math.min(endOfPath, base.indexOf('#')); + } + + base = base.substring(0, endOfPath); + + if (base.indexOf('/', base.length - 1 ) !== -1){ + return base + url; + } + + return base + '/' + url; + } + }, + + // Shows message on topbar of the ui + showMessage: function(data = ''){ + $('#message-bar').removeClass('message-fail'); + $('#message-bar').addClass('message-success'); + $('#message-bar').html(data); + }, + + // shows message in red + onLoadFailure: function(data = ''){ + $('#message-bar').removeClass('message-success'); + $('#message-bar').addClass('message-fail'); + + var val = $('#message-bar').html(data); + + if (this.options.onFailure) { + this.options.onFailure(data); + } + + return val; + }, + + // Renders GFM for elements with 'markdown' class + renderGFM: function(){ + $('.markdown').each(function(){ + $(this).html(marked($(this).html())); + }); + } + +}); + +window.SwaggerUi = SwaggerUi; \ No newline at end of file From 15d1e22dbfdf775078f70d506f358cbdb2baaf6d Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 10:10:16 -0700 Subject: [PATCH 02/25] Convert helpers/handlebars.coffee --- .jshintrc | 3 ++- src/main/coffeescript/helpers/handlebars.coffee | 5 ----- src/main/coffeescript/helpers/handlebars.js | 7 +++++++ 3 files changed, 9 insertions(+), 6 deletions(-) delete mode 100644 src/main/coffeescript/helpers/handlebars.coffee create mode 100644 src/main/coffeescript/helpers/handlebars.js diff --git a/.jshintrc b/.jshintrc index 8aa7d8a0..d7798813 100644 --- a/.jshintrc +++ b/.jshintrc @@ -25,6 +25,7 @@ "HeaderView": false, "MainView": false, "Docs": false, - "SwaggerClient": false + "SwaggerClient": false, + "Handlebars": false } } \ No newline at end of file diff --git a/src/main/coffeescript/helpers/handlebars.coffee b/src/main/coffeescript/helpers/handlebars.coffee deleted file mode 100644 index 5f45bf28..00000000 --- a/src/main/coffeescript/helpers/handlebars.coffee +++ /dev/null @@ -1,5 +0,0 @@ -Handlebars.registerHelper('sanitize', (html) -> - # Strip the script tags from the html, and return it as a Handlebars.SafeString - html = html.replace(/)<[^<]*)*<\/script>/gi, '') - return new Handlebars.SafeString(html) -) \ No newline at end of file diff --git a/src/main/coffeescript/helpers/handlebars.js b/src/main/coffeescript/helpers/handlebars.js new file mode 100644 index 00000000..58a43d70 --- /dev/null +++ b/src/main/coffeescript/helpers/handlebars.js @@ -0,0 +1,7 @@ +'use strict'; + +Handlebars.registerHelper('sanitize', function(html) { + // Strip the script tags from the html, and return it as a Handlebars.SafeString + html = html.replace(/)<[^<]*)*<\/script>/gi, ''); + return new Handlebars.SafeString(html); +}); \ No newline at end of file From f211be40d2bd9ad6a7e225344027cbc2567eec07 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 10:18:20 -0700 Subject: [PATCH 03/25] Convert view/ApiKeyButton.coffee --- .jshintrc | 3 +- .../coffeescript/view/ApiKeyButton.coffee | 31 ------------ src/main/coffeescript/view/ApiKeyButton.js | 47 +++++++++++++++++++ 3 files changed, 49 insertions(+), 32 deletions(-) delete mode 100644 src/main/coffeescript/view/ApiKeyButton.coffee create mode 100644 src/main/coffeescript/view/ApiKeyButton.js diff --git a/.jshintrc b/.jshintrc index d7798813..08f27c77 100644 --- a/.jshintrc +++ b/.jshintrc @@ -26,6 +26,7 @@ "MainView": false, "Docs": false, "SwaggerClient": false, - "Handlebars": false + "Handlebars": false, + "ApiKeyAuthorization": false } } \ No newline at end of file diff --git a/src/main/coffeescript/view/ApiKeyButton.coffee b/src/main/coffeescript/view/ApiKeyButton.coffee deleted file mode 100644 index 0b316692..00000000 --- a/src/main/coffeescript/view/ApiKeyButton.coffee +++ /dev/null @@ -1,31 +0,0 @@ -class ApiKeyButton extends Backbone.View - initialize: -> - - render: -> - template = @template() - $(@el).html(template(@model)) - - @ - - events: - "click #apikey_button" : "toggleApiKeyContainer" - "click #apply_api_key" : "applyApiKey" - - applyApiKey: -> - window.authorizations.add(@model.name, new ApiKeyAuthorization(@model.name, $("#input_apiKey_entry").val(), @model.in)) - window.swaggerUi.load() - elem = $('#apikey_container').show() - - toggleApiKeyContainer: -> - if $('#apikey_container').length > 0 - elem = $('#apikey_container').first() - if elem.is ':visible' - elem.hide() - else - # hide others - $('.auth_container').hide() - elem.show() - - template: -> - Handlebars.templates.apikey_button_view - diff --git a/src/main/coffeescript/view/ApiKeyButton.js b/src/main/coffeescript/view/ApiKeyButton.js new file mode 100644 index 00000000..06bc3d90 --- /dev/null +++ b/src/main/coffeescript/view/ApiKeyButton.js @@ -0,0 +1,47 @@ +'use strict'; + +var ApiKeyButton = Backbone.View.extend({ // TODO: append this to global SwaggerUi + + events:{ + 'click #apikey_button' : 'toggleApiKeyContainer', + 'click #apply_api_key' : 'applyApiKey' + }, + + initialize: function(){}, + + render: function(){ + var template = this.template(); + $(this.el).html(template(this.model)); + + return this; + }, + + + applyApiKey: function(){ + var authorizations = new ApiKeyAuthorization(this.model.name, $('#input_apiKey_entry').val(), this.model.in); + window.authorizations.add(this.model.name, authorizations); + window.swaggerUi.load(); + $('#apikey_container').show(); + }, + + toggleApiKeyContainer: function(){ + if ($('#apikey_container').length > 0) { + + var elem = $('#apikey_container').first(); + + if (elem.is(':visible')){ + elem.hide(); + } else { + + // hide others + $('.auth_container').hide(); + elem.show(); + } + } + }, + + template: function(){ + return Handlebars.templates.apikey_button_view; + } + +}); \ No newline at end of file From 734ad71250a9e96c2162d8ea6337e02dcbad51c8 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 10:24:20 -0700 Subject: [PATCH 04/25] Convert view/BasicAuthButton.coffee --- .jshintrc | 3 +- src/main/coffeescript/view/ApiKeyButton.js | 8 +++- .../coffeescript/view/BasicAuthButton.coffee | 34 -------------- src/main/coffeescript/view/BasicAuthButton.js | 46 +++++++++++++++++++ 4 files changed, 54 insertions(+), 37 deletions(-) delete mode 100644 src/main/coffeescript/view/BasicAuthButton.coffee create mode 100644 src/main/coffeescript/view/BasicAuthButton.js diff --git a/.jshintrc b/.jshintrc index 08f27c77..2c347ba0 100644 --- a/.jshintrc +++ b/.jshintrc @@ -27,6 +27,7 @@ "Docs": false, "SwaggerClient": false, "Handlebars": false, - "ApiKeyAuthorization": false + "ApiKeyAuthorization": false, + "PasswordAuthorization": false } } \ No newline at end of file diff --git a/src/main/coffeescript/view/ApiKeyButton.js b/src/main/coffeescript/view/ApiKeyButton.js index 06bc3d90..2a85a5e5 100644 --- a/src/main/coffeescript/view/ApiKeyButton.js +++ b/src/main/coffeescript/view/ApiKeyButton.js @@ -18,8 +18,12 @@ var ApiKeyButton = Backbone.View.extend({ // TODO: append this to global Swagger applyApiKey: function(){ - var authorizations = new ApiKeyAuthorization(this.model.name, $('#input_apiKey_entry').val(), this.model.in); - window.authorizations.add(this.model.name, authorizations); + var keyAuth = new ApiKeyAuthorization( + this.model.name, + $('#input_apiKey_entry').val(), + this.model.in + ); + window.authorizations.add(this.model.name, keyAuth); window.swaggerUi.load(); $('#apikey_container').show(); }, diff --git a/src/main/coffeescript/view/BasicAuthButton.coffee b/src/main/coffeescript/view/BasicAuthButton.coffee deleted file mode 100644 index 54c96a55..00000000 --- a/src/main/coffeescript/view/BasicAuthButton.coffee +++ /dev/null @@ -1,34 +0,0 @@ -class BasicAuthButton extends Backbone.View - initialize: -> - - render: -> - template = @template() - $(@el).html(template(@model)) - - @ - - events: - "click #basic_auth_button" : "togglePasswordContainer" - "click #apply_basic_auth" : "applyPassword" - - applyPassword: -> - username = $(".input_username").val() - password = $(".input_password").val() - window.authorizations.add(@model.type, new PasswordAuthorization("basic", username, password)) - window.swaggerUi.load() - elem = $('#basic_auth_container').hide() - - - togglePasswordContainer: -> - if $('#basic_auth_container').length > 0 - elem = $('#basic_auth_container').show() - if elem.is ':visible' - elem.slideUp() - else - # hide others - $('.auth_container').hide() - elem.show() - - template: -> - Handlebars.templates.basic_auth_button_view - diff --git a/src/main/coffeescript/view/BasicAuthButton.js b/src/main/coffeescript/view/BasicAuthButton.js new file mode 100644 index 00000000..a7da3992 --- /dev/null +++ b/src/main/coffeescript/view/BasicAuthButton.js @@ -0,0 +1,46 @@ +'use strict'; + +var BasicAuthButton = Backbone.View.extend({ + + + initialize: function () {}, + + render: function(){ + var template = this.template(); + $(this.el).html(template(this.model)); + + return this; + }, + + events: { + 'click #basic_auth_button' : 'togglePasswordContainer', + 'click #apply_basic_auth' : 'applyPassword' + }, + + applyPassword: function(){ + var username = $('.input_username').val(); + var password = $('.input_password').val(); + var basicAuth = new PasswordAuthorization('basic', username, password); + window.authorizations.add(this.model.type, basicAuth); + window.swaggerUi.load(); + $('#basic_auth_container').hide(); + }, + + togglePasswordContainer: function(){ + if ($('#basic_auth_container').length > 0) { + var elem = $('#basic_auth_container').show(); + if (elem.is(':visible')){ + elem.slideUp(); + } else { + // hide others + $('.auth_container').hide(); + elem.show(); + } + } + }, + + template: function(){ + return Handlebars.templates.basic_auth_button_view; + } + +}); \ No newline at end of file From 3944828c60934f0332fd47242987fac9f0cd02e6 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:11:20 -0700 Subject: [PATCH 05/25] Convert view/ContentTypeView.coffee --- src/main/coffeescript/view/ContentTypeView.coffee | 14 -------------- src/main/coffeescript/view/ContentTypeView.js | 13 +++++++++++++ 2 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 src/main/coffeescript/view/ContentTypeView.coffee create mode 100644 src/main/coffeescript/view/ContentTypeView.js diff --git a/src/main/coffeescript/view/ContentTypeView.coffee b/src/main/coffeescript/view/ContentTypeView.coffee deleted file mode 100644 index 1c7a6515..00000000 --- a/src/main/coffeescript/view/ContentTypeView.coffee +++ /dev/null @@ -1,14 +0,0 @@ -class ContentTypeView extends Backbone.View - initialize: -> - - render: -> - template = @template() - $(@el).html(template(@model)) - - $('label[for=contentType]', $(@el)).text('Response Content Type') - - @ - - template: -> - Handlebars.templates.content_type - diff --git a/src/main/coffeescript/view/ContentTypeView.js b/src/main/coffeescript/view/ContentTypeView.js new file mode 100644 index 00000000..b165d80a --- /dev/null +++ b/src/main/coffeescript/view/ContentTypeView.js @@ -0,0 +1,13 @@ +'use strict'; + +var ContentTypeView = Backbone.View.extend({ + initialize: function() {}, + + render: function(){ + $(this.el).html(Handlebars.templates.content_type(this.model)); + + $('label[for=contentType]', $(this.el)).text('Response Content Type'); + + return this; + } +}); \ No newline at end of file From 72dad9685d6d602a26dd0893b9e1f79ee1222026 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:17:03 -0700 Subject: [PATCH 06/25] Convert view/HeaderView.coffee --- src/main/coffeescript/view/HeaderView.coffee | 37 -------------- src/main/coffeescript/view/HeaderView.js | 51 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 37 deletions(-) delete mode 100644 src/main/coffeescript/view/HeaderView.coffee create mode 100644 src/main/coffeescript/view/HeaderView.js diff --git a/src/main/coffeescript/view/HeaderView.coffee b/src/main/coffeescript/view/HeaderView.coffee deleted file mode 100644 index 99ffc346..00000000 --- a/src/main/coffeescript/view/HeaderView.coffee +++ /dev/null @@ -1,37 +0,0 @@ -class HeaderView extends Backbone.View - events: { - 'click #show-pet-store-icon' : 'showPetStore' - 'click #show-wordnik-dev-icon' : 'showWordnikDev' - 'click #explore' : 'showCustom' - 'keyup #input_baseUrl' : 'showCustomOnKeyup' - 'keyup #input_apiKey' : 'showCustomOnKeyup' - } - - initialize: -> - - showPetStore: (e) -> - @trigger( - 'update-swagger-ui' - {url:"http://petstore.swagger.wordnik.com/api/api-docs"} - ) - - showWordnikDev: (e) -> - @trigger( - 'update-swagger-ui' - {url:"http://api.wordnik.com/v4/resources.json"} - ) - - showCustomOnKeyup: (e) -> - @showCustom() if e.keyCode is 13 - - showCustom: (e) -> - e?.preventDefault() - @trigger( - 'update-swagger-ui' - {url: $('#input_baseUrl').val(), apiKey: $('#input_apiKey').val()} - ) - - update: (url, apiKey, trigger = false) -> - $('#input_baseUrl').val url - #$('#input_apiKey').val apiKey - @trigger 'update-swagger-ui', {url:url} if trigger diff --git a/src/main/coffeescript/view/HeaderView.js b/src/main/coffeescript/view/HeaderView.js new file mode 100644 index 00000000..295528af --- /dev/null +++ b/src/main/coffeescript/view/HeaderView.js @@ -0,0 +1,51 @@ +'use strict'; + +var HeaderView = Backbone.View.extend({ + events: { + 'click #show-pet-store-icon' : 'showPetStore', + 'click #show-wordnik-dev-icon' : 'showWordnikDev', + 'click #explore' : 'showCustom', + 'keyup #input_baseUrl' : 'showCustomOnKeyup', + 'keyup #input_apiKey' : 'showCustomOnKeyup' + }, + + initialize: function(){}, + + showPetStore: function(){ + this.trigger('update-swagger-ui', { + url:'http://petstore.swagger.wordnik.com/api/api-docs' + }); + }, + + showWordnikDev: function(){ + this.trigger('update-swagger-ui', { + url: 'http://api.wordnik.com/v4/resources.json' + }); + }, + + showCustomOnKeyup: function(e){ + if (e.keyCode === 13) { + this.showCustom(); + } + }, + + showCustom: function(e){ + if (e) { + e.preventDefault(); + } + + this.trigger('update-swagger-ui', { + url: $('#input_baseUrl').val(), + apiKey: $('#input_apiKey').val() + }); + }, + + update: function(url, apiKey, trigger = false){ + $('#input_baseUrl').val(url); + + //$('#input_apiKey').val(apiKey); + if (trigger) { + this.trigger('update-swagger-ui', {url:url}); + } + } +}); \ No newline at end of file From dc9ce7d7981c6a7c02b4a1b5b192ca8d71126007 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:34:58 -0700 Subject: [PATCH 07/25] Convert view/MainView.coffee --- src/main/coffeescript/view/MainView.js | 107 +++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/main/coffeescript/view/MainView.js diff --git a/src/main/coffeescript/view/MainView.js b/src/main/coffeescript/view/MainView.js new file mode 100644 index 00000000..5918e25e --- /dev/null +++ b/src/main/coffeescript/view/MainView.js @@ -0,0 +1,107 @@ +'use strict'; + +var MainView = Backbone.View.extend({ + + // TODO: sorters were not used in any place, do we need them? + // sorters = { + // alpha : function(a,b){ return a.path.localeCompare(b.path); }, + // method : function(a,b){ return a.method.localeCompare(b.method); }, + // }, + + initialize: function(opts){ + opts = opts || {}; + // set up the UI for input + this.model.auths = []; + var key, value; + + for (key in this.model.securityDefinitions) { + value = this.model.securityDefinitions[key]; + + this.model.auths.push({ + name: key, + type: value.type, + value: value + }); + } + + if (this.model.swaggerVersion === '2.0') { + if ('validatorUrl' in opts.swaggerOptions) { + + // Validator URL specified explicitly + this.model.validatorUrl = opts.swaggerOptions.validatorUrl; + + } else if (this.model.url.indexOf('localhost') > 0) { + + // Localhost override + this.model.validatorUrl = null; + + } else { + + // Default validator + this.model.validatorUrl = 'http://online.swagger.io/validator'; + } + } + }, + + render: function(){ + if (this.model.securityDefinitions) { + for (var name in this.model.securityDefinitions) { + var auth = this.model.securityDefinitions[name]; + var button; + + if (auth.type === 'apiKey' && $('#apikey_button').length === 0) { + button = new ApiKeyButton({model: auth}).render().el; + $('.auth_main_container').append(button); + } + + if (auth.type === 'basicAuth' && $('#basic_auth_button').length === 0) { + button = new BasicAuthButton({model: auth}).render().el; + $('.auth_main_container').append(button); + } + } + } + + // Render the outer container for resources + $(this.el).html(Handlebars.templates.main(this.model)); + + // Render each resource + + var resources = {}; + var counter = 0; + this.model.apisArray.forEach(function(resource) { + var id = resource.name; + while (typeof resources[id] !== 'undefined') { + id = id + '_' + counter; + counter += 1; + } + resource.id = id; + resources[id] = resource; + this.addResource(resource, this.model.auths); + }); + + $('.propWrap').hover(function onHover(){ + $('.optionsWrapper', $(this)).show(); + }, function offhover(){ + $('.optionsWrapper', $(this)).hide(); + }); + return this; + }, + + addResource: function(resource, auths){ + // Render a resource and add it to resources li + resource.id = resource.id.replace(/\s/g, '_'); + var resourceView = new ResourceView({ + model: resource, + tagName: 'li', + id: 'resource_' + resource.id, + className: 'resource', + auths: auths, + swaggerOptions: this.options.swaggerOptions + }); + $('#resources').append(resourceView.render().el); + }, + + clear: function(){ + $(this.el).html(''); + } +}); \ No newline at end of file From dc4e595eedfdae51ffb0d7c438d026d5d11fcd21 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:38:08 -0700 Subject: [PATCH 08/25] Convert view/ResponseContentTypeView.coffee --- .../view/ResponseContentTypeView.coffee | 14 -------------- .../coffeescript/view/ResponseContentTypeView.js | 13 +++++++++++++ 2 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 src/main/coffeescript/view/ResponseContentTypeView.coffee create mode 100644 src/main/coffeescript/view/ResponseContentTypeView.js diff --git a/src/main/coffeescript/view/ResponseContentTypeView.coffee b/src/main/coffeescript/view/ResponseContentTypeView.coffee deleted file mode 100644 index 3d17d477..00000000 --- a/src/main/coffeescript/view/ResponseContentTypeView.coffee +++ /dev/null @@ -1,14 +0,0 @@ -class ResponseContentTypeView extends Backbone.View - initialize: -> - - render: -> - template = @template() - - $(@el).html(template(@model)) - - $('label[for=responseContentType]', $(@el)).text('Response Content Type') - - @ - - template: -> - Handlebars.templates.response_content_type diff --git a/src/main/coffeescript/view/ResponseContentTypeView.js b/src/main/coffeescript/view/ResponseContentTypeView.js new file mode 100644 index 00000000..8b2b7fd9 --- /dev/null +++ b/src/main/coffeescript/view/ResponseContentTypeView.js @@ -0,0 +1,13 @@ +'use strict'; + +var ResponseContentTypeView = Backbone.View.extend({ + initialize: function(){}, + + render: function(){ + $(this.el).html(Handlebars.templates.response_content_type(this.model)); + + $('label[for=responseContentType]', $(this.el)).text('Response Content Type'); + + return this; + } +}); \ No newline at end of file From a11fd8b4c6138fd63aafa7111af491cfca05e52e Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:40:54 -0700 Subject: [PATCH 09/25] Convert view/StatusCodeView.coffee --- src/main/coffeescript/view/StatusCodeView.js | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/coffeescript/view/StatusCodeView.js diff --git a/src/main/coffeescript/view/StatusCodeView.js b/src/main/coffeescript/view/StatusCodeView.js new file mode 100644 index 00000000..cdfa8e6d --- /dev/null +++ b/src/main/coffeescript/view/StatusCodeView.js @@ -0,0 +1,25 @@ +'use strict'; + +var StatusCodeView = Backbone.View.extend({ + initialize: function () { + + }, + + render: function(){ + $(this.el).html(Handlebars.templates.status_code(this.model)); + + if (swaggerUi.api.models.hasOwnProperty(this.model.responseModel)) { + var responseModel = { + sampleJSON: JSON.stringify(swaggerUi.api.models[this.model.responseModel].createJSONSample(), null, 2), + isParam: false, + signature: swaggerUi.api.models[this.model.responseModel].getMockSignature(), + }; + + var responseModelView = new SignatureView({model: responseModel, tagName: 'div'}); + $('.model-signature', this.$el).append(responseModelView.render().el); + } else { + $('.model-signature', this.$el).html(''); + } + return this; + } +}); \ No newline at end of file From 7755235213888b0deb21b646a70ba969dfd616ea Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:45:27 -0700 Subject: [PATCH 10/25] Convert view/SignatureView.coffee --- .../coffeescript/view/SignatureView.coffee | 51 ---------------- src/main/coffeescript/view/SignatureView.js | 60 +++++++++++++++++++ 2 files changed, 60 insertions(+), 51 deletions(-) delete mode 100644 src/main/coffeescript/view/SignatureView.coffee create mode 100644 src/main/coffeescript/view/SignatureView.js diff --git a/src/main/coffeescript/view/SignatureView.coffee b/src/main/coffeescript/view/SignatureView.coffee deleted file mode 100644 index f3b4bde9..00000000 --- a/src/main/coffeescript/view/SignatureView.coffee +++ /dev/null @@ -1,51 +0,0 @@ -class SignatureView extends Backbone.View - events: { - 'click a.description-link' : 'switchToDescription' - 'click a.snippet-link' : 'switchToSnippet' - 'mousedown .snippet' : 'snippetToTextArea' - } - - initialize: -> - - render: -> - template = @template() - $(@el).html(template(@model)) - - @switchToSnippet() - - @isParam = @model.isParam - - if @isParam - $('.notice', $(@el)).text('Click to set as parameter value') - - @ - - template: -> - Handlebars.templates.signature - - # handler for show signature - switchToDescription: (e) -> - e?.preventDefault() - $(".snippet", $(@el)).hide() - $(".description", $(@el)).show() - $('.description-link', $(@el)).addClass('selected') - $('.snippet-link', $(@el)).removeClass('selected') - - # handler for show sample - switchToSnippet: (e) -> - e?.preventDefault() - $(".description", $(@el)).hide() - $(".snippet", $(@el)).show() - $('.snippet-link', $(@el)).addClass('selected') - $('.description-link', $(@el)).removeClass('selected') - - # handler for snippet to text area - snippetToTextArea: (e) -> - if @isParam - e?.preventDefault() - textArea = $('textarea', $(@el.parentNode.parentNode.parentNode)) - if $.trim(textArea.val()) == '' - textArea.val(@model.sampleJSON) - - - diff --git a/src/main/coffeescript/view/SignatureView.js b/src/main/coffeescript/view/SignatureView.js new file mode 100644 index 00000000..693da646 --- /dev/null +++ b/src/main/coffeescript/view/SignatureView.js @@ -0,0 +1,60 @@ +'use strict'; + +var SignatureView = Backbone.View.extend({ + events: { + 'click a.description-link' : 'switchToDescription', + 'click a.snippet-link' : 'switchToSnippet', + 'mousedown .snippet' : 'snippetToTextArea' + }, + + initialize: function () { + + }, + + render: function(){ + + $(this.el).html(Handlebars.templates.signature(this.model)); + + this.switchToSnippet(); + + this.isParam = this.model.isParam; + + if (this.isParam) { + $('.notice', $(this.el)).text('Click to set as parameter value'); + } + + return this; + }, + + // handler for show signature + switchToDescription: function(e){ + if (e) { e.preventDefault(); } + + $('.snippet', $(this.el)).hide(); + $('.description', $(this.el)).show(); + $('.description-link', $(this.el)).addClass('selected'); + $('.snippet-link', $(this.el)).removeClass('selected'); + }, + + // handler for show sample + switchToSnippet: function(e){ + if (e) { e.preventDefault(); } + + $('.description', $(this.el)).hide(); + $('.snippet', $(this.el)).show(); + $('.snippet-link', $(this.el)).addClass('selected'); + $('.description-link', $(this.el)).removeClass('selected'); + }, + + // handler for snippet to text area + snippetToTextArea: function(e) { + if (this.isParam) { + if (e) { e.preventDefault(); } + + var textArea = $('textarea', $(this.el.parentNode.parentNode.parentNode)); + if ($.trim(textArea.val()) === '') { + textArea.val(this.model.sampleJSON); + } + } + } +}); \ No newline at end of file From e3a502c3df04fbf539409789a20f64b8600c3d43 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:50:31 -0700 Subject: [PATCH 11/25] Convert view/ResourceView.coffee --- src/main/coffeescript/view/ResourceView.js | 71 ++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/main/coffeescript/view/ResourceView.js diff --git a/src/main/coffeescript/view/ResourceView.js b/src/main/coffeescript/view/ResourceView.js new file mode 100644 index 00000000..4d0ec3ca --- /dev/null +++ b/src/main/coffeescript/view/ResourceView.js @@ -0,0 +1,71 @@ +'use strict'; + +var ResourceView = Backbone.View.extend({ + initialize: function(opts) { + opts = opts || {}; + this.auths = opts.auths; + if ('' === this.model.description) { + this.model.description = null; + } + if (this.model.description) { + this.model.summary = this.model.description; + } + }, + + render: function(){ + var methods = {}; + + + $(this.el).html(Handlebars.templates.resource(this.model)); + + // Render each operation + this.model.operationsArray.forEach(function(operation){ + + var counter = 0; + var id = operation.nickname; + + while (typeof methods[id] !== 'undefined') { + id = id + '_' + counter; + counter += 1; + } + + methods[id] = operation; + + operation.nickname = id; + operation.parentId = this.model.id; + this.addOperation(operation); + }); + + $('.toggleEndpointList', this.el).click(this.callDocs.bind(this, 'toggleEndpointListForResource')); + $('.collapseResource', this.el).click(this.callDocs.bind(this, 'collapseOperationsForResource')); + $('.expandResource', this.el).click(this.callDocs.bind(this, 'expandOperationsForResource')); + + return this; + }, + + addOperation: function(operation) { + + operation.number = this.number; + + // Render an operation and add it to operations li + var operationView = new OperationView({ + model: operation, + tagName: 'li', + className: 'endpoint', + swaggerOptions: this.options.swaggerOptions, + auths: this.auths + }); + + $('.endpoints', $(this.el)).append(operationView.render().el); + + this.number++; + + }, + // Generic Event handler (`Docs` is global) + + + callDocs: function(fnName, e) { + e.preventDefault(); + Docs[fnName](e.currentTarget.getAttribute('data-id')); + } +}); \ No newline at end of file From 140dc776c91c6c2ce0ebb15c512c5ff6242bd89f Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 11:53:09 -0700 Subject: [PATCH 12/25] Convert view/ParameterContentTypeView.coffee --- .../view/ParameterContentTypeView.coffee | 14 -------------- .../coffeescript/view/ParameterContentTypeView.js | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) delete mode 100644 src/main/coffeescript/view/ParameterContentTypeView.coffee create mode 100644 src/main/coffeescript/view/ParameterContentTypeView.js diff --git a/src/main/coffeescript/view/ParameterContentTypeView.coffee b/src/main/coffeescript/view/ParameterContentTypeView.coffee deleted file mode 100644 index 3a969733..00000000 --- a/src/main/coffeescript/view/ParameterContentTypeView.coffee +++ /dev/null @@ -1,14 +0,0 @@ -class ParameterContentTypeView extends Backbone.View - initialize: -> - - render: -> - template = @template() - $(@el).html(template(@model)) - - $('label[for=parameterContentType]', $(@el)).text('Parameter content type:') - - @ - - template: -> - Handlebars.templates.parameter_content_type - diff --git a/src/main/coffeescript/view/ParameterContentTypeView.js b/src/main/coffeescript/view/ParameterContentTypeView.js new file mode 100644 index 00000000..a23d17c1 --- /dev/null +++ b/src/main/coffeescript/view/ParameterContentTypeView.js @@ -0,0 +1,14 @@ +'use strict'; + +var ParameterContentTypeView = Backbone.View.extend({ + initialize: function () {}, + + render: function(){ + $(this.el).html(Handlebars.templates.parameter_content_type(this.model)); + + $('label[for=parameterContentType]', $(this.el)).text('Parameter content type:'); + + return this; + } + +}); \ No newline at end of file From c85835775be2861f48ad51c07fbdedb4946ea04c Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 12:41:22 -0700 Subject: [PATCH 13/25] Convert view/ParameterView.coffee --- .../coffeescript/view/ParameterView.coffee | 79 -------------- src/main/coffeescript/view/ParameterView.js | 101 ++++++++++++++++++ 2 files changed, 101 insertions(+), 79 deletions(-) delete mode 100644 src/main/coffeescript/view/ParameterView.coffee create mode 100644 src/main/coffeescript/view/ParameterView.js diff --git a/src/main/coffeescript/view/ParameterView.coffee b/src/main/coffeescript/view/ParameterView.coffee deleted file mode 100644 index bac2bcd8..00000000 --- a/src/main/coffeescript/view/ParameterView.coffee +++ /dev/null @@ -1,79 +0,0 @@ -class ParameterView extends Backbone.View - initialize: -> - Handlebars.registerHelper 'isArray', - (param, opts) -> - if param.type.toLowerCase() == 'array' || param.allowMultiple - opts.fn(@) - else - opts.inverse(@) - - render: -> - type = @model.type || @model.dataType - - if typeof type is 'undefined' - schema = @model.schema - if schema and schema['$ref'] - ref = schema['$ref'] - if ref.indexOf('#/definitions/') is 0 - type = ref.substring('#/definitions/'.length) - else - type = ref - - @model.type = type - @model.paramType = @model.in || @model.paramType - @model.isBody = true if @model.paramType == 'body' or @model.in == 'body' - @model.isFile = true if type and type.toLowerCase() == 'file' - @model.default = (@model.default || @model.defaultValue) - - if@model.allowableValues - @model.isList = true - - template = @template() - $(@el).html(template(@model)) - - signatureModel = - sampleJSON: @model.sampleJSON - isParam: true - signature: @model.signature - - if @model.sampleJSON - signatureView = new SignatureView({model: signatureModel, tagName: 'div'}) - $('.model-signature', $(@el)).append signatureView.render().el - else - $('.model-signature', $(@el)).html(@model.signature) - - isParam = false - - if @model.isBody - isParam = true - - contentTypeModel = - isParam: isParam - - contentTypeModel.consumes = @model.consumes - - if isParam - parameterContentTypeView = new ParameterContentTypeView({model: contentTypeModel}) - $('.parameter-content-type', $(@el)).append parameterContentTypeView.render().el - - else - responseContentTypeView = new ResponseContentTypeView({model: contentTypeModel}) - $('.response-content-type', $(@el)).append responseContentTypeView.render().el - - @ - - # Return an appropriate template based on if the parameter is a list, readonly, required - template: -> - if @model.isList - Handlebars.templates.param_list - else - if @options.readOnly - if @model.required - Handlebars.templates.param_readonly_required - else - Handlebars.templates.param_readonly - else - if @model.required - Handlebars.templates.param_required - else - Handlebars.templates.param diff --git a/src/main/coffeescript/view/ParameterView.js b/src/main/coffeescript/view/ParameterView.js new file mode 100644 index 00000000..1253ba8c --- /dev/null +++ b/src/main/coffeescript/view/ParameterView.js @@ -0,0 +1,101 @@ +'use strict'; + +var ParameterView = Backbone.View.extend({ + initialize: function(){ + Handlebars.registerHelper('isArray', function(param, opts) { + if (param.type.toLowerCase() === 'array' || param.allowMultiple) { + opts.fn(this); + } else { + opts.inverse(this); + } + }); + }, + + render: function() { + var type = this.model.type || this.model.dataType; + + if (typeof type === 'undefined') { + var schema = this.model.schema; + if (schema && schema.$ref) { + var ref = schema.$ref; + if (ref.indexOf('#/definitions/') === 0) { + type = ref.substring('#/definitions/'.length); + } else { + type = ref; + } + } + } + + this.model.type = type; + this.model.paramType = this.model.in || this.model.paramType; + this.model.isBody = this.model.paramType === 'body' || this.model.in === 'body'; + this.model.isFile = type && type.toLowerCase() === 'file'; + this.model.default = (this.model.default || this.model.defaultValue); + + if (this.model.allowableValues) { + this.model.isList = true; + } + + var template = this.template(); + $(this.el).html(template(this.model)); + + var signatureModel = { + sampleJSON: this.model.sampleJSON, + isParam: true, + signature: this.model.signature + }; + + if (this.model.sampleJSON) { + var signatureView = new SignatureView({model: signatureModel, tagName: 'div'}); + $('.model-signature', $(this.el)).append(signatureView.render().el); + } + else { + $('.model-signature', $(this.el)).html(this.model.signature); + } + + var isParam = false; + + if (this.model.isBody) { + isParam = true; + } + + var contentTypeModel = { + isParam: isParam + }; + + contentTypeModel.consumes = this.model.consumes; + + if (isParam) { + var parameterContentTypeView = new ParameterContentTypeView({model: contentTypeModel}); + $('.parameter-content-type', $(this.el)).append(parameterContentTypeView.render().el); + } + + else { + var responseContentTypeView = new ResponseContentTypeView({model: contentTypeModel}); + $('.response-content-type', $(this.el)).append(responseContentTypeView.render().el); + } + + return this; + }, + + // Return an appropriate template based on if the parameter is a list, readonly, required + template: function(){ + if (this.model.isList) { + return Handlebars.templates.param_list; + } else { + if (this.options.readOnly) { + if (this.model.required) { + return Handlebars.templates.param_readonly_required; + } else { + return Handlebars.templates.param_readonly; + } + } else { + if (this.model.required) { + return Handlebars.templates.param_required; + } else { + return Handlebars.templates.param; + } + } + } + } +}); \ No newline at end of file From f21ade9ba758c31c846d38e6a2dcc717ed5293f8 Mon Sep 17 00:00:00 2001 From: Mohsen Azimi Date: Thu, 12 Mar 2015 15:49:43 -0700 Subject: [PATCH 14/25] Convert view/OperationView.coffee This file is not fully converted. Some methods (mostly long ones) are brought from compiled CS code --- .jshintrc | 4 +- src/main/coffeescript/view/OperationView.js | 624 ++++++++++++++++++++ 2 files changed, 627 insertions(+), 1 deletion(-) create mode 100644 src/main/coffeescript/view/OperationView.js diff --git a/.jshintrc b/.jshintrc index 2c347ba0..292552e3 100644 --- a/.jshintrc +++ b/.jshintrc @@ -21,6 +21,7 @@ "Backbone": false, "_": false, "$": false, + "jQuery": false, "marked": false, "HeaderView": false, "MainView": false, @@ -28,6 +29,7 @@ "SwaggerClient": false, "Handlebars": false, "ApiKeyAuthorization": false, - "PasswordAuthorization": false + "PasswordAuthorization": false, + "hljs": false } } \ No newline at end of file diff --git a/src/main/coffeescript/view/OperationView.js b/src/main/coffeescript/view/OperationView.js new file mode 100644 index 00000000..026e2ec6 --- /dev/null +++ b/src/main/coffeescript/view/OperationView.js @@ -0,0 +1,624 @@ +'use strict'; + +var OperationView = Backbone.View.extend({ + invocationUrl: null, + + events: { + 'submit .sandbox' : 'submitOperation', + 'click .submit' : 'submitOperation', + 'click .response_hider' : 'hideResponse', + 'click .toggleOperation' : 'toggleOperationContent', + 'mouseenter .api-ic' : 'mouseEnter', + 'mouseout .api-ic' : 'mouseExit', + }, + + initialize: function(opts) { + opts = opts || {}; + this.auths = opts.auths; + this.parentId = this.model.parentId; + this.nickname = this.model.nickname; + return this; + }, + + mouseEnter: function(e) { + var elem = $(this.el).find('.content'); + var x = e.pageX; + var y = e.pageY; + var scX = $(window).scrollLeft(); + var scY = $(window).scrollTop(); + var scMaxX = scX + $(window).width(); + var scMaxY = scY + $(window).height(); + var wd = elem.width(); + var hgh = elem.height(); + + if (x + wd > scMaxX) { + x = scMaxX - wd; + } + + if (x < scX) { + x = scX; + } + + if (y + hgh > scMaxY) { + y = scMaxY - hgh; + } + + if (y < scY) { + y = scY; + } + + var pos = {}; + pos.top = y; + pos.left = x; + elem.css(pos); + $(e.currentTarget.parentNode).find('#api_information_panel').show(); + }, + + mouseExit: function(e) { + $(e.currentTarget.parentNode).find('#api_information_panel').hide(); + }, + + // Note: copied from CoffeeScript compiled file + // TODO: redactor + render: function() { + var a, auth, auths, code, contentTypeModel, isMethodSubmissionSupported, k, key, l, len, len1, len2, len3, len4, m, modelAuths, n, o, p, param, q, ref, ref1, ref2, ref3, ref4, ref5, responseContentTypeView, responseSignatureView, schema, schemaObj, scopeIndex, signatureModel, statusCode, successResponse, type, v, value; + isMethodSubmissionSupported = jQuery.inArray(this.model.method, this.model.supportedSubmitMethods()) >= 0; + if (!isMethodSubmissionSupported) { + this.model.isReadOnly = true; + } + this.model.description = this.model.description || this.model.notes; + if (this.model.description) { + this.model.description = this.model.description.replace(/(?:\r\n|\r|\n)/g, '
'); + } + this.model.oauth = null; + modelAuths = this.model.authorizations || this.model.security; + if (modelAuths) { + if (Array.isArray(modelAuths)) { + for (l = 0, len = modelAuths.length; l < len; l++) { + auths = modelAuths[l]; + for (key in auths) { + auth = auths[key]; + for (a in this.auths) { + auth = this.auths[a]; + if (auth.type === 'oauth2') { + this.model.oauth = {}; + this.model.oauth.scopes = []; + ref1 = auth.value.scopes; + for (k in ref1) { + v = ref1[k]; + scopeIndex = auths[key].indexOf(k); + if (scopeIndex >= 0) { + o = { + scope: k, + description: v + }; + this.model.oauth.scopes.push(o); + } + } + } + } + } + } + } else { + for (k in modelAuths) { + v = modelAuths[k]; + if (k === 'oauth2') { + if (this.model.oauth === null) { + this.model.oauth = {}; + } + if (this.model.oauth.scopes === void 0) { + this.model.oauth.scopes = []; + } + for (m = 0, len1 = v.length; m < len1; m++) { + o = v[m]; + this.model.oauth.scopes.push(o); + } + } + } + } + } + if (typeof this.model.responses !== 'undefined') { + this.model.responseMessages = []; + ref2 = this.model.responses; + for (code in ref2) { + value = ref2[code]; + schema = null; + schemaObj = this.model.responses[code].schema; + if (schemaObj && schemaObj.$ref) { + schema = schemaObj.$ref; + if (schema.indexOf('#/definitions/') === 0) { + schema = schema.substring('#/definitions/'.length); + } + } + this.model.responseMessages.push({ + code: code, + message: value.description, + responseModel: schema + }); + } + } + if (typeof this.model.responseMessages === 'undefined') { + this.model.responseMessages = []; + } + signatureModel = null; + if (this.model.successResponse) { + successResponse = this.model.successResponse; + for (key in successResponse) { + value = successResponse[key]; + this.model.successCode = key; + if (typeof value === 'object' && typeof value.createJSONSample === 'function') { + signatureModel = { + sampleJSON: JSON.stringify(value.createJSONSample(), void 0, 2), + isParam: false, + signature: value.getMockSignature() + }; + } + } + } else if (this.model.responseClassSignature && this.model.responseClassSignature !== 'string') { + signatureModel = { + sampleJSON: this.model.responseSampleJSON, + isParam: false, + signature: this.model.responseClassSignature + }; + } + $(this.el).html(Handlebars.templates.operation(this.model)); + if (signatureModel) { + responseSignatureView = new SignatureView({ + model: signatureModel, + tagName: 'div' + }); + $('.model-signature', $(this.el)).append(responseSignatureView.render().el); + } else { + this.model.responseClassSignature = 'string'; + $('.model-signature', $(this.el)).html(this.model.type); + } + contentTypeModel = { + isParam: false + }; + contentTypeModel.consumes = this.model.consumes; + contentTypeModel.produces = this.model.produces; + ref3 = this.model.parameters; + for (n = 0, len2 = ref3.length; n < len2; n++) { + param = ref3[n]; + type = param.type || param.dataType || ''; + if (typeof type === 'undefined') { + schema = param.schema; + if (schema && schema.$ref) { + ref = schema.$ref; + if (ref.indexOf('#/definitions/') === 0) { + type = ref.substring('#/definitions/'.length); + } else { + type = ref; + } + } + } + if (type && type.toLowerCase() === 'file') { + if (!contentTypeModel.consumes) { + contentTypeModel.consumes = 'multipart/form-data'; + } + } + param.type = type; + } + responseContentTypeView = new ResponseContentTypeView({ + model: contentTypeModel + }); + $('.response-content-type', $(this.el)).append(responseContentTypeView.render().el); + ref4 = this.model.parameters; + for (p = 0, len3 = ref4.length; p < len3; p++) { + param = ref4[p]; + this.addParameter(param, contentTypeModel.consumes); + } + ref5 = this.model.responseMessages; + for (q = 0, len4 = ref5.length; q < len4; q++) { + statusCode = ref5[q]; + this.addStatusCode(statusCode); + } + return this; + }, + + addParameter: function(param, consumes) { + // Render a parameter + param.consumes = consumes; + var paramView = new ParameterView({ + model: param, + tagName: 'tr', + readOnly: this.model.isReadOnly + }); + $('.operation-params', $(this.el)).append(paramView.render().el); + }, + + addStatusCode: function(statusCode) { + // Render status codes + var statusCodeView = new StatusCodeView({model: statusCode, tagName: 'tr'}); + $('.operation-status', $(this.el)).append(statusCodeView.render().el); + }, + + // Note: copied from CoffeeScript compiled file + // TODO: redactor + submitOperation: function(e) { + var error_free, form, isFileUpload, l, len, len1, len2, m, map, n, o, opts, ref1, ref2, ref3, val; + if (e !== null) { + e.preventDefault(); + } + form = $('.sandbox', $(this.el)); + error_free = true; + form.find('input.required').each(function() { + $(this).removeClass('error'); + if (jQuery.trim($(this).val()) === '') { + $(this).addClass('error'); + $(this).wiggle({ + callback: (function(_this) { + return function() { + $(_this).focus(); + }; + })(this) + }); + error_free = false; + } + }); + form.find('textarea.required').each(function() { + $(this).removeClass('error'); + if (jQuery.trim($(this).val()) === '') { + $(this).addClass('error'); + $(this).wiggle({ + callback: (function(_this) { + return function() { + return $(_this).focus(); + }; + })(this) + }); + error_free = false; + } + }); + if (error_free) { + map = {}; + opts = { + parent: this + }; + isFileUpload = false; + ref1 = form.find('input'); + for (l = 0, len = ref1.length; l < len; l++) { + o = ref1[l]; + if ((o.value !== null) && jQuery.trim(o.value).length > 0) { + map[o.name] = o.value; + } + if (o.type === 'file') { + map[o.name] = o.files[0]; + isFileUpload = true; + } + } + ref2 = form.find('textarea'); + for (m = 0, len1 = ref2.length; m < len1; m++) { + o = ref2[m]; + if ((o.value !== null) && jQuery.trim(o.value).length > 0) { + map[o.name] = o.value; + } + } + ref3 = form.find('select'); + for (n = 0, len2 = ref3.length; n < len2; n++) { + o = ref3[n]; + val = this.getSelectedValue(o); + if ((val !== null) && jQuery.trim(val).length > 0) { + map[o.name] = val; + } + } + opts.responseContentType = $('div select[name=responseContentType]', $(this.el)).val(); + opts.requestContentType = $('div select[name=parameterContentType]', $(this.el)).val(); + $('.response_throbber', $(this.el)).show(); + if (isFileUpload) { + return this.handleFileUpload(map, form); + } else { + return this.model['do'](map, opts, this.showCompleteStatus, this.showErrorStatus, this); + } + } + }, + + success: function(response, parent) { + parent.showCompleteStatus(response); + }, + + // Note: This is compiled code + // TODO: Refactor + handleFileUpload: function(map, form) { + var bodyParam, el, headerParams, l, len, len1, len2, len3, m, n, o, obj, p, param, params, ref1, ref2, ref3, ref4; + ref1 = form.serializeArray(); + for (l = 0, len = ref1.length; l < len; l++) { + o = ref1[l]; + if ((o.value !== null) && jQuery.trim(o.value).length > 0) { + map[o.name] = o.value; + } + } + bodyParam = new FormData(); + params = 0; + ref2 = this.model.parameters; + for (m = 0, len1 = ref2.length; m < len1; m++) { + param = ref2[m]; + if (param.paramType === 'form' || param['in'] === 'formData') { + if (param.type.toLowerCase() !== 'file' && map[param.name] !== void 0) { + bodyParam.append(param.name, map[param.name]); + } + } + } + headerParams = {}; + ref3 = this.model.parameters; + for (n = 0, len2 = ref3.length; n < len2; n++) { + param = ref3[n]; + if (param.paramType === 'header') { + headerParams[param.name] = map[param.name]; + } + } + ref4 = form.find('input[type~="file"]'); + for (p = 0, len3 = ref4.length; p < len3; p++) { + el = ref4[p]; + if (typeof el.files[0] !== 'undefined') { + bodyParam.append($(el).attr('name'), el.files[0]); + params += 1; + } + } + this.invocationUrl = this.model.supportHeaderParams() ? (headerParams = this.model.getHeaderParams(map), delete headerParams['Content-Type'], this.model.urlify(map, false)) : this.model.urlify(map, true); + $('.request_url', $(this.el)).html('
');
+    $('.request_url pre', $(this.el)).text(this.invocationUrl);
+    obj = {
+      type: this.model.method,
+      url: this.invocationUrl,
+      headers: headerParams,
+      data: bodyParam,
+      dataType: 'json',
+      contentType: false,
+      processData: false,
+      error: (function(_this) {
+        return function(data) {
+          return _this.showErrorStatus(_this.wrap(data), _this);
+        };
+      })(this),
+      success: (function(_this) {
+        return function(data) {
+          return _this.showResponse(data, _this);
+        };
+      })(this),
+      complete: (function(_this) {
+        return function(data) {
+          return _this.showCompleteStatus(_this.wrap(data), _this);
+        };
+      })(this)
+    };
+    if (window.authorizations) {
+      window.authorizations.apply(obj);
+    }
+    if (params === 0) {
+      obj.data.append('fake', 'true');
+    }
+    jQuery.ajax(obj);
+    return false;
+    // end of file-upload nastiness
+  },
+  // wraps a jquery response as a shred response
+
+  wrap: function(data) {
+   var h, headerArray, headers, i, l, len, o;
+    headers = {};
+    headerArray = data.getAllResponseHeaders().split('\r');
+    for (l = 0, len = headerArray.length; l < len; l++) {
+      i = headerArray[l];
+      h = i.match(/^([^:]*?):(.*)$/);
+      if (!h) {
+        h = [];
+      }
+      h.shift();
+      if (h[0] !== void 0 && h[1] !== void 0) {
+        headers[h[0].trim()] = h[1].trim();
+      }
+    }
+    o = {};
+    o.content = {};
+    o.content.data = data.responseText;
+    o.headers = headers;
+    o.request = {};
+    o.request.url = this.invocationUrl;
+    o.status = data.status;
+    return o;
+  },
+
+  getSelectedValue: function(select) {
+    if (!select.multiple) {
+      return select.value;
+    } else {
+      var options = [];
+      for (var l = 0, len = select.options.length; l < len; l++) {
+        var opt = select.options[l];
+        if (opt.selected) {
+          options.push(opt.value);
+        }
+      }
+      if (options.length > 0) {
+        return options;
+      } else {
+        return null;
+      }
+    }
+  },
+
+  // handler for hide response link
+  hideResponse: function(e) {
+    if (e) { e.preventDefault(); }
+    $('.response', $(this.el)).slideUp();
+    $('.response_hider', $(this.el)).fadeOut();
+  },
+
+  // Show response from server
+  showResponse: function(response) {
+    var prettyJson = JSON.stringify(response, null, '\t').replace(/\n/g, '
'); + $('.response_body', $(this.el)).html(_.escape(prettyJson)); + }, + + // Show error from server + showErrorStatus: function(data, parent) { + parent.showStatus(data); + }, + + // show the status codes + showCompleteStatus: function(data, parent){ + parent.showStatus(data); + }, + + // Adapted from http://stackoverflow.com/a/2893259/454004 + // Note: directly ported from CoffeeScript + // TODO: Cleanup CoffeeScript artifacts + formatXml: function(xml) { + var contexp, fn, formatted, indent, l, lastType, len, lines, ln, pad, reg, transitions, wsexp; + reg = /(>)(<)(\/*)/g; + wsexp = /[ ]*(.*)[ ]+\n/g; + contexp = /(<.+>)(.+\n)/g; + xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2'); + pad = 0; + formatted = ''; + lines = xml.split('\n'); + indent = 0; + lastType = 'other'; + transitions = { + 'single->single': 0, + 'single->closing': -1, + 'single->opening': 0, + 'single->other': 0, + 'closing->single': 0, + 'closing->closing': -1, + 'closing->opening': 0, + 'closing->other': 0, + 'opening->single': 1, + 'opening->closing': 0, + 'opening->opening': 1, + 'opening->other': 1, + 'other->single': 0, + 'other->closing': -1, + 'other->opening': 0, + 'other->other': 0 + }; + fn = function(ln) { + var fromTo, j, key, padding, type, types, value; + types = { + single: Boolean(ln.match(/<.+\/>/)), + closing: Boolean(ln.match(/<\/.+>/)), + opening: Boolean(ln.match(/<[^!?].*>/)) + }; + type = ((function() { + var results; + results = []; + for (key in types) { + value = types[key]; + if (value) { + results.push(key); + } + } + return results; + })())[0]; + type = type === void 0 ? 'other' : type; + fromTo = lastType + '->' + type; + lastType = type; + padding = ''; + indent += transitions[fromTo]; + padding = ((function() { + var m, ref1, results; + results = []; + for (j = m = 0, ref1 = indent; 0 <= ref1 ? m < ref1 : m > ref1; j = 0 <= ref1 ? ++m : --m) { + results.push(' '); + } + return results; + })()).join(''); + if (fromTo === 'opening->closing') { + formatted = formatted.substr(0, formatted.length - 1) + ln + '\n'; + } else { + formatted += padding + ln + '\n'; + } + }; + for (l = 0, len = lines.length; l < len; l++) { + ln = lines[l]; + fn(ln); + } + return formatted; + }, + + // puts the response data in UI + showStatus: function(response) { + var url, content; + if (response.content === undefined) { + content = response.data; + url = response.url; + } else { + content = response.content.data; + url = response.request.url; + } + var headers = response.headers; + + // if server is nice, and sends content-type back, we can use it + var contentType = null; + if (headers) { + contentType = headers['Content-Type'] || headers['content-type']; + if (contentType) { + contentType = contentType.split(';')[0].trim(); + } + } + $('.response_body', $(this.el)).removeClass('json'); + $('.response_body', $(this.el)).removeClass('xml'); + + var supportsAudioPlayback = function(contentType){ + var audioElement = document.createElement('audio'); + return !!(audioElement.canPlayType && audioElement.canPlayType(contentType).replace(/no/, '')); + }; + + var pre; + var code; + if (!content) { + code = $('').text('no content'); + pre = $('
').append(code);
+    } else if (contentType === 'application/json' || /\+json$/.test(contentType)) {
+      var json = null;
+      try {
+        json = JSON.stringify(JSON.parse(content), null, '  ');
+      } catch (_error) {
+        json = 'can\'t parse JSON.  Raw result:\n\n' + content;
+      }
+      code = $('').text(json);
+      pre = $('
').append(code);
+    } else if (contentType === 'application/xml' || /\+xml$/.test(contentType)) {
+      code = $('').text(this.formatXml(content));
+      pre = $('
').append(code);
+    } else if (contentType === 'text/html') {
+      code = $('').html(_.escape(content));
+      pre = $('
').append(code);
+    } else if (/^image\//.test(contentType)) {
+      pre = $('').attr('src', url);
+    } else if (/^audio\//.test(contentType) && supportsAudioPlayback(contentType)) {
+      pre = $('