Merge branch 'master' into releases

This commit is contained in:
Roman Hotsiy 2016-02-22 15:27:17 +02:00
commit e3025b0d06
48 changed files with 583 additions and 355 deletions

View File

@ -9,7 +9,8 @@
"globals": { "globals": {
"should": true, "should": true,
"expect": true, "expect": true,
"sinon": true "sinon": true,
"Reflect": true
}, },
"rules": { "rules": {
"quotes": [2, "single"], "quotes": [2, "single"],

View File

@ -29,6 +29,8 @@ cache:
before_install: before_install:
- travis_retry npm install jspm - travis_retry npm install jspm
- jspm config registries.github.auth $JSPM_GITHUB_AUTH_TOKEN - jspm config registries.github.auth $JSPM_GITHUB_AUTH_TOKEN
before_script:
- npm run jspm-install
before_deploy: before_deploy:
- npm run build-dist - npm run build-dist
deploy: deploy:
@ -38,12 +40,6 @@ deploy:
on: on:
branch: master branch: master
condition: $JOB != e2e condition: $JOB != e2e
- skip_cleanup: true
provider: script
script: npm run branch-release
on:
branch: master
condition: $JOB != e2e
- provider: npm - provider: npm
skip_cleanup: true skip_cleanup: true
email: gotsijroman@gmail.com email: gotsijroman@gmail.com
@ -51,3 +47,9 @@ deploy:
secure: PuhWLERrCEFmXmdFpw2OVFlqpOIVDmgwk5JUJOYaFdVCh/smp0+jZCQ4vrdFpuG96rnDVirD+A8xvW6NgsNNaRthLgOB/LRdFN69rU6Gvn3At6wlnC55t5dlhxPvCfnzJcHVBLXX4EmMkjnZqDg2uczXTzPodr3FnQJNuXmP8B33fzDVLyHccvXZ90abwXWVrgRIXPU28niqCR8DOC2OTzs7wqz+BLNkYDRRbyYXsg62HWuD33x5iof5IqBmhzBt3usCGmF3QGcgHrXHdZw3sZnit8+Bua++3KrXR0x6HGXXN1AoXVmCAkCa5OTQ5R3tCRxiJN3P2KLnvWeZR74sTFkovJB/6pGCvbJ/c7Wnuw6sD7SgOUBD359ULB6lAf5OnxBLoNebX4JxxVXF+zA4E3Bl44VxkzDpPWc15xqBPMB5vBREzMVmJ5mExn2s5cmLQjADbl9h0y6gZnhnNJ+iTmqtrVyM0ZkF2rPrzrTdGD+ULmRIlTMkdD1bh+/TJ3RdXT3P4/zNUJmiNnvgnnJVYYvsGaXWF+7uCVHT/8k2RsoSHqgkqh0gkDqGSwVix55y5mC7T2Vk9lMBhm6MvFJXaonOX0kxJS4EDQ3plPd6/ybG+TLhwggYnQ8o9msU5Nt6FpUShKiezjKurIhbQZdwlVivX3tahjW2QjNDO58xGgY= secure: PuhWLERrCEFmXmdFpw2OVFlqpOIVDmgwk5JUJOYaFdVCh/smp0+jZCQ4vrdFpuG96rnDVirD+A8xvW6NgsNNaRthLgOB/LRdFN69rU6Gvn3At6wlnC55t5dlhxPvCfnzJcHVBLXX4EmMkjnZqDg2uczXTzPodr3FnQJNuXmP8B33fzDVLyHccvXZ90abwXWVrgRIXPU28niqCR8DOC2OTzs7wqz+BLNkYDRRbyYXsg62HWuD33x5iof5IqBmhzBt3usCGmF3QGcgHrXHdZw3sZnit8+Bua++3KrXR0x6HGXXN1AoXVmCAkCa5OTQ5R3tCRxiJN3P2KLnvWeZR74sTFkovJB/6pGCvbJ/c7Wnuw6sD7SgOUBD359ULB6lAf5OnxBLoNebX4JxxVXF+zA4E3Bl44VxkzDpPWc15xqBPMB5vBREzMVmJ5mExn2s5cmLQjADbl9h0y6gZnhnNJ+iTmqtrVyM0ZkF2rPrzrTdGD+ULmRIlTMkdD1bh+/TJ3RdXT3P4/zNUJmiNnvgnnJVYYvsGaXWF+7uCVHT/8k2RsoSHqgkqh0gkDqGSwVix55y5mC7T2Vk9lMBhm6MvFJXaonOX0kxJS4EDQ3plPd6/ybG+TLhwggYnQ8o9msU5Nt6FpUShKiezjKurIhbQZdwlVivX3tahjW2QjNDO58xGgY=
on: on:
tags: true tags: true
- skip_cleanup: true
provider: script
script: npm run branch-release
on:
branch: master
condition: $JOB != e2e

View File

@ -1,5 +1,5 @@
# ReDoc # ReDoc
[![Build Status](https://travis-ci.org/Rebilly/ReDoc.svg?branch=master)](https://travis-ci.org/Rebilly/ReDoc) [![Coverage Status](https://coveralls.io/repos/Rebilly/ReDoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Rebilly/ReDoc?branch=master) [![Code Climate](https://codeclimate.com/github/Rebilly/ReDoc/badges/gpa.svg)](https://codeclimate.com/github/Rebilly/ReDoc) [![David](https://david-dm.org/Rebilly/ReDoc/dev-status.svg)](https://david-dm.org/Rebilly/ReDoc#info=devDependencies) [![Build Status](https://travis-ci.org/Rebilly/ReDoc.svg?branch=master)](https://travis-ci.org/Rebilly/ReDoc) [![Coverage Status](https://coveralls.io/repos/Rebilly/ReDoc/badge.svg?branch=master&service=github)](https://coveralls.io/github/Rebilly/ReDoc?branch=master) [![Code Climate](https://codeclimate.com/github/Rebilly/ReDoc/badges/gpa.svg)](https://codeclimate.com/github/Rebilly/ReDoc) [![David](https://david-dm.org/Rebilly/ReDoc/dev-status.svg)](https://david-dm.org/Rebilly/ReDoc#info=devDependencies) [![Stories in Ready](https://badge.waffle.io/Rebilly/ReDoc.png?label=ready&title=Ready)](https://waffle.io/Rebilly/ReDoc)
[![npm](http://img.shields.io/npm/v/redoc.svg)](https://www.npmjs.com/package/swagger-parser) [![Bower](http://img.shields.io/bower/v/redoc.svg)](http://bower.io/) [![License](https://img.shields.io/npm/l/redoc.svg)](https://github.com/Rebilly/ReDoc/blob/master/LICENSE) [![npm](http://img.shields.io/npm/v/redoc.svg)](https://www.npmjs.com/package/swagger-parser) [![Bower](http://img.shields.io/bower/v/redoc.svg)](http://bower.io/) [![License](https://img.shields.io/npm/l/redoc.svg)](https://github.com/Rebilly/ReDoc/blob/master/LICENSE)

View File

@ -42,12 +42,14 @@ gulp.task('inlineTemplates', ['sass'], function() {
}); });
var JS_DEV_DEPS = [ var JS_DEV_DEPS = [
'lib/utils/browser-update.js',
'node_modules/zone.js/dist/zone-microtask.js', 'node_modules/zone.js/dist/zone-microtask.js',
'node_modules/reflect-metadata/Reflect.js', 'node_modules/reflect-metadata/Reflect.js',
'node_modules/babel-polyfill/dist/polyfill.js' 'node_modules/babel-polyfill/dist/polyfill.js'
]; ];
var JS_DEV_DEPS_MIN = [ var JS_DEV_DEPS_MIN = [
'lib/utils/browser-update.js',
'node_modules/zone.js/dist/zone-microtask.min.js', 'node_modules/zone.js/dist/zone-microtask.min.js',
'node_modules/reflect-metadata/Reflect.js', 'node_modules/reflect-metadata/Reflect.js',
'node_modules/babel-polyfill/dist/polyfill.min.js' 'node_modules/babel-polyfill/dist/polyfill.min.js'
@ -91,7 +93,7 @@ function bundle(outputFile, minify, cb) {
builder builder
.buildStatic(path.join(paths.tmp, paths.sourceEntryPoint), .buildStatic(path.join(paths.tmp, paths.sourceEntryPoint),
outputFile, outputFile,
{ format:'umd', sourceMaps: true, lowResSourceMaps: true, minify: minify } { format:'umd', sourceMaps: true, mangle: false, lowResSourceMaps: true, minify: minify }
) )
.then(function() { .then(function() {
cb(); cb();

View File

@ -17,7 +17,7 @@
<redoc scroll-y-offset="body > nav" spec-url='swagger.yml'></redoc> <redoc scroll-y-offset="body > nav" spec-url='swagger.yml'></redoc>
<!-- ReDoc built file with all dependencies included --> <!-- ReDoc built file with all dependencies included -->
<script src="dist/redoc.js"> </script>
<script src="main.js"> </script> <script src="main.js"> </script>
<script src="dist/redoc.js"> </script>
</body> </body>
</html> </html>

View File

@ -8,4 +8,6 @@
Redoc.init(schemaUrlInput.value); Redoc.init(schemaUrlInput.value);
return false; return false;
}) })
//window.redocDebugMode = true;
})(); })();

View File

@ -49,18 +49,19 @@ module.exports = function (config) {
'node_modules/zone.js/dist/long-stack-trace-zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/jasmine-patch.js', 'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/babel-polyfill/dist/polyfill.js', 'node_modules/babel-polyfill/dist/polyfill.js',
'node_modules/reflect-metadata/Reflect.js' './node_modules/reflect-metadata/Reflect.js'
], ],
jspm: { jspm: {
config: 'system.config.js', config: 'system.config.js',
loadFiles: ['tests/unit/*.spec.js', 'tests/helpers.js', 'lib/**/*.js'], loadFiles: ['tests/setup.js', 'tests/helpers.js', 'tests/unit/*.spec.js', 'lib/**/*.js'],
serveFiles: ['tests/schemas/**/*.json','tests/schemas/**/*.yml', 'lib/**/*.html', '.tmp/lib/**/*.css'], serveFiles: ['tests/schemas/**/*.json','tests/schemas/**/*.yml', 'lib/**/*.html', '.tmp/lib/**/*.css'],
nocache: true nocache: true
}, },
proxies: { proxies: {
'/tests/': '/base/tests/', '/tests/': '/base/tests/',
'/lib/components/Redoc/redoc-loading-styles.css': '/base/.tmp/lib/components/Redoc/redoc-loading-styles.css',
'/lib/': '/base/lib/', '/lib/': '/base/lib/',
'/jspm_packages/': '/base/jspm_packages/', '/jspm_packages/': '/base/jspm_packages/',
'/node_modules/': '/base/node_modules/', '/node_modules/': '/base/node_modules/',

View File

@ -7,16 +7,17 @@ import {BrowserDomAdapter} from 'angular2/platform/browser';
selector: '[sticky-sidebar]', selector: '[sticky-sidebar]',
inputs: ['scrollParent', 'scrollYOffset'] inputs: ['scrollParent', 'scrollYOffset']
}) })
@Reflect.metadata('parameters', [[ElementRef], [BrowserDomAdapter]])
export default class StickySidebar { export default class StickySidebar {
constructor(elementRef, dom) { constructor(elementRef, dom) {
this.element = elementRef.nativeElement; this.$element = elementRef.nativeElement;
this.dom = dom; this.dom = dom;
// initial styling // initial styling
this.dom.setStyle(this.element, 'position', 'absolute'); this.dom.setStyle(this.$element, 'position', 'absolute');
this.dom.setStyle(this.element, 'top', '0'); this.dom.setStyle(this.$element, 'top', '0');
this.dom.setStyle(this.element, 'bottom', '0'); this.dom.setStyle(this.$element, 'bottom', '0');
this.dom.setStyle(this.element, 'max-height', '100%'); this.dom.setStyle(this.$element, 'max-height', '100%');
} }
bind() { bind() {
@ -29,7 +30,7 @@ export default class StickySidebar {
} }
updatePosition() { updatePosition() {
if ( this.scrollY + this.scrollYOffset() >= this.redocEl.offsetTop) { if ( this.scrollY + this.scrollYOffset() >= this.$redocEl.offsetTop) {
this.stick(); this.stick();
} else { } else {
this.unstick(); this.unstick();
@ -37,13 +38,13 @@ export default class StickySidebar {
} }
stick() { stick() {
this.dom.setStyle(this.element, 'position', 'fixed'); this.dom.setStyle(this.$element, 'position', 'fixed');
this.dom.setStyle(this.element, 'top', this.scrollYOffset() + 'px'); this.dom.setStyle(this.$element, 'top', this.scrollYOffset() + 'px');
} }
unstick() { unstick() {
this.dom.setStyle(this.element, 'position', 'absolute'); this.dom.setStyle(this.$element, 'position', 'absolute');
this.dom.setStyle(this.element, 'top', 0); this.dom.setStyle(this.$element, 'top', 0);
} }
get scrollY() { get scrollY() {
@ -51,7 +52,8 @@ export default class StickySidebar {
} }
ngOnInit() { ngOnInit() {
this.redocEl = this.element.offsetParent; // FIXME use more reliable code
this.$redocEl = this.$element.offsetParent;
this.bind(); this.bind();
} }
@ -59,5 +61,3 @@ export default class StickySidebar {
this.unbind(); this.unbind();
} }
} }
StickySidebar.parameters = [ [ElementRef], [BrowserDomAdapter] ];

View File

@ -65,16 +65,25 @@ export class Tabs {
}) })
@View({ @View({
template: ` template: `
<div class="tab-wrap" [hidden]="!active"> <div class="tab-wrap" [ngClass]="{ 'active': active }">
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>
` `,
directives: [CORE_DIRECTIVES],
styles: [`
.tab-wrap {
display: none;
}
.tab-wrap.active {
display: block;
}`
]
}) })
@Reflect.metadata('parameters', [ [Tabs] ])
export class Tab { export class Tab {
constructor(tabs) { constructor(tabs) {
this.active = false; this.active = false;
tabs.addTab(this); tabs.addTab(this);
} }
} }
Tab.parameters = [ [ Tabs ] ];

View File

@ -64,6 +64,7 @@ span.zippy-indicator {
} }
.zippy-hidden { .zippy-hidden {
overflow: hidden;
visibility: hidden; visibility: hidden;
height: 0; height: 0;
padding: 0; padding: 0;

View File

@ -56,7 +56,6 @@ describe('Redoc components', () => {
@Component({selector: 'test-app'}) @Component({selector: 'test-app'})
@View({ @View({
directives: [ApiInfo], directives: [ApiInfo],
providers: [SchemaManager],
template: template:
`<api-info></api-info>` `<api-info></api-info>`
}) })

View File

@ -7,7 +7,7 @@ import {RedocComponent, BaseComponent} from '../base';
styleUrls: ['./lib/components/ApiLogo/api-logo.css'], styleUrls: ['./lib/components/ApiLogo/api-logo.css'],
templateUrl: './lib/components/ApiLogo/api-logo.html' templateUrl: './lib/components/ApiLogo/api-logo.html'
}) })
export default class ApiInfo extends BaseComponent { export default class ApiLogo extends BaseComponent {
constructor(schemaMgr) { constructor(schemaMgr) {
super(schemaMgr); super(schemaMgr);
} }

View File

@ -30,8 +30,11 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
} }
.param-name { .param-name {
flex-grow: 0;
-ms-flex-grow: 0;
display: inline-block;
font-size: 14px; font-size: 14px;
padding: $cell-padding $cell-spacing $cell-padding 0; padding: $cell-padding 0 $cell-padding 0;
font-weight: bold; font-weight: bold;
box-sizing: border-box; box-sizing: border-box;
line-height: $param-name-height; line-height: $param-name-height;
@ -40,15 +43,24 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
position: relative; position: relative;
} }
.param-name-content {
padding-right: $cell-spacing;
display: inline-block;
}
.param-info { .param-info {
width: 100%; //width: 100%;
flex-grow: 1;
-ms-flex-grow: 1;
padding: $cell-padding 0; padding: $cell-padding 0;
box-sizing: border-box; box-sizing: border-box;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
display: inline-block;
} }
.param { .param {
display: flex; display: flex;
display: -ms-flexbox;
} }
.param-required { .param-required {
@ -72,11 +84,11 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
color: #999; color: #999;
} }
.param-type.string { .param-type.string, .enum-value.string {
color: rgba(0, 80, 0, 0.7); color: rgba(0, 80, 0, 0.7);
} }
.param-type.integer, .param-type.number { .param-type.integer, .param-type.number, .enum-value.number {
color: rgba(74, 139, 179, 0.8); color: rgba(74, 139, 179, 0.8);
} }
@ -84,7 +96,7 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
color: rgba(0, 50, 159, 0.7); color: rgba(0, 50, 159, 0.7);
} }
.param-type.boolean { .param-type.boolean, .enum-value.boolean {
color: firebrick; color: firebrick;
} }
@ -158,3 +170,25 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin;
.param-schema .param-wrap:first-of-type .param-name:before { .param-schema .param-wrap:first-of-type .param-name:before {
display: none !important; display: none !important;
} }
.param-enum {
color: #666;
&:before {
content: "Values: {"
}
&:after {
content: "}"
}
> .enum-value {
&:after {
content: ", ";
}
&:last-of-type:after {
content: none;
}
}
}

