diff --git a/build/tasks/build.js b/build/tasks/build.js
index c5cd692f..ec122a9e 100644
--- a/build/tasks/build.js
+++ b/build/tasks/build.js
@@ -50,34 +50,6 @@ gulp.task('tsc', function() {
exec('tsc -p ./tsconfig.json');
});
-
-// function compileTs(files, es5) {
-// var tsProject = ts.createProject('tsconfig.json');
-// var allFiles = [].concat(files, ['typings/**/*.d.ts']);
-// var res = gulp.src(allFiles, {
-// base: config.src,
-// outDir: config.tmp
-// })
-// .pipe(tslint())
-// .pipe(tslint.report('prose', {
-// summarizeFailureOutput: true,
-// emitError: !watchMode
-// }))
-// .pipe(sourcemaps.init())
-// .pipe(ts(tsProject))
-// .on('error', function () {
-// if (watchMode) {
-// return;
-// }
-// process.exit(1);
-// });
-// return res.js
-// .pipe(sourcemaps.write('.', {
-// includeContent: inline
-// }))
-// .pipe(gulp.dest(config.tmp));
-// }
-
gulp.task('inlineTemplates', ['sass'], function() {
return gulp.src('.tmp/**/*.js', { base: './tmp' })
.pipe(replace(/'(.*?)\.css'/g, '\'$1.scss\''))
diff --git a/lib/components/JsonSchema/json-schema-common.scss b/lib/components/JsonSchema/json-schema-common.scss
index e805e8eb..e6d24407 100644
--- a/lib/components/JsonSchema/json-schema-common.scss
+++ b/lib/components/JsonSchema/json-schema-common.scss
@@ -5,67 +5,69 @@ $cell-spacing: 25px;
$cell-padding: 10px;
$bullet-margin: 10px;
$line-border: $lines-width solid $tree-lines-color;
-$line-border-erase: ($lines-width + 1px) solid white;
+$line-border-erase: ($lines-width + 1px) solid #fff;
$param-name-height: 20px;
-$sub-schema-offset: ($bullet-size/2) + $bullet-margin;
+$sub-schema-offset: ($bullet-size / 2) + $bullet-margin;
.param-name {
-
- font-size: 0.929em;
- padding: $cell-padding 0 $cell-padding 0;
- font-weight: $regular;
- box-sizing: border-box;
- line-height: $param-name-height;
- border-left: $line-border;
- white-space: nowrap;
position: relative;
+ border-left: $line-border;
+ padding: $cell-padding 0;
vertical-align: top;
+ line-height: $param-name-height;
+
+ white-space: nowrap;
+ font-size: 0.929em;
+ font-weight: $regular;
+
+ box-sizing: border-box;
}
.param-name-wrap {
- padding-right: $cell-spacing;
display: inline-block;
+ padding-right: $cell-spacing;
font-family: $headers-font, $headers-font-family;
}
.param-info {
- padding: $cell-padding 0;
- box-sizing: border-box;
border-bottom: 1px solid #ccc;
+ padding: $cell-padding 0;
width: 75%;
line-height: 1em;
+
+ box-sizing: border-box;
}
.param-range {
- color: rgba($primary-color, .7);
position: relative;
top: 1px;
- padding: 0 4px;
+ margin-right: 6px;
+ margin-left: 6px;
border-radius: $border-radius;
background-color: rgba($primary-color, .1);
- margin-left: 6px;
- margin-right: 6px;
+ padding: 0 4px;
+ color: rgba($primary-color, 0.7);
}
.param-description {
- font-size: 13px;
+ font-size: 13px;
}
.param-required {
- color: red;
- font-weight: bold;
- font-size: 12px;
- line-height: $param-name-height;
vertical-align: middle;
+ line-height: $param-name-height;
+ color: #f00;
+ font-size: 12px;
+ font-weight: bold;
}
.param-type {
+ vertical-align: middle;
+ line-height: $param-name-height;
color: rgba($black, 0.4);
font-size: 0.929em;
- line-height: $param-name-height;
- vertical-align: middle;
font-weight: normal;
}
@@ -158,11 +160,11 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
font-size: 13px;
&:before {
- content: "Values: {"
+ content: 'Values: {';
}
&:after {
- content: "}"
+ content: '}';
}
> .enum-value {
diff --git a/lib/components/Method/method.html b/lib/components/Method/method.html
index 333ceaa3..6821882b 100644
--- a/lib/components/Method/method.html
+++ b/lib/components/Method/method.html
@@ -14,10 +14,14 @@
Definition
-
- {{data.httpMethod}}
- {{data.apiUrl}}{{data.path}}
-
+
+
+
{{data.httpMethod}}
+ {{data.apiUrl}}{{data.path}}
+
+
diff --git a/lib/components/Method/method.scss b/lib/components/Method/method.scss
index 75043e51..aa373c10 100644
--- a/lib/components/Method/method.scss
+++ b/lib/components/Method/method.scss
@@ -134,6 +134,10 @@ responses-samples {
text-transform: uppercase;
}
+[select-on-click] {
+ cursor: pointer;
+}
+
@media (max-width: 1100px) {
.methods:before {
display: none;
diff --git a/lib/components/Method/method.ts b/lib/components/Method/method.ts
index 781b2afe..3df30528 100644
--- a/lib/components/Method/method.ts
+++ b/lib/components/Method/method.ts
@@ -3,6 +3,8 @@ import { Input } from '@angular/core';
import JsonPointer from '../../utils/JsonPointer';
import { RedocComponent, BaseComponent, SpecManager} from '../base';
+import { SelectOnClick } from '../../shared/components/SelectOnClick/select-on-click.directive';
+
import { ParamsList } from '../ParamsList/params-list';
import { ResponsesList } from '../ResponsesList/responses-list';
import { ResponsesSamples } from '../ResponsesSamples/responses-samples';
@@ -14,7 +16,7 @@ import { SchemaHelper } from '../../services/schema-helper.service';
selector: 'method',
templateUrl: './method.html',
styleUrls: ['./method.css'],
- directives: [ ParamsList, ResponsesList, ResponsesSamples, SchemaSample, RequestSamples ],
+ directives: [ ParamsList, ResponsesList, ResponsesSamples, SchemaSample, RequestSamples, SelectOnClick ],
detect: true
})
export class Method extends BaseComponent {
diff --git a/lib/components/Redoc/redoc.scss b/lib/components/Redoc/redoc.scss
index dcbb2bd4..fe47c8da 100644
--- a/lib/components/Redoc/redoc.scss
+++ b/lib/components/Redoc/redoc.scss
@@ -139,6 +139,17 @@ api-logo {
color: $red;
border: 1px solid rgba(38,50,56,0.1);
}
+
+ .hint--inversed {
+ &:before {
+ border-top-color: #fff;
+ }
+
+ &:after {
+ background: #fff;
+ color: #383838;
+ }
+ }
}
footer {
diff --git a/lib/components/RequestSamples/request-samples.html b/lib/components/RequestSamples/request-samples.html
index 53a9ef20..05715ba8 100644
--- a/lib/components/RequestSamples/request-samples.html
+++ b/lib/components/RequestSamples/request-samples.html
@@ -5,6 +5,11 @@
-
+
diff --git a/lib/components/RequestSamples/request-samples.scss b/lib/components/RequestSamples/request-samples.scss
index 84569503..60c3fc5a 100644
--- a/lib/components/RequestSamples/request-samples.scss
+++ b/lib/components/RequestSamples/request-samples.scss
@@ -1,5 +1,37 @@
@import '../../shared/styles/variables';
+.action-buttons {
+ display: block;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ transform: translateY(100%);
+
+ > span {
+ float: right;
+ }
+
+ > span > a {
+ padding: 2px 10px;
+ color: #ffffff;
+ cursor: pointer;
+ background-color: darken($black, 4%);
+
+ &:hover {
+ background-color: $black;
+ }
+ }
+
+ &:after {
+ display: block;
+ content: '';
+ clear: both;
+ }
+}
+
+.code-sample:hover > .action-buttons {
+ opacity: 1;
+}
+
header {
font-family: $headers-font;
font-size: $h5;
@@ -39,4 +71,10 @@ pre {
word-break: break-all;
word-wrap: break-word;
white-space: pre-wrap;
+ margin-top: 0;
+ overflow-x: auto;
+ padding: 20px;
+ border-radius: 4px;
+ background-color: #222d32;
+ margin-bottom: 36px;
}
diff --git a/lib/components/RequestSamples/request-samples.ts b/lib/components/RequestSamples/request-samples.ts
index 487fcc9f..4b3fcc83 100644
--- a/lib/components/RequestSamples/request-samples.ts
+++ b/lib/components/RequestSamples/request-samples.ts
@@ -9,11 +9,13 @@ import { SchemaSample } from '../SchemaSample/schema-sample';
import { PrismPipe } from '../../utils/pipes';
import { RedocEventsService } from '../../services/index';
+import { CopyButton } from '../../shared/components/CopyButton/copy-button.directive';
+
@RedocComponent({
selector: 'request-samples',
templateUrl: './request-samples.html',
styleUrls: ['./request-samples.css'],
- directives: [SchemaSample, Tabs, Tab],
+ directives: [SchemaSample, Tabs, Tab, CopyButton],
inputs: ['schemaPointer'],
pipes: [PrismPipe],
detect: true,
diff --git a/lib/components/SchemaSample/schema-sample.html b/lib/components/SchemaSample/schema-sample.html
index 8d0f2973..9e34e6dd 100644
--- a/lib/components/SchemaSample/schema-sample.html
+++ b/lib/components/SchemaSample/schema-sample.html
@@ -1,5 +1,10 @@
diff --git a/lib/components/SchemaSample/schema-sample.scss b/lib/components/SchemaSample/schema-sample.scss
index 0df65e80..c0480079 100644
--- a/lib/components/SchemaSample/schema-sample.scss
+++ b/lib/components/SchemaSample/schema-sample.scss
@@ -3,7 +3,56 @@
pre {
background-color: transparent;
padding: 0;
+ margin: 0;
+ clear: both;
}
+
+.action-buttons {
+ display: block;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ transform: translateY(100%);
+
+ > span {
+ float: right;
+ }
+
+ > span > a {
+ padding: 2px 10px;
+ color: #ffffff;
+ cursor: pointer;
+
+ &:before {
+ content: '|';
+ display: inline-block;
+ transform: translateX(-10px);
+ }
+
+ &:last-child:before {
+ display: none;
+ margin-left: 0;
+ }
+
+ &:first-child {
+ margin-right: 0;
+ }
+
+ &:hover {
+ background-color: $black;
+ }
+ }
+
+ &:after {
+ display: block;
+ content: '';
+ clear: both;
+ }
+}
+
+.snippet:hover .action-buttons {
+ opacity: 1;
+}
+
:host {
.property {
//font-weight: bold;
@@ -107,7 +156,7 @@ pre {
display: none;
}
- .collapsed>.ellipsis {
+ .collapsed > .ellipsis {
display: inherit;
}
diff --git a/lib/components/SchemaSample/schema-sample.ts b/lib/components/SchemaSample/schema-sample.ts
index 6a2a0d11..86abd1b2 100644
--- a/lib/components/SchemaSample/schema-sample.ts
+++ b/lib/components/SchemaSample/schema-sample.ts
@@ -8,15 +8,19 @@ import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { JsonFormatter } from '../../utils/JsonFormatterPipe';
import { SchemaNormalizer } from '../../services/schema-normalizer.service';
+import { CopyButton } from '../../shared/components/CopyButton/copy-button.directive';
+
@RedocComponent({
selector: 'schema-sample',
templateUrl: './schema-sample.html',
pipes: [JsonFormatter],
+ directives: [CopyButton],
styleUrls: ['./schema-sample.css']
})
export class SchemaSample extends BaseComponent {
element: any;
data: any;
+ enableButtons: boolean = false;
@Input() skipReadOnly:boolean;
private _normalizer:SchemaNormalizer;
@@ -74,6 +78,10 @@ export class SchemaSample extends BaseComponent {
}
this.cache(sample);
this.data.sample = sample;
+
+ if (typeof sample === 'object') {
+ this.enableButtons = true;
+ }
}
cache(sample) {
@@ -108,4 +116,21 @@ export class SchemaSample extends BaseComponent {
}
});
}
+
+ expandAll() {
+ let elements = this.element.getElementsByClassName('collapsible');
+ for (let i = 0; i < elements.length; i++) {
+ let collapsed = elements[i];
+ collapsed.parentNode.classList.remove('collapsed');
+ }
+ }
+
+ collapseAll() {
+ let elements = this.element.getElementsByClassName('collapsible');
+ for (let i = 0; i < elements.length; i++) {
+ let expanded = elements[i];
+ if (expanded.parentNode.classList.contains('redoc-json')) continue;
+ expanded.parentNode.classList.add('collapsed');
+ }
+ }
}
diff --git a/lib/services/clipboard.service.ts b/lib/services/clipboard.service.ts
new file mode 100644
index 00000000..da618fe3
--- /dev/null
+++ b/lib/services/clipboard.service.ts
@@ -0,0 +1,83 @@
+'use strict';
+
+export class Clipboard {
+ static isSupported():boolean {
+ return document.queryCommandSupported && document.queryCommandSupported('copy');
+ }
+
+ static selectElement(element:any):void {
+ let range;
+ let selection;
+ if ((document.body).createTextRange) {
+ range = (document.body).createTextRange();
+ range.moveToElementText(element);
+ range.select();
+ } else if (document.createRange && window.getSelection) {
+ selection = window.getSelection();
+ range = document.createRange();
+ range.selectNodeContents(element);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ }
+
+ static deselect():void {
+ if ( (document).selection ) {
+ (document).selection.empty();
+ } else if ( window.getSelection ) {
+ window.getSelection().removeAllRanges();
+ }
+ }
+
+ static copySelected():boolean {
+ let result;
+ try {
+ result = document.execCommand('copy');
+ } catch (err) {
+ result = false;
+ }
+ return result;
+ }
+
+ static copyElement(element:any):boolean {
+ Clipboard.selectElement(element);
+ let res = Clipboard.copySelected();
+ if (res) Clipboard.deselect();
+ return res;
+ }
+
+ static copyCustom(text:string):boolean {
+ let textArea = document.createElement('textarea');
+ textArea.style.position = 'fixed';
+ textArea.style.top = '0';
+ textArea.style.left = '0';
+
+ // Ensure it has a small width and height. Setting to 1px / 1em
+ // doesn't work as this gives a negative w/h on some browsers.
+ textArea.style.width = '2em';
+ textArea.style.height = '2em';
+
+ // We don't need padding, reducing the size if it does flash render.
+ textArea.style.padding = '0';
+
+ // Clean up any borders.
+ textArea.style.border = 'none';
+ textArea.style.outline = 'none';
+ textArea.style.boxShadow = 'none';
+
+ // Avoid flash of white box if rendered for any reason.
+ textArea.style.background = 'transparent';
+
+
+ textArea.value = text;
+
+ document.body.appendChild(textArea);
+
+ textArea.select();
+
+ let res = Clipboard.copySelected();
+
+ document.body.removeChild(textArea);
+ return res;
+ }
+}
diff --git a/lib/shared/components/CopyButton/copy-button.directive.ts b/lib/shared/components/CopyButton/copy-button.directive.ts
new file mode 100644
index 00000000..9007fbe9
--- /dev/null
+++ b/lib/shared/components/CopyButton/copy-button.directive.ts
@@ -0,0 +1,52 @@
+'use strict';
+
+import { Directive, Input, HostListener, Renderer, ElementRef, OnInit} from '@angular/core';
+import { Clipboard } from '../../../services/clipboard.service';
+
+@Directive({
+ selector: '[copy-button]'
+})
+export class CopyButton implements OnInit {
+ $element: any;
+ cancelScrollBinding: any;
+ $redocEl: any;
+ @Input() copyText: string;
+ @Input() copyElement:any;
+ @Input() hintElement:any;
+
+ constructor(private renderer: Renderer, private element: ElementRef) {}
+
+ ngOnInit () {
+ if (!Clipboard.isSupported()) {
+ this.element.nativeElement.parentNode.removeChild(this.element.nativeElement);
+ }
+ this.renderer.setElementAttribute(this.element.nativeElement, 'data-hint', 'Copy to Clipboard!');
+ }
+
+ @HostListener('click')
+ onClick() {
+ let copied;
+ if (this.copyText) {
+ copied = Clipboard.copyCustom(this.copyText);
+ } else {
+ copied = Clipboard.copyElement(this.copyElement);
+ }
+
+ if (copied) {
+ this.renderer.setElementAttribute(this.element.nativeElement, 'data-hint', 'Copied!');
+ } else {
+ let hintElem = this.hintElement || this.copyElement;
+ if (!hintElem) return;
+ this.renderer.setElementAttribute(hintElem, 'data-hint', 'Press "ctrl + c" to copy');
+ this.renderer.setElementClass(hintElem, 'hint--top', true);
+ this.renderer.setElementClass(hintElem, 'hint--always', true);
+ }
+ }
+
+ @HostListener('mouseleave')
+ onLeave() {
+ setTimeout(() => {
+ this.renderer.setElementAttribute(this.element.nativeElement, 'data-hint', 'Copy to Clipboard');
+ }, 500);
+ }
+}
diff --git a/lib/shared/components/SelectOnClick/select-on-click.directive.ts b/lib/shared/components/SelectOnClick/select-on-click.directive.ts
new file mode 100644
index 00000000..1b651f82
--- /dev/null
+++ b/lib/shared/components/SelectOnClick/select-on-click.directive.ts
@@ -0,0 +1,17 @@
+'use strict';
+
+import { Directive, HostListener, ElementRef} from '@angular/core';
+import { Clipboard } from '../../../services/clipboard.service';
+
+@Directive({
+ selector: '[select-on-click]'
+})
+export class SelectOnClick {
+ $element: any;
+ constructor(private element: ElementRef) {}
+
+ @HostListener('click')
+ onClick() {
+ Clipboard.selectElement(this.element.nativeElement);
+ }
+}
diff --git a/package.json b/package.json
index 5a2a3798..7fe16147 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "redoc",
"description": "Swagger-generated API Reference Documentation",
- "version": "0.15.3",
+ "version": "0.16.0",
"repository": {
"type": "git",
"url": "git://github.com/Rebilly/ReDoc"
diff --git a/protractor.conf.js b/protractor.conf.js
index aac8415f..ddf61283 100644
--- a/protractor.conf.js
+++ b/protractor.conf.js
@@ -50,18 +50,21 @@ if (travis) {
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
build: process.env.TRAVIS_BUILD_NUMBER,
name: 'Redoc Safari Latest/OSX build ' + process.env.TRAVIS_BUILD_NUMBER,
- idleTimeout: 180
+ idleTimeout: 180,
+ maxDuration: 1800*2
},{
browserName: 'firefox',
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
build: process.env.TRAVIS_BUILD_NUMBER,
- name: 'Redoc Firefox Latest/Win build ' + process.env.TRAVIS_BUILD_NUMBER
+ name: 'Redoc Firefox Latest/Win build ' + process.env.TRAVIS_BUILD_NUMBER,
+ maxDuration: 1800*2
},{
browserName: 'internet explorer',
version: '11.0',
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
build: process.env.TRAVIS_BUILD_NUMBER,
- name: 'Redoc IE11/Win build ' + process.env.TRAVIS_BUILD_NUMBER
+ name: 'Redoc IE11/Win build ' + process.env.TRAVIS_BUILD_NUMBER,
+ maxDuration: 1800*2
}];
} else {
config.directConnect = true;