View File

@ -2,27 +2,41 @@
import {Component, View, ElementRef} from 'angular2/core'; import {Component, View, ElementRef} from 'angular2/core';
import {CORE_DIRECTIVES} from 'angular2/common'; import {CORE_DIRECTIVES} from 'angular2/common';
import JsonSchema from './json-schema';
import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader'; import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader';
import JsonSchema from './json-schema';
import OptionsManager from '../../options'; import OptionsManager from '../../options';
import SchemaManager from '../../utils/SchemaManager';
var cache = {};
@Component({ @Component({
selector: 'json-schema-lazy', selector: 'json-schema-lazy',
inputs: ['pointer'] inputs: ['pointer', 'auto']
}) })
@View({ @View({
template: '', template: '',
directives: [CORE_DIRECTIVES] directives: [CORE_DIRECTIVES]
}) })
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef], [DynamicComponentLoader], [OptionsManager]])
export default class JsonSchemaLazy { export default class JsonSchemaLazy {
constructor(elementRef, dcl) { constructor(schemaMgr, elementRef, dcl, optionsMgr) {
this.elementRef = elementRef; this.elementRef = elementRef;
this.dcl = dcl; this.dcl = dcl;
this.optionsMgr = optionsMgr;
this.schemaMgr = schemaMgr;
}
normalizePointer() {
let schema = this.schemaMgr.byPointer(this.pointer);
return schema && schema.$ref || this.pointer;
} }
load() { load() {
if (OptionsManager.instance().options.disableLazySchemas) return; if (this.optionsMgr.options.disableLazySchemas) return;
if (this.loaded) return; if (this.loaded) return;
if (this.pointer) { if (this.pointer) {
this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => { this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => {
@ -31,5 +45,46 @@ export default class JsonSchemaLazy {
} }
this.loaded = true; this.loaded = true;
} }
// cache JsonSchema view
loadCached() {
this.pointer = this.normalizePointer(this.pointer);
if (cache[this.pointer]) {
cache[this.pointer].then((compRef) => {
setTimeout( ()=> {
let $element = compRef.location.nativeElement;
// skip caching view with tabs inside (discriminator) as it needs attached controller
if ($element.querySelector('tabs')) {
this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => {
compRef.instance.pointer = this.pointer;
compRef.hostView.changeDetectorRef.markForCheck();
});
return;
}
insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
} );
});
} else {
cache[this.pointer] = this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => {
compRef.instance.pointer = this.pointer;
compRef.hostView.changeDetectorRef.markForCheck();
return compRef;
});
}
}
ngAfterViewInit() {
if (!this.auto) return;
this.loadCached();
}
ngOnDestroy() {
// clear cache
cache = {};
}
}
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
} }
JsonSchemaLazy.parameters = [[ElementRef], [DynamicComponentLoader]];

View File

@ -3,6 +3,7 @@
import { getChildDebugElement } from 'tests/helpers'; import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core'; import {Component, View, provide} from 'angular2/core';
import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader'; import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import { import {
TestComponentBuilder, TestComponentBuilder,
@ -14,6 +15,7 @@ import {
import JsonSchemaLazy from 'lib/components/JsonSchema/json-schema-lazy'; import JsonSchemaLazy from 'lib/components/JsonSchema/json-schema-lazy';
import SchemaManager from 'lib/utils/SchemaManager'; import SchemaManager from 'lib/utils/SchemaManager';
import OptionsManager from 'lib/options';
describe('Redoc components', () => { describe('Redoc components', () => {
describe('JsonSchemaLazy Component', () => { describe('JsonSchemaLazy Component', () => {
@ -26,7 +28,9 @@ describe('Redoc components', () => {
instance: {} instance: {}
}; };
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: schemaMgr}) provide(SchemaManager, {useValue: schemaMgr}),
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter}),
provide(OptionsManager, {useClass: OptionsManager})
]); ]);
beforeEach(inject([TestComponentBuilder, DynamicComponentLoader], (tcb, dcl) => { beforeEach(inject([TestComponentBuilder, DynamicComponentLoader], (tcb, dcl) => {
builder = tcb; builder = tcb;
@ -79,7 +83,6 @@ describe('Redoc components', () => {
@Component({selector: 'test-app'}) @Component({selector: 'test-app'})
@View({ @View({
directives: [JsonSchemaLazy], directives: [JsonSchemaLazy],
providers: [SchemaManager, DynamicComponentLoader],
template: template:
`<json-schema-lazy></json-schema-lazy>` `<json-schema-lazy></json-schema-lazy>`
}) })

View File

@ -6,13 +6,16 @@
<div *ngFor="#prop of data.properties" class="param-wrap"> <div *ngFor="#prop of data.properties" class="param-wrap">
<div class="param" [ngClass]="{'discriminator': prop.isDiscriminator}"> <div class="param" [ngClass]="{'discriminator': prop.isDiscriminator}">
<div class="param-name"> <div class="param-name">
<span>{{prop._name}}</span> <span class="param-name-content">{{prop._name}}</span>
</div> </div>
<div class="param-info"> <div class="param-info">
<div> <div>
<span class="param-type {{prop.type}}" [ngClass]="{'with-hint': prop._displayTypeHint}" <span class="param-type {{prop.type}}" [ngClass]="{'with-hint': prop._displayTypeHint}"
title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}</span> title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}</span>
<span *ngIf="prop.isRequired" class="param-required">Required</span> <span *ngIf="prop.required" class="param-required">Required</span>
<div *ngIf="prop.enum" class="param-enum">
<span *ngFor="#enumItem of prop.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
</div>
</div> </div>
<div class="param-description" innerHtml="{{prop.description | marked}}"></div> <div class="param-description" innerHtml="{{prop.description | marked}}"></div>
<div class="discriminator-info" *ngIf="prop.isDiscriminator"> This field value determines the exact schema: </div> <div class="discriminator-info" *ngIf="prop.isDiscriminator"> This field value determines the exact schema: </div>

View File

@ -1,8 +1,9 @@
'use strict'; 'use strict';
import {RedocComponent, BaseComponent} from '../base';
import {Tabs, Tab} from '../../common/components/Tabs/tabs';
import {ElementRef} from 'angular2/core'; import {ElementRef} from 'angular2/core';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import {Tabs, Tab} from '../../common/components/Tabs/tabs';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
@RedocComponent({ @RedocComponent({
@ -12,10 +13,11 @@ import JsonPointer from '../../utils/JsonPointer';
directives: [JsonSchema, Tabs, Tab], directives: [JsonSchema, Tabs, Tab],
inputs: ['isArray', 'final'] inputs: ['isArray', 'final']
}) })
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef]])
export default class JsonSchema extends BaseComponent { export default class JsonSchema extends BaseComponent {
constructor(schemaMgr, elementRef) { constructor(schemaMgr, elementRef) {
super(schemaMgr); super(schemaMgr);
this.element = elementRef.nativeElement; this.$element = elementRef.nativeElement;
this.final = false; this.final = false;
} }
@ -71,7 +73,8 @@ export default class JsonSchema extends BaseComponent {
let discriminatorFieldIdx = -1; let discriminatorFieldIdx = -1;
let props = Object.keys(schema.properties).map((prop, idx) => { let props = Object.keys(schema.properties).map((prop, idx) => {
let propData = schema.properties[prop]; let propData = schema.properties[prop];
propData = this.injectPropData(prop, propData, schema); let propPointer = JsonPointer.join(this.pointer, ['properties', prop]);
propData = JsonSchema.injectPropData(propData, prop, propPointer, this.requiredMap, schema);
if (propData.isDiscriminator) discriminatorFieldIdx = idx; if (propData.isDiscriminator) discriminatorFieldIdx = idx;
return propData; return propData;
}); });
@ -83,32 +86,23 @@ export default class JsonSchema extends BaseComponent {
this.data.properties = props; this.data.properties = props;
} }
adjustNameColumnWidth() { static injectPropData(propData, propName, propPointer, requiredMap, schema) {
// TODO handle internal schemes differently let propEnum;
let names = [].slice.call(this.element.querySelectorAll('.param-name'));
let widths = names.map(el => el.offsetWidth);
let maxWidth = Math.max(...widths);
if (!maxWidth) return;
names.forEach(el => {
el.style.minWidth = maxWidth + 'px';
});
let discrValues = this.element.querySelector('tabs ul');
if (discrValues) discrValues.style.paddingLeft = maxWidth + 'px';
}
injectPropData(prop, propData, schema) {
propData = Object.assign({}, propData); propData = Object.assign({}, propData);
propData._name = prop; propData._name = propName;
propData.isRequired = this.requiredMap[prop]; propData.required = propData.required || (requiredMap && requiredMap[propName]);
propData._displayType = propData.type; propData._displayType = propData.type;
propData.isDiscriminator = (schema.discriminator === prop); propData.isDiscriminator = (schema && schema.discriminator === propName);
propEnum = propData.enum;
if (propData.type === 'array') { if (propData.type === 'array') {
let itemType = propData.items.type; let itemType = propData.items.type;
let itemFormat = propData.items.format; let itemFormat = propData.items.format;
propEnum = propData.items.enum;
if (itemType === 'object' || !itemType) { if (itemType === 'object' || !itemType) {
itemType = propData.items.title || 'object'; itemType = propData.items.title || 'object';
propData._pointer = propData.items._pointer || JsonPointer.join(this.pointer, ['properties', prop, 'items']); propData._pointer = propData.items._pointer
|| JsonPointer.join(propPointer, ['items']);
} }
propData._displayType = `${itemType}`; propData._displayType = `${itemType}`;
propData.format = itemFormat; propData.format = itemFormat;
@ -126,12 +120,38 @@ export default class JsonSchema extends BaseComponent {
} }
if (propData.format) propData._displayFormat = `<${propData.format}>`; if (propData.format) propData._displayFormat = `<${propData.format}>`;
if (propEnum) {
propData.enum = propEnum.map((value) => {
return {val: value, type: typeof value};
});
}
return propData; return propData;
} }
init() { ngAfterViewInit() {
setTimeout(() => this.adjustNameColumnWidth()); // adjust widht only on parent level
let $el = this.$element.parentElement;
while($el && $el.tagName !== 'JSON-SCHEMA' && $el.tagName !== 'REDOC') {
$el = $el.parentElement;
}
if ($el && $el.tagName === 'REDOC' ) {
this.adjustNameColumnWidth();
}
}
adjustNameColumnWidth() {
// TODO handle internal schemes differently
let names = [].slice.call(this.$element.querySelectorAll('.param-name-content'));
let widths = [144];//names.map(el => el.offsetWidth);
let maxWidth = Math.max(...widths);
if (!maxWidth) return;
names.forEach(el => {
el.parentNode.style.minWidth = maxWidth + 'px';
});
let discrValues = this.$element.querySelector('tabs ul');
if (discrValues) discrValues.style.paddingLeft = maxWidth + 'px';
} }
} }
JsonSchema.parameters = JsonSchema.parameters.concat([[ElementRef]]);

View File

@ -3,6 +3,10 @@
/* styles for array-schema for array */ /* styles for array-schema for array */
$array-marker-font-sz: 12px; $array-marker-font-sz: 12px;
$array-marker-line-height: 1.5; $array-marker-line-height: 1.5;
:host {
display: block;
}
.params-wrap.params-array:before, .params-wrap.params-array:after { .params-wrap.params-array:before, .params-wrap.params-array:after {
display: block; display: block;
font-weight: bold; font-weight: bold;

View File

@ -2,6 +2,7 @@
import { getChildDebugElement } from 'tests/helpers'; import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core'; import {Component, View, provide} from 'angular2/core';
import OptionsManager from 'lib/options';
import { import {
TestComponentBuilder, TestComponentBuilder,
@ -21,7 +22,8 @@ describe('Redoc components', () => {
let schemaMgr = new SchemaManager(); let schemaMgr = new SchemaManager();
let fixture; let fixture;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: schemaMgr}) provide(SchemaManager, {useValue: schemaMgr}),
provide(OptionsManager, {useClass: OptionsManager})
]); ]);
beforeEach(inject([TestComponentBuilder], (tcb) => { beforeEach(inject([TestComponentBuilder], (tcb) => {
builder = tcb; builder = tcb;

View File

@ -1,7 +1,7 @@
<div class="method"> <div class="method">
<div class="method-content"> <div class="method-content">
<h2 class="method-header sharable-header"> <h2 class="method-header sharable-header">
<a class="share-link" href="#{{tag}}{{pointer}}"></a>{{data.methodInfo.summary}} <a class="share-link" href="#{{data.methodAnchor}}"></a>{{data.methodInfo.summary}}
</h2> </h2>
<h3 class="method-endpoint"> <h3 class="method-endpoint">
<span class="http-method" [ngClass]="data.httpMethod">{{data.httpMethod}}</span> <span class="http-method" [ngClass]="data.httpMethod">{{data.httpMethod}}</span>

View File

@ -2,6 +2,7 @@
import {JsonPointer} from '../../utils/JsonPointer'; import {JsonPointer} from '../../utils/JsonPointer';
import {RedocComponent, BaseComponent} from '../base'; import {RedocComponent, BaseComponent} from '../base';
import ParamsList from '../ParamsList/params-list'; import ParamsList from '../ParamsList/params-list';
import ResponsesList from '../ResponsesList/responses-list'; import ResponsesList from '../ResponsesList/responses-list';
import ResponsesSamples from '../ResponsesSamples/responses-samples'; import ResponsesSamples from '../ResponsesSamples/responses-samples';
@ -28,6 +29,11 @@ export default class Method extends BaseComponent {
this.data.methodInfo = this.componentSchema; this.data.methodInfo = this.componentSchema;
this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags); this.data.methodInfo.tags = this.filterMainTags(this.data.methodInfo.tags);
this.data.bodyParam = this.findBodyParam(); this.data.bodyParam = this.findBodyParam();
if (this.componentSchema.operationId) {
this.data.methodAnchor = 'operation/' + this.componentSchema.operationId;
} else {
this.data.methodAnchor = 'tag/' + this.tag + this.pointer;
}
} }
filterMainTags(tags) { filterMainTags(tags) {

View File

@ -2,6 +2,7 @@
import { getChildDebugElement } from 'tests/helpers'; import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core'; import {Component, View, provide} from 'angular2/core';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import { import {
TestComponentBuilder, TestComponentBuilder,
@ -13,13 +14,16 @@ import {
import Method from 'lib/components/Method/method'; import Method from 'lib/components/Method/method';
import SchemaManager from 'lib/utils/SchemaManager'; import SchemaManager from 'lib/utils/SchemaManager';
import OptionsManager from 'lib/options';
describe('Redoc components', () => { describe('Redoc components', () => {
describe('Method Component', () => { describe('Method Component', () => {
let builder; let builder;
let component; let component;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}) provide(SchemaManager, {useValue: new SchemaManager()}),
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter}),
provide(OptionsManager, {useClass: OptionsManager})
]); ]);
beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => { beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
builder = tcb; builder = tcb;

View File

@ -1,10 +1,10 @@
<div class="methods"> <div class="methods">
<div class="tag" *ngFor="#tag of data.tags"> <div class="tag" *ngFor="#tag of data.tags">
<div class="tag-info" [attr.tag]="tag.name"> <div class="tag-info" [attr.tag]="tag.name">
<h1 class="sharable-header"> <a class="share-link" href="#{{tag.name}}"></a>{{tag.name}} </h1> <h1 class="sharable-header"> <a class="share-link" href="#tag/{{tag.name}}"></a>{{tag.name}} </h1>
<p *ngIf="tag.description" innerHtml="{{ tag.description | marked }}"> </p> <p *ngIf="tag.description" innerHtml="{{ tag.description | marked }}"> </p>
</div> </div>
<method *ngFor="#method of tag.methods" [pointer]="method.pointer" [attr.pointer]="method.pointer" <method *ngFor="#method of tag.methods" [pointer]="method.pointer" [attr.pointer]="method.pointer"
[attr.tag]="method.tag" [tag]="method.tag"></method> [attr.tag]="method.tag" [tag]="method.tag" [attr.operation-id]="method.operationId"></method>
</div> </div>
</div> </div>

View File

@ -2,6 +2,8 @@
import { getChildDebugElement } from 'tests/helpers'; import { getChildDebugElement } from 'tests/helpers';
import {Component, View, provide} from 'angular2/core'; import {Component, View, provide} from 'angular2/core';
import OptionsManager from 'lib/options';
import {BrowserDomAdapter} from 'angular2/platform/browser';
import { import {
TestComponentBuilder, TestComponentBuilder,
@ -15,12 +17,14 @@ import MethodsList from 'lib/components/MethodsList/methods-list';
import SchemaManager from 'lib/utils/SchemaManager'; import SchemaManager from 'lib/utils/SchemaManager';
describe('Redoc components', () => { describe('Redoc components', () => {
describe('ApiInfo Component', () => { describe('MethodsList Component', () => {
let builder; let builder;
let component; let component;
let fixture; let fixture;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}) provide(SchemaManager, {useValue: new SchemaManager()}),
provide(OptionsManager, {useClass: OptionsManager}),
provide(BrowserDomAdapter, {useClass: BrowserDomAdapter})
]); ]);
beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => { beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
builder = tcb; builder = tcb;
@ -32,7 +36,7 @@ describe('Redoc components', () => {
component = getChildDebugElement(fixture.debugElement, 'methods-list').componentInstance; component = getChildDebugElement(fixture.debugElement, 'methods-list').componentInstance;
fixture.detectChanges(); fixture.detectChanges();
done(); done();
}, err => { throw err; }); }, err => done.fail(err) );
}); });

View File

@ -2,12 +2,16 @@
<div class="params-wrap"> <div class="params-wrap">
<div *ngFor="#param of data.params" class="param"> <div *ngFor="#param of data.params" class="param">
<div class="param-name"> <div class="param-name">
<span> {{param.name}} </span> <span class="param-name-content"> {{param.name}} </span>
</div> </div>
<div class="param-info"> <div class="param-info">
<div> <div>
<span class="param-type" [ngClass]="param.type">{{param.type}}</span> <span class="param-type {{param.type}}" [ngClass]="{'with-hint': param._displayTypeHint}"
title="{{param._displayTypeHint}}"> {{param._displayType}} {{param._displayFormat}}</span>
<span *ngIf="param.required" class="param-required">Required</span> <span *ngIf="param.required" class="param-required">Required</span>
<div *ngIf="param.enum" class="param-enum">
<span *ngFor="#enumItem of param.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
</div>
</div> </div>
<div class="param-description" innerHtml="{{param.description | marked}}"></div> <div class="param-description" innerHtml="{{param.description | marked}}"></div>
</div> </div>
@ -19,7 +23,7 @@
<div class="body-param-description" innerHtml="{{data.bodyParam.description | marked}}"></div> <div class="body-param-description" innerHtml="{{data.bodyParam.description | marked}}"></div>
<div> <div>
<json-schema pointer="{{data.bodyParam.pointer}}/schema"> <json-schema-lazy [auto]="true" pointer="{{data.bodyParam.pointer}}/schema">
</json-schema> </json-schema-lazy>
</div> </div>
</div> </div>

View File

@ -2,12 +2,13 @@
import {RedocComponent, BaseComponent} from '../base'; import {RedocComponent, BaseComponent} from '../base';
import JsonSchema from '../JsonSchema/json-schema'; import JsonSchema from '../JsonSchema/json-schema';
import JsonSchemaLazy from '../JsonSchema/json-schema-lazy';
@RedocComponent({ @RedocComponent({
selector: 'params-list', selector: 'params-list',
templateUrl: './lib/components/ParamsList/params-list.html', templateUrl: './lib/components/ParamsList/params-list.html',
styleUrls: ['./lib/components/ParamsList/params-list.css'], styleUrls: ['./lib/components/ParamsList/params-list.css'],
directives: [JsonSchema] directives: [JsonSchema, JsonSchemaLazy]
}) })
export default class ParamsList extends BaseComponent { export default class ParamsList extends BaseComponent {
constructor(schemaMgr) { constructor(schemaMgr) {
@ -26,6 +27,11 @@ export default class ParamsList extends BaseComponent {
this.data.bodyParam = bodyParam; this.data.bodyParam = bodyParam;
} }
params = params.map((paramData) => {
let propPointer = paramData._pointer;
return JsonSchema.injectPropData(paramData, paramData.name, propPointer);
});
this.data.noParams = !(params.length || this.data.bodyParam); this.data.noParams = !(params.length || this.data.bodyParam);
this.data.params = params; this.data.params = params;
} }

View File

@ -28,23 +28,27 @@
display: table-row; display: table-row;
} }
.param:first-of-type .param-name:before { .param:last-of-type > .param-name {
border-left: 0;
&:after {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
left: -$lines-width; left: 0;
border-left: $line-border;
height: ($param-name-height/2) + $cell-padding + $lines-width;
background-color: white;
top: 0; top: 0;
border-left: $line-border-erase; }
height: ($param-name-height/2) + $cell-padding;
} }
.param:last-of-type .param-name:after { .param:first-of-type .param-name:after {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
left: -$lines-width; left: -$lines-width;
border-left: $line-border-erase; border-left: $line-border-erase;
top: ($param-name-height/2) + $cell-padding + $lines-width; height: ($param-name-height/2) + $cell-padding;
background-color: white; background-color: white;
bottom: 0; top: 0;
} }

View File

@ -0,0 +1,37 @@
redoc.loading {
position: relative;
display: block;
min-height:350px;
}
@keyframes move {
0% {transform: translateY(10px)}
25% {transform: translateY(0px)}
50% {transform: translateY(10px)}
75% {transform: translateY(20px)}
100% {transform: translateY(10px)}
}
redoc.loading:before {
content: "Loading...";
font-size: 28px;
text-align: center;
padding-top: 40px;
color: #3F5C9C;
font-weight: bold;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: white;
z-index: 9999;
opacity: 1;
transition: all 0.6s ease-out;
animation: 2s move linear infinite;
}
redoc.loading-remove:before {
opacity: 0;
}

View File

@ -1,5 +1,5 @@
<div class="redoc-wrap"> <div class="redoc-wrap">
<div class="menu-content" sticky-sidebar [scrollParent]="scrollParent" [scrollYOffset]="options.scrollYOffset"> <div class="menu-content" sticky-sidebar [scrollParent]="options.$scrollParent" [scrollYOffset]="options.scrollYOffset">
<api-logo> </api-logo> <api-logo> </api-logo>
<side-menu> </side-menu> <side-menu> </side-menu>
</div> </div>

View File

@ -1,5 +1,9 @@
'use strict'; 'use strict';
import {ChangeDetectionStrategy, provide, enableProdMode} from 'angular2/core';
import {ElementRef} from 'angular2/core';
import {BrowserDomAdapter, bootstrap} from 'angular2/platform/browser';
import detectScollParent from 'scrollparent';
import {RedocComponent, BaseComponent} from '../base'; import {RedocComponent, BaseComponent} from '../base';
import SchemaManager from '../../utils/SchemaManager'; import SchemaManager from '../../utils/SchemaManager';
@ -11,116 +15,35 @@ import StickySidebar from '../../common/components/StickySidebar/sticky-sidebar'
import OptionsManager from '../../options'; import OptionsManager from '../../options';
import {redocEvents} from '../../events'; import {redocEvents} from '../../events';
import {ChangeDetectionStrategy} from 'angular2/core'; import './redoc-loading-styles.css!css';
import {ElementRef} from 'angular2/core';
import {BrowserDomAdapter, bootstrap} from 'angular2/platform/browser';
import detectScollParent from 'scrollparent';
import {isFunction, isString} from 'angular2/src/facade/lang'; var dom = new BrowserDomAdapter();
var _modeLocked = false;
let optionNames = new Set(['scrollYOffset', 'disableLazySchemas']);
let dom = new BrowserDomAdapter();
@RedocComponent({ @RedocComponent({
selector: 'redoc', selector: 'redoc',
providers: [SchemaManager, BrowserDomAdapter, OptionsManager], providers: [
SchemaManager,
BrowserDomAdapter
],
templateUrl: './lib/components/Redoc/redoc.html', templateUrl: './lib/components/Redoc/redoc.html',
styleUrls: ['./lib/components/Redoc/redoc.css'], styleUrls: ['./lib/components/Redoc/redoc.css'],
directives: [ApiInfo, ApiLogo, MethodsList, SideMenu, StickySidebar], directives: [ApiInfo, ApiLogo, MethodsList, SideMenu, StickySidebar],
changeDetection: ChangeDetectionStrategy.Default changeDetection: ChangeDetectionStrategy.Default
}) })
@Reflect.metadata('parameters', [
[SchemaManager], [OptionsManager], [ElementRef]])
export default class Redoc extends BaseComponent { export default class Redoc extends BaseComponent {
constructor(schemaMgr, optionsMgr, elementRef, dom) { constructor(schemaMgr, optionsMgr, elementRef) {
super(schemaMgr); super(schemaMgr);
this.element = elementRef.nativeElement; this.element = elementRef.nativeElement;
this.dom = dom;
let el = this.element;
//parse options (top level component doesn't support inputs) //parse options (top level component doesn't support inputs)
this.scrollParent = detectScollParent(el); optionsMgr.parseOptions( this.element );
this.parseOptions(); optionsMgr.options.$scrollParent = detectScollParent( this.element );
this.options = Object.assign({}, optionsMgr.options, this.options); this.options = optionsMgr.options;
this.normalizeOptions();
optionsMgr.options = this.options;
}
parseOptions() {
let attributesMap = this.dom.attributeMap(this.element);
this.options = {};
Array.from(attributesMap.keys())
//camelCasify
.map(k => ({
attrName: k,
name: k.replace(/-(.)/g, (m, $1) => $1.toUpperCase())
})
)
.filter(option => optionNames.has(option.name))
.forEach(option => {
this.options[option.name] = attributesMap.get(option.attrName);
});
}
normalizeOptions() {
// modify scrollYOffset to always be a function
if (!isFunction(this.options.scrollYOffset)) {
if (isFinite(this.options.scrollYOffset)) {
// if number specified create function that returns this value
let numberOffset = parseFloat(this.options.scrollYOffset);
this.options.scrollYOffset = () => numberOffset;
} else {
// if selector or node function that returns bottom offset of this node
let el = this.options.scrollYOffset;
if (!(el instanceof Node)) {
el = this.dom.query(el);
}
if (!el) {
this.options.scrollYOffset = () => 0;
} else {
this.options.scrollYOffset = () => el.offsetTop + el.offsetHeight;
}
}
}
if (isString(this.options.disableLazySchemas)) this.options.disableLazySchemas = true;
} }
static showLoadingAnimation() { static showLoadingAnimation() {
if (!dom.query('#redoc-loading-style')) {
let animStyle = dom.createStyleElement(`
redoc.loading {
position: relative;
display: block;
min-height:350px;
}
redoc.loading:before {
content: "Loading...";
font-size: 28px;
text-align: center;
padding-top: 40px;
color: #3F5C9C;
font-weight: bold;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: white;
z-index: 9999;
opacity: 1;
transition: all 0.6s ease-out;
}
redoc.loading-remove:before {
opacity: 0;
}
`);
animStyle.id = 'redoc-loading-style';
dom.appendChild(dom.defaultDoc().head, animStyle);
}
let elem = dom.query('redoc'); let elem = dom.query('redoc');
dom.addClass(elem, 'loading'); dom.addClass(elem, 'loading');
} }
@ -135,20 +58,30 @@ export default class Redoc extends BaseComponent {
} }
static init(schemaUrl, options) { static init(schemaUrl, options) {
var optionsMgr = new OptionsManager();
optionsMgr.options = options;
var providers = [
provide(OptionsManager, {useValue: optionsMgr})
];
if (Redoc.appRef) { if (Redoc.appRef) {
Redoc.dispose(); Redoc.dispose();
} }
Redoc.showLoadingAnimation(); Redoc.showLoadingAnimation();
return SchemaManager.instance().load(schemaUrl) return SchemaManager.instance().load(schemaUrl)
.then(() => { .then(() => {
(new OptionsManager()).options = options; if (!_modeLocked && !optionsMgr.options.debugMode) {
return bootstrap(Redoc); enableProdMode();
_modeLocked = true;
}
return bootstrap(Redoc, providers);
}) })
.then( .then(
(appRef) => { (appRef) => {
Redoc.hideLoadingAnimation(); Redoc.hideLoadingAnimation();
Redoc.appRef = appRef; Redoc.appRef = appRef;
redocEvents.bootstrapped.next(); // setTimeout to allow cached elements to init
setTimeout(() => redocEvents.bootstrapped.next());
console.log('ReDoc bootstrapped!'); console.log('ReDoc bootstrapped!');
}, },
error => { error => {
@ -160,7 +93,6 @@ export default class Redoc extends BaseComponent {
static autoInit() { static autoInit() {
const specUrlAttributeName = 'spec-url'; const specUrlAttributeName = 'spec-url';
let dom = new BrowserDomAdapter();
let redocEl = dom.query('redoc'); let redocEl = dom.query('redoc');
if (!redocEl) return; if (!redocEl) return;
if (dom.hasAttribute(redocEl, specUrlAttributeName)) { if (dom.hasAttribute(redocEl, specUrlAttributeName)) {
@ -170,7 +102,6 @@ export default class Redoc extends BaseComponent {
} }
static dispose() { static dispose() {
let dom = new BrowserDomAdapter();
let el = dom.query('redoc'); let el = dom.query('redoc');
let elClone; let elClone;
let parent; let parent;
@ -192,8 +123,3 @@ export default class Redoc extends BaseComponent {
} }
} }
} }
Redoc.parameters = Redoc.parameters.concat([[OptionsManager], [ElementRef], [BrowserDomAdapter]]);
// TODO
// this doesn't work in side-menu.js because of some circular references issue
SideMenu.parameters = SideMenu.parameters.concat([[Redoc]]);

View File

@ -1,12 +1,12 @@
'use strict'; 'use strict';
import {RedocComponent, BaseComponent} from '../base'; import {ViewChildren, QueryList, ChangeDetectorRef, ChangeDetectionStrategy} from 'angular2/core';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
import {Tabs, Tab} from '../../common/components/Tabs/tabs'; import {Tabs, Tab} from '../../common/components/Tabs/tabs';
import SchemaSample from '../SchemaSample/schema-sample'; import SchemaSample from '../SchemaSample/schema-sample';
import {PrismPipe} from '../../utils/pipes'; import {PrismPipe} from '../../utils/pipes';
import {ViewChildren, QueryList, ChangeDetectorRef, ChangeDetectionStrategy} from 'angular2/core';
import {redocEvents} from '../../events'; import {redocEvents} from '../../events';
@RedocComponent({ @RedocComponent({
@ -18,6 +18,7 @@ import {redocEvents} from '../../events';
pipes: [PrismPipe], pipes: [PrismPipe],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
@Reflect.metadata('parameters', [[SchemaManager], [new ViewChildren(Tabs), QueryList], [ChangeDetectorRef]])
export default class RequestSamples extends BaseComponent { export default class RequestSamples extends BaseComponent {
constructor(schemaMgr, tabs, changeDetector) { constructor(schemaMgr, tabs, changeDetector) {
super(schemaMgr); super(schemaMgr);
@ -46,5 +47,3 @@ export default class RequestSamples extends BaseComponent {
this.data.samples = this.componentSchema['x-code-samples'] || []; this.data.samples = this.componentSchema['x-code-samples'] || [];
} }
} }
RequestSamples.parameters = RequestSamples.parameters.concat([ [new ViewChildren(Tabs), QueryList], [ChangeDetectorRef] ]);

View File

@ -14,7 +14,7 @@
<header> <header>
Response schema Response schema
</header> </header>
<json-schema *ngIf="response.schema && !enabledLazy" class="schema type" pointer="{{response.pointer}}/schema"> <json-schema *ngIf="response.schema && options.disableLazySchemas" class="schema type" pointer="{{response.pointer}}/schema">
</json-schema> </json-schema>
<json-schema-lazy #lazySchema pointer="{{response.schema ? response.pointer + '/schema' : null}}"> <json-schema-lazy #lazySchema pointer="{{response.schema ? response.pointer + '/schema' : null}}">
</json-schema-lazy> </json-schema-lazy>

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import {RedocComponent, BaseComponent} from '../base'; import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
import JsonSchema from '../JsonSchema/json-schema'; import JsonSchema from '../JsonSchema/json-schema';
import JsonSchemaLazy from '../JsonSchema/json-schema-lazy'; import JsonSchemaLazy from '../JsonSchema/json-schema-lazy';
@ -18,15 +18,16 @@ function isNumeric(n) {
styleUrls: ['./lib/components/ResponsesList/responses-list.css'], styleUrls: ['./lib/components/ResponsesList/responses-list.css'],
directives: [JsonSchema, Zippy, JsonSchemaLazy] directives: [JsonSchema, Zippy, JsonSchemaLazy]
}) })
@Reflect.metadata('parameters', [[SchemaManager], [OptionsManager]])
export default class ResponsesList extends BaseComponent { export default class ResponsesList extends BaseComponent {
constructor(schemaMgr) { constructor(schemaMgr, optionsMgr) {
super(schemaMgr); super(schemaMgr);
this.options = optionsMgr.options;
} }
prepareModel() { prepareModel() {
this.data = {}; this.data = {};
this.data.responses = []; this.data.responses = [];
this.enabledLazy = !OptionsManager.instance().options.disableLazySchemas;
let responses = this.componentSchema; let responses = this.componentSchema;
if (!responses) return; if (!responses) return;

View File

@ -1,11 +1,11 @@
'use strict'; 'use strict';
import {RedocComponent, BaseComponent} from '../base'; import {ElementRef} from 'angular2/core';
import SchemaSampler from 'json-schema-instantiator'; import SchemaSampler from 'json-schema-instantiator';
import {JsonFormatter} from '../../utils/JsonFormatterPipe'; import {JsonFormatter} from '../../utils/JsonFormatterPipe';
import {ElementRef} from 'angular2/core'; import {RedocComponent, BaseComponent, SchemaManager} from '../base';
@RedocComponent({ @RedocComponent({
selector: 'schema-sample', selector: 'schema-sample',
@ -13,6 +13,7 @@ import {ElementRef} from 'angular2/core';
pipes: [JsonFormatter], pipes: [JsonFormatter],
styleUrls: ['./lib/components/SchemaSample/schema-sample.css'] styleUrls: ['./lib/components/SchemaSample/schema-sample.css']
}) })
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef]])
export default class SchemaSample extends BaseComponent { export default class SchemaSample extends BaseComponent {
constructor(schemaMgr, elementRef) { constructor(schemaMgr, elementRef) {
super(schemaMgr); super(schemaMgr);
@ -54,4 +55,3 @@ export default class SchemaSample extends BaseComponent {
}); });
} }
} }
SchemaSample.parameters = SchemaSample.parameters.concat([[ElementRef]]);

View File

@ -1,13 +1,14 @@
'use strict'; 'use strict';
import {RedocComponent, BaseComponent} from '../base';
import {redocEvents} from '../../events';
import {NgZone, ChangeDetectionStrategy, ElementRef} from 'angular2/core'; import {NgZone, ChangeDetectionStrategy, ElementRef} from 'angular2/core';
import {document} from 'angular2/src/facade/browser'; import {document} from 'angular2/src/facade/browser';
import {BrowserDomAdapter} from 'angular2/platform/browser'; import {BrowserDomAdapter} from 'angular2/platform/browser';
import {global} from 'angular2/src/facade/lang'; import {global} from 'angular2/src/facade/lang';
import {RedocComponent, BaseComponent, SchemaManager} from '../base';
import {redocEvents} from '../../events';
import OptionsManager from '../../options';
const CHANGE = { const CHANGE = {
NEXT : 1, NEXT : 1,
BACK : -1, BACK : -1,
@ -26,20 +27,18 @@ const INVIEW_POSITION = {
styleUrls: ['./lib/components/SideMenu/side-menu.css'], styleUrls: ['./lib/components/SideMenu/side-menu.css'],
changeDetection: ChangeDetectionStrategy.Default changeDetection: ChangeDetectionStrategy.Default
}) })
@Reflect.metadata('parameters', [[SchemaManager], [ElementRef],
[BrowserDomAdapter], [NgZone], OptionsManager])
export default class SideMenu extends BaseComponent { export default class SideMenu extends BaseComponent {
constructor(schemaMgr, elementRef, adapter, zone, redoc) { constructor(schemaMgr, elementRef, dom, zone, optionsMgr) {
super(schemaMgr); super(schemaMgr);
this.zone = zone; this.$element = elementRef.nativeElement;
this.adapter = adapter; this.dom = dom;
this.redoc = redoc; this.options = optionsMgr.options;
this.$scrollParent = this.options.$scrollParent;
this.scrollParent = this.redoc.scrollParent;
this.mobileNav = adapter.querySelector(elementRef.nativeElement, '.mobile-nav');
this.resourcesNav = adapter.querySelector(elementRef.nativeElement, '#resources-nav');
// for some reason constructor is not run inside zone // for some reason constructor is not run inside zone
// as workaround running it manually // as workaround running it manually
this.zone.run(() => { zone.run(() => {
this.bindEvents(); this.bindEvents();
}); });
this.activeCatIdx = 0; this.activeCatIdx = 0;
@ -53,18 +52,26 @@ export default class SideMenu extends BaseComponent {
} }
scrollY() { scrollY() {
return (this.scrollParent.pageYOffset != null) ? this.scrollParent.pageYOffset : this.scrollParent.scrollTop; return (this.$scrollParent.pageYOffset != null) ? this.$scrollParent.pageYOffset : this.$scrollParent.scrollTop;
} }
hashScroll(evt) { hashScroll(evt) {
let hash = this.adapter.getLocation().hash; let hash = this.dom.getLocation().hash;
if (!hash) return; if (!hash) return;
let $el;
hash = hash.substr(1); hash = hash.substr(1);
let tag = hash.split('/')[0]; let namespace = hash.split('/')[0];
let ptr = hash.substr(tag.length); let ptr = hash.substr(namespace.length + 1);
let el = this.getMethodEl(ptr, tag); if (namespace === 'operation') {
if (el) this.scrollTo(el); $el = this.getMethodElByOperId(ptr);
} else if (namespace === 'tag') {
let tag = ptr.split('/')[0];
ptr = ptr.substr(tag.length);
$el = this.getMethodElByPtr(ptr, tag);
}
if ($el) this.scrollTo($el);
if (evt) evt.preventDefault(); if (evt) evt.preventDefault();
} }
@ -73,12 +80,12 @@ export default class SideMenu extends BaseComponent {
//decorate option.scrollYOffset to account mobile nav //decorate option.scrollYOffset to account mobile nav
this.scrollYOffset = () => { this.scrollYOffset = () => {
let mobileNavOffset = this.mobileNav.clientHeight; let mobileNavOffset = this.$mobileNav.clientHeight;
return this.redoc.options.scrollYOffset() + mobileNavOffset; return this.options.scrollYOffset() + mobileNavOffset;
}; };
this._cancel = {}; this._cancel = {};
this._cancel.scroll = this.adapter.onAndCancel(this.scrollParent, 'scroll', () => { this.scrollHandler(); }); this._cancel.scroll = this.dom.onAndCancel(this.$scrollParent, 'scroll', () => { this.scrollHandler(); });
this._cancel.hash = this.adapter.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt)); this._cancel.hash = this.dom.onAndCancel(global, 'hashchange', evt => this.hashScroll(evt));
} }
destroy() { destroy() {
@ -94,14 +101,14 @@ export default class SideMenu extends BaseComponent {
this.scrollToActive(); this.scrollToActive();
} }
scrollTo(el) { scrollTo($el) {
// TODO: rewrite this to use offsetTop as more reliable solution // TODO: rewrite this to use offsetTop as more reliable solution
let subjRect = el.getBoundingClientRect(); let subjRect = $el.getBoundingClientRect();
let offset = this.scrollY() + subjRect.top - this.scrollYOffset() + 1; let offset = this.scrollY() + subjRect.top - this.scrollYOffset() + 1;
if (this.scrollParent.scrollTo) { if (this.$scrollParent.scrollTo) {
this.scrollParent.scrollTo(0, offset); this.$scrollParent.scrollTo(0, offset);
} else { } else {
this.scrollParent.scrollTop = offset; this.$scrollParent.scrollTop = offset;
} }
} }
@ -170,22 +177,27 @@ export default class SideMenu extends BaseComponent {
return (methodIdx === 0 && catIdx === 0); return (methodIdx === 0 && catIdx === 0);
} }
getMethodEl(ptr, tag) { getMethodElByPtr(ptr, tag) {
let selector = ptr ? `[pointer="${ptr}"][tag="${tag}"]` : `[tag="${tag}"]`; let selector = ptr ? `[pointer="${ptr}"][tag="${tag}"]` : `[tag="${tag}"]`;
return document.querySelector(selector); return document.querySelector(selector);
} }
getMethodElByOperId(operationId) {
let selector =`[operation-id="${operationId}"]`;
return document.querySelector(selector);
}
getCurrentMethodEl() { getCurrentMethodEl() {
return this.getMethodEl(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name); return this.getMethodElByPtr(this.activeMethodPtr, this.data.menu[this.activeCatIdx].name);
} }
/* returns 1 if element if above the view, 0 if in view and -1 below the view */ /* returns 1 if element if above the view, 0 if in view and -1 below the view */
getElementInViewPos(el) { getElementInViewPos($el) {
if (Math.floor(el.getBoundingClientRect().top) > this.scrollYOffset()) { if (Math.floor($el.getBoundingClientRect().top) > this.scrollYOffset()) {
return INVIEW_POSITION.ABOVE; return INVIEW_POSITION.ABOVE;
} }
if (el.getBoundingClientRect().bottom <= this.scrollYOffset()) { if ($el.getBoundingClientRect().bottom <= this.scrollYOffset()) {
return INVIEW_POSITION.BELLOW; return INVIEW_POSITION.BELLOW;
} }
return INVIEW_POSITION.INVIEW; return INVIEW_POSITION.INVIEW;
@ -196,9 +208,9 @@ export default class SideMenu extends BaseComponent {
this.prevOffsetY = this.scrollY(); this.prevOffsetY = this.scrollY();
let stable = false; let stable = false;
while(!stable) { while(!stable) {
let activeMethodHost = this.getCurrentMethodEl(); let $activeMethodHost = this.getCurrentMethodEl();
if (!activeMethodHost) return; if (!$activeMethodHost) return;
var elementInViewPos = this.getElementInViewPos(activeMethodHost); var elementInViewPos = this.getElementInViewPos($activeMethodHost);
if(isScrolledDown && elementInViewPos === INVIEW_POSITION.BELLOW) { if(isScrolledDown && elementInViewPos === INVIEW_POSITION.BELLOW) {
stable = this.changeActive(CHANGE.NEXT); stable = this.changeActive(CHANGE.NEXT);
continue; continue;
@ -219,25 +231,26 @@ export default class SideMenu extends BaseComponent {
} }
mobileMode() { mobileMode() {
return this.mobileNav.clientHeight > 0; return this.$mobileNav.clientHeight > 0;
} }
toggleMobileNav() { toggleMobileNav() {
let dom = this.adapter; let dom = this.dom;
let overflowParent = (this.scrollParent === global) ? dom.defaultDoc().body : this.scrollParent; let $overflowParent = (this.$scrollParent === global) ? dom.defaultDoc().body : this.$scrollParent;
if (dom.hasStyle(this.resourcesNav, 'height')) { if (dom.hasStyle(this.$resourcesNav, 'height')) {
dom.removeStyle(this.resourcesNav, 'height'); dom.removeStyle(this.$resourcesNav, 'height');
dom.removeStyle(overflowParent, 'overflow-y'); dom.removeStyle($overflowParent, 'overflow-y');
} else { } else {
let viewportHeight = this.scrollParent.innerHeight || this.scrollParent.clientHeight; let viewportHeight = this.$scrollParent.innerHeight || this.$scrollParent.clientHeight;
let height = viewportHeight - this.mobileNav.getBoundingClientRect().bottom; let height = viewportHeight - this.$mobileNav.getBoundingClientRect().bottom;
dom.setStyle(overflowParent, 'overflow-y', 'hidden'); dom.setStyle($overflowParent, 'overflow-y', 'hidden');
dom.setStyle(this.resourcesNav, 'height', height + 'px'); dom.setStyle(this.$resourcesNav, 'height', height + 'px');
} }
} }
init() { init() {
this.$mobileNav = this.dom.querySelector(this.$element, '.mobile-nav');
this.$resourcesNav = this.dom.querySelector(this.$element, '#resources-nav');
this.changeActive(CHANGE.INITIAL); this.changeActive(CHANGE.INITIAL);
} }
} }
SideMenu.parameters = SideMenu.parameters.concat([[ElementRef], [BrowserDomAdapter], [NgZone]]);

View File

@ -3,6 +3,7 @@
import { getChildDebugElement, mouseclick} from 'tests/helpers'; import { getChildDebugElement, mouseclick} from 'tests/helpers';
import {Component, View, provide, ViewMetadata} from 'angular2/core'; import {Component, View, provide, ViewMetadata} from 'angular2/core';
import {BrowserDomAdapter} from 'angular2/platform/browser'; import {BrowserDomAdapter} from 'angular2/platform/browser';
import OptionsManager from 'lib/options';
import { import {
TestComponentBuilder, TestComponentBuilder,
@ -15,15 +16,14 @@ import {
import {redocEvents} from 'lib/events'; import {redocEvents} from 'lib/events';
import MethodsList from 'lib/components/MethodsList/methods-list'; import MethodsList from 'lib/components/MethodsList/methods-list';
import SideMenu from 'lib/components/SideMenu/side-menu'; import SideMenu from 'lib/components/SideMenu/side-menu';
import Redoc from 'lib/components/Redoc/redoc';
import SchemaManager from 'lib/utils/SchemaManager'; import SchemaManager from 'lib/utils/SchemaManager';
let _mockRedoc = { let testOptions = new OptionsManager();
options: { testOptions.options = {
scrollYOffset: () => 0 scrollYOffset: () => 0,
},
scrollParent: window scrollParent: window
}; };
describe('Redoc components', () => { describe('Redoc components', () => {
describe('SideMenu Component', () => { describe('SideMenu Component', () => {
let builder; let builder;
@ -32,7 +32,7 @@ describe('Redoc components', () => {
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}), provide(SchemaManager, {useValue: new SchemaManager()}),
provide(BrowserDomAdapter, {useValue: new BrowserDomAdapter()}), provide(BrowserDomAdapter, {useValue: new BrowserDomAdapter()}),
provide(Redoc, {useValue: _mockRedoc}) provide(OptionsManager, {useValue: testOptions})
]); ]);
beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => { beforeEach(injectAsync([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => {
builder = tcb; builder = tcb;
@ -61,7 +61,7 @@ describe('Redoc components', () => {
}); });
it('should run hashScroll when redoc bootstrapped', (done) => { it('should run hashScroll when redoc bootstrapped', (done) => {
spyOn(component.adapter, 'getLocation').and.returnValue({hash: ''}); spyOn(component.dom, 'getLocation').and.returnValue({hash: ''});
spyOn(component, 'hashScroll').and.stub(); spyOn(component, 'hashScroll').and.stub();
spyOn(window, 'scrollTo').and.stub(); spyOn(window, 'scrollTo').and.stub();
redocEvents.bootstrapped.next(); redocEvents.bootstrapped.next();
@ -75,9 +75,23 @@ describe('Redoc components', () => {
}); });
}); });
it('should scroll to method when location hash is present', (done) => { it('should scroll to method when location hash is present [jp]', (done) => {
let hash = '#pet/paths/~1pet~1findByStatus/get'; let hash = '#tag/pet/paths/~1pet~1findByStatus/get';
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash}); spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough();
spyOn(window, 'scrollTo').and.stub();
redocEvents.bootstrapped.next();
setTimeout(() => {
expect(component.hashScroll).toHaveBeenCalled();
let scrollY = window.scrollTo.calls.argsFor(0)[1];
expect(scrollY).toBeGreaterThan(0);
done();
});
});
it('should scroll to method when location hash is present [operation]', (done) => {
let hash = '#operation/getPetById';
spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough(); spyOn(component, 'hashScroll').and.callThrough();
spyOn(window, 'scrollTo').and.stub(); spyOn(window, 'scrollTo').and.stub();
redocEvents.bootstrapped.next(); redocEvents.bootstrapped.next();
@ -90,7 +104,7 @@ describe('Redoc components', () => {
}); });
}); });
describe('scollable div parent case', () => { describe('scrollable div parent case', () => {
let menuNativeEl; let menuNativeEl;
beforeEach((done) => { beforeEach((done) => {
let scollableDivTmpl = let scollableDivTmpl =
@ -100,14 +114,13 @@ describe('Redoc components', () => {
</div>`; </div>`;
builder = builder.overrideView( builder = builder.overrideView(
TestApp, new ViewMetadata({template: scollableDivTmpl, directives: [MethodsList, SideMenu]})); TestApp, new ViewMetadata({template: scollableDivTmpl, directives: [MethodsList, SideMenu]}));
builder.createAsync(TestApp).then(_fixture => { builder.createAsync(TestApp).then(_fixture => {
fixture = _fixture; fixture = _fixture;
component = getChildDebugElement(fixture.debugElement, 'side-menu').componentInstance; component = getChildDebugElement(fixture.debugElement, 'side-menu').componentInstance;
menuNativeEl = getChildDebugElement(fixture.debugElement, 'side-menu').nativeElement; menuNativeEl = getChildDebugElement(fixture.debugElement, 'side-menu').nativeElement;
component.scrollParent = _fixture.nativeElement.children[0]; component.options.scrollParent = _fixture.nativeElement.children[0];
component.$scrollParent = _fixture.nativeElement.children[0];
fixture.detectChanges(); fixture.detectChanges();
done(); done();
}, err => { }, err => {
throw err; throw err;
@ -121,14 +134,26 @@ describe('Redoc components', () => {
expect(component.data).not.toBeNull(); expect(component.data).not.toBeNull();
}); });
it('should scroll to method when location hash is present', (done) => { it('should scroll to method when location hash is present [jp]', (done) => {
let hash = '#pet/paths/~1pet~1findByStatus/get'; let hash = '#tag/pet/paths/~1pet~1findByStatus/get';
spyOn(component.adapter, 'getLocation').and.returnValue({hash: hash}); spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough(); spyOn(component, 'hashScroll').and.callThrough();
redocEvents.bootstrapped.next(); redocEvents.bootstrapped.next();
setTimeout(() => { setTimeout(() => {
expect(component.hashScroll).toHaveBeenCalled(); expect(component.hashScroll).toHaveBeenCalled();
expect(component.scrollParent.scrollTop).toBeGreaterThan(0); expect(component.$scrollParent.scrollTop).toBeGreaterThan(0);
done();
});
});
it('should scroll to method when location hash is present [operation]', (done) => {
let hash = '#operation/getPetById';
spyOn(component.dom, 'getLocation').and.returnValue({hash: hash});
spyOn(component, 'hashScroll').and.callThrough();
redocEvents.bootstrapped.next();
setTimeout(() => {
expect(component.hashScroll).toHaveBeenCalled();
expect(component.$scrollParent.scrollTop).toBeGreaterThan(0);
done(); done();
}); });
}); });
@ -138,14 +163,14 @@ describe('Redoc components', () => {
component.activeMethodIdx.should.be.equal(-1); component.activeMethodIdx.should.be.equal(-1);
let elTop = component.getCurrentMethodEl().getBoundingClientRect().bottom; let elTop = component.getCurrentMethodEl().getBoundingClientRect().bottom;
component.scrollParent.scrollTop = elTop + 1; component.$scrollParent.scrollTop = elTop + 1;
//simulate scroll down //simulate scroll down
spyOn(component, 'scrollY').and.returnValue(elTop + 2); spyOn(component, 'scrollY').and.returnValue(elTop + 2);
component.scrollHandler(); component.scrollHandler();
component.activeCatIdx.should.be.equal(1); component.activeCatIdx.should.be.equal(1);
component.scrollParent.scrollTop = elTop - 1; component.$scrollParent.scrollTop = elTop - 1;
//simulate scroll up //simulate scroll up
component.scrollY.and.returnValue(elTop - 2); component.scrollY.and.returnValue(elTop - 2);
component.scrollHandler(); component.scrollHandler();
@ -161,10 +186,10 @@ describe('Redoc components', () => {
}); });
it('should scroll to appropriate element when click on menu label', () => { it('should scroll to appropriate element when click on menu label', () => {
component.scrollParent.scrollTop.should.be.equal(0); component.$scrollParent.scrollTop.should.be.equal(0);
let menuItemEl = menuNativeEl.querySelector('li'); let menuItemEl = menuNativeEl.querySelector('li');
mouseclick(menuItemEl); mouseclick(menuItemEl);
component.scrollParent.scrollTop.should.be.above(0); component.$scrollParent.scrollTop.should.be.above(0);
}); });
}); });
}); });

View File

@ -5,6 +5,8 @@ import SchemaManager from '../utils/SchemaManager';
import JsonPointer from '../utils/JsonPointer'; import JsonPointer from '../utils/JsonPointer';
import {MarkedPipe, JsonPointerEscapePipe} from '../utils/pipes'; import {MarkedPipe, JsonPointerEscapePipe} from '../utils/pipes';
export { SchemaManager };
// common inputs for all components // common inputs for all components
let commonInputs = ['pointer']; // json pointer to the schema chunk let commonInputs = ['pointer']; // json pointer to the schema chunk
@ -77,6 +79,7 @@ export function RedocComponent(options) {
* Generic Component * Generic Component
* @class * @class
*/ */
@Reflect.metadata('parameters', [[SchemaManager]])
export class BaseComponent { export class BaseComponent {
constructor(schemaMgr) { constructor(schemaMgr) {
this.schemaMgr = schemaMgr; this.schemaMgr = schemaMgr;
@ -208,4 +211,3 @@ export class BaseComponent {
*/ */
destroy() {} destroy() {}
} }
BaseComponent.parameters = [[SchemaManager]];

View File

@ -1,10 +1,7 @@
'use strict'; 'use strict';
import {Redoc} from './components/index'; import {Redoc} from './components/index';
import {enableProdMode} from 'angular2/core';
export var init = Redoc.init; export var init = Redoc.init;
window.Redoc = Redoc; window.Redoc = Redoc;
enableProdMode();
Redoc.autoInit(); Redoc.autoInit();

View File

@ -1,28 +1,22 @@
'use strict'; 'use strict';
export var options = { import {isFunction, isString} from 'angular2/src/facade/lang';
scrollYOffset: 0 import {BrowserDomAdapter} from 'angular2/platform/browser';
import {global} from 'angular2/src/facade/lang';
var defaults = {
scrollYOffset: 0,
disableLazySchemas: false,
debugMode: global.redocDebugMode
}; };
// singleton var OPTION_NAMES = new Set(['scrollYOffset', 'disableLazySchemas']);
@Reflect.metadata('parameters', [[BrowserDomAdapter]])
export default class OptionsManager { export default class OptionsManager {
constructor() { constructor() {
if (OptionsManager.prototype._instance) { this._options = defaults;
return OptionsManager.prototype._instance; this.dom = new BrowserDomAdapter();
}
OptionsManager.prototype._instance = this;
this._defaults = {
scrollYOffset: 0,
disableLazySchemas: false
};
this._options = {};
}
static instance() {
return new OptionsManager();
} }
get options() { get options() {
@ -30,6 +24,50 @@ export default class OptionsManager {
} }
set options(opts) { set options(opts) {
this._options = Object.assign({}, this._defaults, opts); this._options = Object.assign(this._options, opts);
}
parseOptions(el) {
let parsedOpts;
let attributesMap = this.dom.attributeMap(el);
parsedOpts = {};
Array.from(attributesMap.keys())
//camelCasify
.map(k => ({
attrName: k,
name: k.replace(/-(.)/g, (m, $1) => $1.toUpperCase())
})
)
.filter(option => OPTION_NAMES.has(option.name))
.forEach(option => {
parsedOpts[option.name] = attributesMap.get(option.attrName);
});
this.options = parsedOpts;
this._normalizeOptions();
}
_normalizeOptions() {
// modify scrollYOffset to always be a function
if (!isFunction(this._options.scrollYOffset)) {
if (isFinite(this._options.scrollYOffset)) {
// if number specified create function that returns this value
let numberOffset = parseFloat(this._options.scrollYOffset);
this.options.scrollYOffset = () => numberOffset;
} else {
// if selector or node function that returns bottom offset of this node
let el = this._options.scrollYOffset;
if (!(el instanceof Node)) {
el = this.dom.query(el);
}
if (!el) {
this._options.scrollYOffset = () => 0;
} else {
this._options.scrollYOffset = () => el.offsetTop + el.offsetHeight;
}
}
}
if (isString(this._options.disableLazySchemas)) this._options.disableLazySchemas = true;
} }
} }

View File

@ -63,4 +63,6 @@ export class JsonPointer extends JsonPointerLib {
JsonPointerLib._origParse = JsonPointerLib.parse; JsonPointerLib._origParse = JsonPointerLib.parse;
JsonPointerLib.parse = JsonPointer.parse; JsonPointerLib.parse = JsonPointer.parse;
Object.assign(JsonPointer, JsonPointerLib);
export default JsonPointer; export default JsonPointer;

View File

@ -150,7 +150,11 @@ export default class SchemaManager {
} }
if (tagDetails['x-traitTag']) continue; if (tagDetails['x-traitTag']) continue;
if (!tagDetails.methods) tagDetails.methods = []; if (!tagDetails.methods) tagDetails.methods = [];
tagDetails.methods.push({pointer: methodPointer, summary: methodSummary}); tagDetails.methods.push({
pointer: methodPointer,
summary: methodSummary,
operationId: methodInfo.operationId
});
} }
} }
} }

View File

@ -0,0 +1,10 @@
/*eslint no-unused-vars: 0*/
/*eslint strict: 0*/
var $buoop = { vs: {i:9, f:25, o:12.1, s:7}, c:2 };
function $buo_f(){
var e = document.createElement('script');
e.src = '//browser-update.org/update.min.js';
document.body.appendChild(e);
}
try {document.addEventListener('DOMContentLoaded', $buo_f, false);}
catch(e){window.attachEvent('onload', $buo_f);}

View File

@ -4,7 +4,7 @@ import {Pipe} from 'angular2/core';
import {isString, stringify, isBlank} from 'angular2/src/facade/lang'; import {isString, stringify, isBlank} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions'; import {BaseException} from 'angular2/src/facade/exceptions';
import {JsonPointer} from './JsonPointer'; import {JsonPointer} from './JsonPointer';
import marked from 'marked';
import Prism from 'prismjs'; import Prism from 'prismjs';
import 'prismjs/components/prism-actionscript.js'; import 'prismjs/components/prism-actionscript.js';
import 'prismjs/components/prism-c.js'; import 'prismjs/components/prism-c.js';
@ -29,6 +29,8 @@ import 'prismjs/components/prism-vim.js';
import 'prismjs/themes/prism-dark.css!css'; import 'prismjs/themes/prism-dark.css!css';
import marked from 'marked';
marked.setOptions({ marked.setOptions({
renderer: new marked.Renderer(), renderer: new marked.Renderer(),
gfm: true, gfm: true,

View File

@ -1,7 +1,7 @@
{ {
"name": "redoc", "name": "redoc",
"description": "Swagger-generated API Reference Documentation", "description": "Swagger-generated API Reference Documentation",
"version": "0.5.2", "version": "0.6.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/Rebilly/ReDoc" "url": "git://github.com/Rebilly/ReDoc"
@ -9,7 +9,6 @@
"main": "dist/redoc.min.js", "main": "dist/redoc.min.js",
"scripts": { "scripts": {
"test": "gulp lint && ./build/run_tests.sh", "test": "gulp lint && ./build/run_tests.sh",
"prepublish": "gulp build",
"jspm-install": "jspm install", "jspm-install": "jspm install",
"start": "gulp serve", "start": "gulp serve",
"build-dist": "gulp build", "build-dist": "gulp build",
@ -29,8 +28,9 @@
"jspm": { "jspm": {
"configFile": "system.config.js", "configFile": "system.config.js",
"dependencies": { "dependencies": {
"angular2": "npm:angular2@^2.0.0-beta.0", "angular2": "npm:angular2@2.0.0-beta.6",
"es6-shim": "github:es-shims/es6-shim@^0.33.6", "es6-shim": "github:es-shims/es6-shim@^0.33.6",
"json": "github:systemjs/plugin-json@^0.1.0",
"json-formatter-js": "npm:json-formatter-js@^0.2.0", "json-formatter-js": "npm:json-formatter-js@^0.2.0",
"json-pointer": "npm:json-pointer@^0.3.0", "json-pointer": "npm:json-pointer@^0.3.0",
"json-schema-instantiator": "npm:json-schema-instantiator@^0.3.0", "json-schema-instantiator": "npm:json-schema-instantiator@^0.3.0",

View File

@ -37,7 +37,7 @@ System.config({
}, },
map: { map: {
"angular2": "npm:angular2@2.0.0-beta.0", "angular2": "npm:angular2@2.0.0-beta.6",
"babel": "npm:babel-core@5.8.34", "babel": "npm:babel-core@5.8.34",
"babel-runtime": "npm:babel-runtime@5.8.34", "babel-runtime": "npm:babel-runtime@5.8.34",
"clean-css": "npm:clean-css@3.4.6", "clean-css": "npm:clean-css@3.4.6",
@ -135,14 +135,14 @@ System.config({
"path": "github:jspm/nodelibs-path@0.1.0", "path": "github:jspm/nodelibs-path@0.1.0",
"process": "github:jspm/nodelibs-process@0.1.2" "process": "github:jspm/nodelibs-process@0.1.2"
}, },
"npm:angular2@2.0.0-beta.0": { "npm:angular2@2.0.0-beta.6": {
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
"es6-promise": "npm:es6-promise@3.0.2", "es6-promise": "npm:es6-promise@3.1.2",
"es6-shim": "npm:es6-shim@0.33.13", "es6-shim": "npm:es6-shim@0.33.13",
"process": "github:jspm/nodelibs-process@0.1.2", "process": "github:jspm/nodelibs-process@0.1.2",
"reflect-metadata": "npm:reflect-metadata@0.1.2", "reflect-metadata": "npm:reflect-metadata@0.1.2",
"rxjs": "npm:rxjs@5.0.0-beta.0", "rxjs": "npm:rxjs@5.0.0-beta.0",
"zone.js": "npm:zone.js@0.5.10" "zone.js": "npm:zone.js@0.5.14"
}, },
"npm:argparse@1.0.3": { "npm:argparse@1.0.3": {
"assert": "github:jspm/nodelibs-assert@0.1.0", "assert": "github:jspm/nodelibs-assert@0.1.0",
@ -153,9 +153,9 @@ System.config({
"sprintf-js": "npm:sprintf-js@1.0.3", "sprintf-js": "npm:sprintf-js@1.0.3",
"util": "github:jspm/nodelibs-util@0.1.0" "util": "github:jspm/nodelibs-util@0.1.0"
}, },
"npm:asn1.js@4.3.0": { "npm:asn1.js@4.4.0": {
"assert": "github:jspm/nodelibs-assert@0.1.0", "assert": "github:jspm/nodelibs-assert@0.1.0",
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"inherits": "npm:inherits@2.0.1", "inherits": "npm:inherits@2.0.1",
"minimalistic-assert": "npm:minimalistic-assert@1.0.0", "minimalistic-assert": "npm:minimalistic-assert@1.0.0",
@ -199,11 +199,14 @@ System.config({
"readable-stream": "npm:readable-stream@2.0.5", "readable-stream": "npm:readable-stream@2.0.5",
"util": "github:jspm/nodelibs-util@0.1.0" "util": "github:jspm/nodelibs-util@0.1.0"
}, },
"npm:bn.js@4.10.3": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0"
},
"npm:boom@2.10.1": { "npm:boom@2.10.1": {
"hoek": "npm:hoek@2.16.3", "hoek": "npm:hoek@2.16.3",
"http": "github:jspm/nodelibs-http@1.7.1" "http": "github:jspm/nodelibs-http@1.7.1"
}, },
"npm:browserify-aes@1.0.5": { "npm:browserify-aes@1.0.6": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"buffer-xor": "npm:buffer-xor@1.0.3", "buffer-xor": "npm:buffer-xor@1.0.3",
"cipher-base": "npm:cipher-base@1.0.2", "cipher-base": "npm:cipher-base@1.0.2",
@ -215,7 +218,7 @@ System.config({
"systemjs-json": "github:systemjs/plugin-json@0.1.0" "systemjs-json": "github:systemjs/plugin-json@0.1.0"
}, },
"npm:browserify-cipher@1.0.0": { "npm:browserify-cipher@1.0.0": {
"browserify-aes": "npm:browserify-aes@1.0.5", "browserify-aes": "npm:browserify-aes@1.0.6",
"browserify-des": "npm:browserify-des@1.0.0", "browserify-des": "npm:browserify-des@1.0.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
@ -229,20 +232,20 @@ System.config({
"inherits": "npm:inherits@2.0.1" "inherits": "npm:inherits@2.0.1"
}, },
"npm:browserify-rsa@4.0.0": { "npm:browserify-rsa@4.0.0": {
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"constants": "github:jspm/nodelibs-constants@0.1.0", "constants": "github:jspm/nodelibs-constants@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
"randombytes": "npm:randombytes@2.0.2" "randombytes": "npm:randombytes@2.0.2"
}, },
"npm:browserify-sign@4.0.0": { "npm:browserify-sign@4.0.0": {
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"browserify-rsa": "npm:browserify-rsa@4.0.0", "browserify-rsa": "npm:browserify-rsa@4.0.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"create-hash": "npm:create-hash@1.1.2", "create-hash": "npm:create-hash@1.1.2",
"create-hmac": "npm:create-hmac@1.1.4", "create-hmac": "npm:create-hmac@1.1.4",
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
"elliptic": "npm:elliptic@6.0.2", "elliptic": "npm:elliptic@6.2.3",
"inherits": "npm:inherits@2.0.1", "inherits": "npm:inherits@2.0.1",
"parse-asn1": "npm:parse-asn1@5.0.0", "parse-asn1": "npm:parse-asn1@5.0.0",
"stream": "github:jspm/nodelibs-stream@0.1.0" "stream": "github:jspm/nodelibs-stream@0.1.0"
@ -332,10 +335,10 @@ System.config({
"buffer": "github:jspm/nodelibs-buffer@0.1.0" "buffer": "github:jspm/nodelibs-buffer@0.1.0"
}, },
"npm:create-ecdh@4.0.0": { "npm:create-ecdh@4.0.0": {
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
"elliptic": "npm:elliptic@6.0.2" "elliptic": "npm:elliptic@6.2.3"
}, },
"npm:create-hash@1.1.2": { "npm:create-hash@1.1.2": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
@ -363,14 +366,14 @@ System.config({
"create-ecdh": "npm:create-ecdh@4.0.0", "create-ecdh": "npm:create-ecdh@4.0.0",
"create-hash": "npm:create-hash@1.1.2", "create-hash": "npm:create-hash@1.1.2",
"create-hmac": "npm:create-hmac@1.1.4", "create-hmac": "npm:create-hmac@1.1.4",
"diffie-hellman": "npm:diffie-hellman@5.0.1", "diffie-hellman": "npm:diffie-hellman@5.0.2",
"inherits": "npm:inherits@2.0.1", "inherits": "npm:inherits@2.0.1",
"pbkdf2": "npm:pbkdf2@3.0.4", "pbkdf2": "npm:pbkdf2@3.0.4",
"public-encrypt": "npm:public-encrypt@4.0.0", "public-encrypt": "npm:public-encrypt@4.0.0",
"randombytes": "npm:randombytes@2.0.2" "randombytes": "npm:randombytes@2.0.2"
}, },
"npm:dashdash@1.12.1": { "npm:dashdash@1.12.2": {
"assert-plus": "npm:assert-plus@0.1.5", "assert-plus": "npm:assert-plus@0.2.0",
"fs": "github:jspm/nodelibs-fs@0.1.2", "fs": "github:jspm/nodelibs-fs@0.1.2",
"path": "github:jspm/nodelibs-path@0.1.0", "path": "github:jspm/nodelibs-path@0.1.0",
"process": "github:jspm/nodelibs-process@0.1.2", "process": "github:jspm/nodelibs-process@0.1.2",
@ -393,8 +396,8 @@ System.config({
"inherits": "npm:inherits@2.0.1", "inherits": "npm:inherits@2.0.1",
"minimalistic-assert": "npm:minimalistic-assert@1.0.0" "minimalistic-assert": "npm:minimalistic-assert@1.0.0"
}, },
"npm:diffie-hellman@5.0.1": { "npm:diffie-hellman@5.0.2": {
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
"miller-rabin": "npm:miller-rabin@4.0.0", "miller-rabin": "npm:miller-rabin@4.0.0",
@ -406,14 +409,14 @@ System.config({
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
"jsbn": "npm:jsbn@0.1.0" "jsbn": "npm:jsbn@0.1.0"
}, },
"npm:elliptic@6.0.2": { "npm:elliptic@6.2.3": {
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"brorand": "npm:brorand@1.0.5", "brorand": "npm:brorand@1.0.5",
"hash.js": "npm:hash.js@1.0.3", "hash.js": "npm:hash.js@1.0.3",
"inherits": "npm:inherits@2.0.1", "inherits": "npm:inherits@2.0.1",
"systemjs-json": "github:systemjs/plugin-json@0.1.0" "systemjs-json": "github:systemjs/plugin-json@0.1.0"
}, },
"npm:es6-promise@3.0.2": { "npm:es6-promise@3.1.2": {
"process": "github:jspm/nodelibs-process@0.1.2" "process": "github:jspm/nodelibs-process@0.1.2"
}, },
"npm:es6-shim@0.33.13": { "npm:es6-shim@0.33.13": {
@ -543,7 +546,7 @@ System.config({
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"call-me-maybe": "npm:call-me-maybe@1.0.1", "call-me-maybe": "npm:call-me-maybe@1.0.1",
"debug": "npm:debug@2.2.0", "debug": "npm:debug@2.2.0",
"es6-promise": "npm:es6-promise@3.0.2", "es6-promise": "npm:es6-promise@3.1.2",
"events": "github:jspm/nodelibs-events@0.1.1", "events": "github:jspm/nodelibs-events@0.1.1",
"fs": "github:jspm/nodelibs-fs@0.1.2", "fs": "github:jspm/nodelibs-fs@0.1.2",
"http": "github:jspm/nodelibs-http@1.7.1", "http": "github:jspm/nodelibs-http@1.7.1",
@ -584,7 +587,7 @@ System.config({
"process": "github:jspm/nodelibs-process@0.1.2" "process": "github:jspm/nodelibs-process@0.1.2"
}, },
"npm:miller-rabin@4.0.0": { "npm:miller-rabin@4.0.0": {
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"brorand": "npm:brorand@1.0.5" "brorand": "npm:brorand@1.0.5"
}, },
"npm:mime-db@1.21.0": { "npm:mime-db@1.21.0": {
@ -616,8 +619,8 @@ System.config({
"process": "github:jspm/nodelibs-process@0.1.2" "process": "github:jspm/nodelibs-process@0.1.2"
}, },
"npm:parse-asn1@5.0.0": { "npm:parse-asn1@5.0.0": {
"asn1.js": "npm:asn1.js@4.3.0", "asn1.js": "npm:asn1.js@4.4.0",
"browserify-aes": "npm:browserify-aes@1.0.5", "browserify-aes": "npm:browserify-aes@1.0.6",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"create-hash": "npm:create-hash@1.1.2", "create-hash": "npm:create-hash@1.1.2",
"evp_bytestokey": "npm:evp_bytestokey@1.0.0", "evp_bytestokey": "npm:evp_bytestokey@1.0.0",
@ -637,10 +640,7 @@ System.config({
"systemjs-json": "github:systemjs/plugin-json@0.1.0" "systemjs-json": "github:systemjs/plugin-json@0.1.0"
}, },
"npm:pinkie-promise@2.0.0": { "npm:pinkie-promise@2.0.0": {
"pinkie": "npm:pinkie@2.0.1" "pinkie": "npm:pinkie@2.0.4"
},
"npm:pinkie@2.0.1": {
"process": "github:jspm/nodelibs-process@0.1.2"
}, },
"npm:prismjs@1.3.0": { "npm:prismjs@1.3.0": {
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
@ -654,7 +654,7 @@ System.config({
"assert": "github:jspm/nodelibs-assert@0.1.0" "assert": "github:jspm/nodelibs-assert@0.1.0"
}, },
"npm:public-encrypt@4.0.0": { "npm:public-encrypt@4.0.0": {
"bn.js": "npm:bn.js@4.6.4", "bn.js": "npm:bn.js@4.10.3",
"browserify-rsa": "npm:browserify-rsa@4.0.0", "browserify-rsa": "npm:browserify-rsa@4.0.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"create-hash": "npm:create-hash@1.1.2", "create-hash": "npm:create-hash@1.1.2",
@ -758,7 +758,7 @@ System.config({
"assert-plus": "npm:assert-plus@0.2.0", "assert-plus": "npm:assert-plus@0.2.0",
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"crypto": "github:jspm/nodelibs-crypto@0.1.0", "crypto": "github:jspm/nodelibs-crypto@0.1.0",
"dashdash": "npm:dashdash@1.12.1", "dashdash": "npm:dashdash@1.12.2",
"ecc-jsbn": "npm:ecc-jsbn@0.1.1", "ecc-jsbn": "npm:ecc-jsbn@0.1.1",
"jodid25519": "npm:jodid25519@1.0.2", "jodid25519": "npm:jodid25519@1.0.2",
"jsbn": "npm:jsbn@0.1.0", "jsbn": "npm:jsbn@0.1.0",
@ -793,7 +793,7 @@ System.config({
"buffer": "github:jspm/nodelibs-buffer@0.1.0", "buffer": "github:jspm/nodelibs-buffer@0.1.0",
"call-me-maybe": "npm:call-me-maybe@1.0.1", "call-me-maybe": "npm:call-me-maybe@1.0.1",
"debug": "npm:debug@2.2.0", "debug": "npm:debug@2.2.0",
"es6-promise": "npm:es6-promise@3.0.2", "es6-promise": "npm:es6-promise@3.1.2",
"events": "github:jspm/nodelibs-events@0.1.1", "events": "github:jspm/nodelibs-events@0.1.1",
"fs": "github:jspm/nodelibs-fs@0.1.2", "fs": "github:jspm/nodelibs-fs@0.1.2",
"http": "github:jspm/nodelibs-http@1.7.1", "http": "github:jspm/nodelibs-http@1.7.1",
@ -867,8 +867,8 @@ System.config({
"util": "github:jspm/nodelibs-util@0.1.0", "util": "github:jspm/nodelibs-util@0.1.0",
"validator": "npm:validator@4.5.0" "validator": "npm:validator@4.5.0"
}, },
"npm:zone.js@0.5.10": { "npm:zone.js@0.5.14": {
"es6-promise": "npm:es6-promise@3.0.2", "es6-promise": "npm:es6-promise@3.1.2",
"process": "github:jspm/nodelibs-process@0.1.2" "process": "github:jspm/nodelibs-process@0.1.2"
} }
} }

View File

@ -1,27 +1,20 @@
'use strict'; 'use strict';
import {BrowserDomAdapter} from 'angular2/platform/browser'; import {By} from 'angular2/platform/browser';
BrowserDomAdapter.makeCurrent();
/** Gets a child DebugElement by tag name. */ /** Gets a child DebugElement by tag name. */
export function getChildDebugElement(parent, tagName) { export function getChildDebugElement(parent, tagName) {
return parent.query(debugEl => { return parent.query(By.css(tagName));
return debugEl.nativeElement.tagName && debugEl.nativeElement.tagName.toLowerCase() === tagName;
});
} }
/** Gets a child DebugElement by Component Type. */ /** Gets a child DebugElement by Component Type. */
export function getChildDebugElementByType(parent, type) { export function getChildDebugElementByType(parent, type) {
return parent.query(debugEl => { return parent.query(By.directive(type));
return debugEl.componentInstance instanceof type;
});
} }
/** Gets a child DebugElements by tag name. */ /** Gets a child DebugElements by tag name. */
export function getChildDebugElementAll(parent, tagName) { export function getChildDebugElementAll(parent, tagName) {
return parent.queryAll(debugEl => { return parent.queryAll(By.css(tagName));
return debugEl.nativeElement.tagName && debugEl.nativeElement.tagName.toLowerCase() === tagName;
});
} }
export function mouseclick( element ) { export function mouseclick( element ) {

13
tests/setup.js Normal file
View File

@ -0,0 +1,13 @@
'use strict';
import {setBaseTestProviders} from 'angular2/testing';
import {
TEST_BROWSER_PLATFORM_PROVIDERS,
TEST_BROWSER_APPLICATION_PROVIDERS
} from 'angular2/platform/testing/browser';
import {
ELEMENT_PROBE_PROVIDERS_PROD_MODE
} from 'angular2/platform/browser';
setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS, [TEST_BROWSER_APPLICATION_PROVIDERS, ELEMENT_PROBE_PROVIDERS_PROD_MODE]);

View File

@ -165,7 +165,7 @@ describe('Utils', () => {
info.should.be.an.Object(); info.should.be.an.Object();
info.methods.should.be.an.Array(); info.methods.should.be.an.Array();
for (let methodInfo of info.methods) { for (let methodInfo of info.methods) {
methodInfo.should.have.keys('pointer', 'summary'); methodInfo.should.have.properties(['pointer', 'summary']);
let methSchema = schemaMgr.byPointer(methodInfo.pointer); let methSchema = schemaMgr.byPointer(methodInfo.pointer);
expect(methSchema).not.toBeNull(); expect(methSchema).not.toBeNull();
if (methSchema.summary) { if (methSchema.summary) {