Refactor + minor fixes/optimizations

This commit is contained in:
Roman Hotsiy 2016-10-23 20:18:42 +03:00
parent 76035c84d7
commit 17e7bf9fd8
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
41 changed files with 251 additions and 391 deletions

View File

@ -11,7 +11,7 @@ import {
} from '@angular/core/testing';
import { ApiInfo } from './api-info';
import { SpecManager } from '../../utils/SpecManager';
import { SpecManager } from '../../utils/spec-manager';
describe('Redoc components', () => {
describe('ApiInfo Component', () => {

View File

@ -12,7 +12,7 @@ import { OptionsService, MenuService } from '../../services/index';
export class ApiInfo extends BaseComponent implements OnInit {
info: any = {};
specUrl: String;
constructor(specMgr:SpecManager, private optionsService:OptionsService, private menuServ: MenuService) {
constructor(specMgr:SpecManager, private optionsService: OptionsService) {
super(specMgr);
}

View File

@ -10,7 +10,7 @@ import {
} from '@angular/core/testing';
import { ApiLogo } from './api-logo';
import { SpecManager } from '../../utils/SpecManager';
import { SpecManager } from '../../utils/spec-manager';
describe('Redoc components', () => {

View File

@ -5,7 +5,7 @@ import { Component, ElementRef, ViewContainerRef, OnDestroy, Input,
import { JsonSchema } from './json-schema';
import { OptionsService } from '../../services/options.service';
import { SpecManager } from '../../utils/SpecManager';
import { SpecManager } from '../../utils/spec-manager';
var cache = {};
@ -38,7 +38,7 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit {
var componentFactory = this.resolver.resolveComponentFactory(JsonSchema);
let contextInjector = this.location.parentInjector;
let compRef = this.location.createComponent(componentFactory, null, contextInjector, null);
this.initComponent(compRef.instance);
this.projectComponentInputs(compRef.instance);
this._renderer.setElementAttribute(compRef.location.nativeElement, 'class', this.location.element.nativeElement.className);
compRef.changeDetectorRef.detectChanges();
this.loaded = true;
@ -58,24 +58,22 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit {
this.pointer = this.normalizePointer();
if (cache[this.pointer]) {
let compRef = cache[this.pointer];
setTimeout( ()=> {
let $element = compRef.location.nativeElement;
let $element = compRef.location.nativeElement;
// skip caching view with descendant schemas
// as it needs attached controller
if (!this.disableLazy && (compRef.instance.hasDescendants || compRef.instance._hasSubSchemas)) {
this._loadAfterSelf();
return;
}
insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
this.loaded = true;
});
// skip caching view with descendant schemas
// as it needs attached controller
if (!this.disableLazy && (compRef.instance.hasDescendants || compRef.instance._hasSubSchemas)) {
this._loadAfterSelf();
return;
}
insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
this.loaded = true;
} else {
cache[this.pointer] = this._loadAfterSelf();
}
}
initComponent(instance:JsonSchema) {
projectComponentInputs(instance:JsonSchema) {
Object.assign(instance, this);
}

View File

@ -10,7 +10,7 @@ import { getChildDebugElement } from '../../../tests/helpers';
import { JsonSchema } from './json-schema';
import { SpecManager } from '../../utils/SpecManager';;
import { SpecManager } from '../../utils/spec-manager';;
describe('Redoc components', () => {
beforeEach(() => {

View File

@ -10,7 +10,7 @@ import {
import { getChildDebugElement } from '../../../tests/helpers';
import { Method } from './method';
import { SpecManager } from '../../utils/SpecManager';;
import { SpecManager } from '../../utils/spec-manager';;
describe('Redoc components', () => {
beforeEach(() => {

View File

@ -11,7 +11,7 @@ import { getChildDebugElement } from '../../../tests/helpers';
import { MethodsList } from './methods-list';
import { SpecManager } from '../../utils/SpecManager';
import { SpecManager } from '../../utils/spec-manager';
describe('Redoc components', () => {
beforeEach(() => {

View File

@ -1,4 +1,8 @@
<div class="redoc-wrap" *ngIf="specLoaded">
<div class="redoc-error" *ngIf="error">
<h1>Oops... ReDoc failed to render this spec</h1>
<div class='redoc-error-details'>{{_error.message}}</div>
</div>
<div class="redoc-wrap" *ngIf="specLoaded && !error">
<div class="menu-content" sticky-sidebar [scrollParent]="options.$scrollParent" [scrollYOffset]="options.scrollYOffset">
<api-logo> </api-logo>
<side-menu> </side-menu>

View File

@ -11,7 +11,7 @@ import {
import { TestBed } from '@angular/core/testing';
import { Redoc } from './redoc';
import { SpecManager } from '../../utils/SpecManager';
import { SpecManager } from '../../utils/spec-manager';
import { OptionsService } from '../../services/index';
let optsMgr:OptionsService;
@ -46,123 +46,6 @@ describe('Redoc components', () => {
fixture.destroy();
});
});
// describe('Redoc init', () => {
// let dom = new BrowserDomAdapter();
// let elem;
// beforeEach(() => {
// elem = dom.createElement('redoc');
// dom.defaultDoc().body.appendChild(elem);
// });
//
// afterEach(() => {
// dom.defaultDoc().body.removeChild(elem);
// });
//
// it('should return promise', () => {
// let res = Redoc.init().catch(() => {/**/});
// res.should.be.instanceof(Promise);
// });
//
// it('should hide loading animation and display message in case of error', async(() => {
// spyOn(Redoc, 'hideLoadingAnimation').and.callThrough();
// spyOn(Redoc, 'displayError').and.callThrough();
// let res = Redoc.init();
// return res.catch(() => {
// expect(Redoc.hideLoadingAnimation).toHaveBeenCalled();
// expect(Redoc.displayError).toHaveBeenCalled();
// });
// }));
//
// //skip because of PhantomJS crashes on this testcase
// xit('should init redoc', (done) => {
// var node = document.createElement('redoc');
// document.body.appendChild(node);
// let res = Redoc.init('/tests/schemas/extended-petstore.yml');
// res.then(() => { done(); }, () => {
// done.fail('Error handler should not been called');
// });
// });
// });
//
// describe('Redoc destroy', () => {
// let builder;
// let fixture;
// let element;
// let dom;
// let destroySpy;
//
// beforeEach(async(inject([SpecManager, OptionsService, BrowserDomAdapter],
// ( specMgr, opts, _dom) => {
//
// optsMgr = opts;
// dom = _dom;
// return specMgr.load('/tests/schemas/extended-petstore.yml');
// })));
//
// beforeEach(() => {
// fixture = TestBed.createComponent(TestAppComponent);
// element = getChildDebugElement(fixture.debugElement, 'methods-list').nativeElement;
// destroySpy = jasmine.createSpy('spy');
// Redoc.appRef = <ComponentRef<any>>{
// destroy: destroySpy
// };
// fixture.detectChanges();
// });
//
// afterEach(()=> {
// fixture.destroy();
// Redoc.appRef = null;
// });
//
// it('should call componentRef.destroy', () => {
// Redoc.destroy();
// expect(destroySpy).toHaveBeenCalled();
// });
//
// it('should create new host element', () => {
// element.parentElement.removeChild(element);
// Redoc.destroy();
// expect(dom.query('redoc')).not.toBeNull();
// dom.query('redoc').should.not.be.equal(element);
// });
//
// it('should set to null appRef', () => {
// Redoc.destroy();
// expect(Redoc.appRef).toBeNull();
// });
// });
//
// describe('Redoc autoInit', () => {
// const testURL = 'testurl';
// let dom = new BrowserDomAdapter();
// //let redocInitSpy;
// let elem: HTMLElement;
//
// beforeEach(() => {
// spyOn(Redoc, 'init').and.stub();
// elem = dom.createElement('redoc');
// dom.defaultDoc().body.appendChild(elem);
// dom.setAttribute(elem, 'spec-url', testURL);
// });
//
// it('should call Redoc.init with url from param spec-url', () => {
// Redoc.autoInit();
// expect(Redoc.init).toHaveBeenCalled();
// expect((<jasmine.Spy>Redoc.init).calls.argsFor(0)).toEqual([testURL]);
// });
//
// it('should not call Redoc.init when spec-url param is not provided', () => {
// dom.removeAttribute(elem, 'spec-url');
// Redoc.autoInit();
// expect(Redoc.init).not.toHaveBeenCalled();
// });
//
// afterEach(() => {
// (<jasmine.Spy>Redoc.init).and.callThrough();
// dom.defaultDoc().body.removeChild(elem);
// });
// });
});
/** Test component that contains a Redoc. */

View File

@ -1,15 +1,23 @@
'use strict';
import { ElementRef, ComponentRef, ChangeDetectorRef, Input,
Component, OnInit, ChangeDetectionStrategy} from '@angular/core';
import { ElementRef,
ComponentRef,
ChangeDetectorRef,
Input,
Component,
OnInit,
ChangeDetectionStrategy,
HostBinding
} from '@angular/core';
import { BrowserDomAdapter as DOM } from '../../utils/browser-adapter';
import { BaseComponent } from '../base';
import * as detectScollParent from 'scrollparent';
import { SpecManager } from '../../utils/SpecManager';
import { OptionsService, RedocEventsService } from '../../services/index';
import { SpecManager } from '../../utils/spec-manager';
import { OptionsService, Hash, MenuService, AppStateService } from '../../services/index';
import { CustomErrorHandler } from '../../utils/';
@Component({
selector: 'redoc',
@ -18,45 +26,29 @@ import { OptionsService, RedocEventsService } from '../../services/index';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class Redoc extends BaseComponent implements OnInit {
static appRef: ComponentRef<any>;
static _preOptions: any;
specLoaded: boolean;
public options: any;
private element: any;
error: any;
specLoaded: boolean;
options: any;
@Input() specUrl: string;
@HostBinding('class.loading') specLoading: boolean = false;
@HostBinding('class.loading-remove') specLoadingRemove: boolean = false;
// TODO: refactor in separate component/service
showLoadingAnimation() {
DOM.addClass(this.element, 'loading');
}
hideLoadingAnimation() {
DOM.addClass(this.element, 'loading-remove');
setTimeout(() => {
DOM.removeClass(this.element, 'loading-remove');
DOM.removeClass(this.element, 'loading');
}, 400);
}
static displayError(err, elem?) {
let redocEl = elem || DOM.query('redoc');
if (!redocEl) return;
let heading = 'Oops... ReDoc failed to render this spec';
let details = err.message;
let erroHtml = `<div class="redoc-error">
<h1>${heading}</h1>
<div class='redoc-error-details'>${details}</div>`;
redocEl.innerHTML = erroHtml;
}
constructor(specMgr: SpecManager, optionsMgr:OptionsService, elementRef:ElementRef,
public events:RedocEventsService, private changeDetector: ChangeDetectorRef) {
constructor(
specMgr: SpecManager,
optionsMgr: OptionsService,
elementRef: ElementRef,
private changeDetector: ChangeDetectorRef,
private appState: AppStateService
) {
super(specMgr);
// merge options passed before init
optionsMgr.options = Redoc._preOptions || {};
this.element = elementRef.nativeElement;
//parse options (top level component doesn't support inputs)
optionsMgr.parseOptions( this.element );
@ -66,22 +58,34 @@ export class Redoc extends BaseComponent implements OnInit {
this.options = optionsMgr.options;
}
hideLoadingAnimation() {
this.specLoadingRemove = true;
setTimeout(() => {
this.specLoadingRemove = true;
this.specLoading = false;
}, 400);
}
load() {
this.showLoadingAnimation();
SpecManager.instance().load(this.options.specUrl).then(() => {
this.specLoaded = true;
this.changeDetector.markForCheck();
//this.changeDetector.detectChanges();
this.events.bootstrapped.next({});
this.hideLoadingAnimation();
}).catch((err) => {
this.hideLoadingAnimation();
Redoc.displayError(err, this.element);
throw err;
})
this.specMgr.load(this.options.specUrl);
this.specMgr.spec.subscribe((spec) => {
if (!spec) {
this.specLoading = true;
this.specLoaded = false;
} else {
this.specLoaded = true;
this.hideLoadingAnimation();
this.changeDetector.markForCheck();
}
});
}
ngOnInit() {
this.appState.error.subscribe(_err => {
this.error = _err;
this.changeDetector.detectChanges();
})
if (this.specUrl) {
this.options.specUrl = this.specUrl;
}

View File

@ -1,12 +1,14 @@
'use strict';
import { Component, ViewChildren, QueryList, EventEmitter, Input,
import { Component, ViewChildren, QueryList, Input,
ChangeDetectionStrategy, OnInit, HostBinding } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { BaseComponent, SpecManager } from '../base';
import JsonPointer from '../../utils/JsonPointer';
import { Tabs } from '../../shared/components/index';
import { RedocEventsService } from '../../services/index';
import { AppStateService } from '../../services/index';
@Component({
selector: 'request-samples',
@ -21,18 +23,17 @@ export class RequestSamples extends BaseComponent implements OnInit {
@HostBinding('attr.hidden') hidden;
childTabs: Tabs;
selectedLang: EventEmitter<any>;
selectedLang: Subject<any>;
samples: Array<any>;
constructor(specMgr:SpecManager, public events:RedocEventsService) {
constructor(specMgr:SpecManager, public appState:AppStateService) {
super(specMgr);
this.selectedLang = this.events.samplesLanguageChanged;
this.selectedLang = this.appState.samplesLanguage;
}
changeLangNotify(lang) {
this.events.samplesLanguageChanged.next(lang);
this.selectedLang.next(lang);
}
init() {

View File

@ -0,0 +1,2 @@
<h1> Hello world </h1>
<h2> {{message}} {{pointer}} </h2>

View File

@ -0,0 +1,28 @@
@import '../../shared/styles/variables';
.api-info-header {
font-weight: normal;
}
:host > div {
width: 60%;
padding: 40px;
box-sizing: border-box;
@media (max-width: $right-panel-squash-breakpoint) {
width: 100%;
}
}
a.openapi-button {
padding: 3px 8px 4px 8px;
color: $primary-color;
border: 1px solid $primary-color;
margin-left: 0.5em;
font-weight: normal;
}
:host /deep/ [section] {
padding-top: 60px;
margin-top: 20px;
}

View File

@ -0,0 +1,25 @@
'use strict';
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { SpecManager, BaseComponent } from '../base';
@Component({
selector: 'security-definitions',
styleUrls: ['./security-definitions.css'],
templateUrl: './security-definitions.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SecurityDefinitions extends BaseComponent implements OnInit {
info: any = {};
specUrl: String;
message: string = 'Roman';
constructor(specMgr:SpecManager) {
super(specMgr);
}
init() {
}
ngOnInit() {
this.preinit();
}
}

View File

@ -13,7 +13,7 @@ import { TestBed } from '@angular/core/testing';
import { MethodsList, SideMenu } from '../index';
import { SpecManager } from '../../utils/SpecManager';
import { SpecManager } from '../../utils/spec-manager';
let testOptions;

View File

@ -103,7 +103,6 @@ export class SideMenu extends BaseComponent implements OnInit {
destroy() {
this.scrollService.unbind();
this.hash.unbind();
}
ngOnInit() {

View File

@ -1,6 +1,6 @@
'use strict';
import { SpecManager } from '../utils/SpecManager';
import { SpecManager } from '../utils/spec-manager';
import { BaseComponent } from '../components/base';
describe('Redoc components', () => {

View File

@ -1,6 +1,6 @@
'use strict';
import { OnInit, OnDestroy } from '@angular/core';
import { SpecManager } from '../utils/SpecManager';
import { SpecManager } from '../utils/spec-manager';
export { SpecManager };

View File

@ -13,13 +13,14 @@ import { SideMenu } from './SideMenu/side-menu';
import { MethodsList } from './MethodsList/methods-list';
import { Method } from './Method/method';
import { Warnings } from './Warnings/warnings';
import { SecurityDefinitions } from './SecurityDefinitions/security-definitions';
import { Redoc } from './Redoc/redoc';
export const REDOC_DIRECTIVES = [
ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList,
ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc
ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions
];
export { ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList,
ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc }
ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions }

View File

@ -3,7 +3,7 @@ import './components/Redoc/redoc-initial-styles.css';
import { enableProdMode } from '@angular/core';
import { Redoc } from './components/index';
import { SpecManager } from './utils/SpecManager';
import { SpecManager } from './utils/spec-manager';
import { BrowserDomAdapter as DOM } from './utils/browser-adapter';
import { disableDebugTools } from '@angular/platform-browser';
@ -34,7 +34,7 @@ export function init(specUrl:string, options:any = {}) {
moduleRef = appRef;
console.log('ReDoc initialized!');
}).catch(err => {
Redoc.displayError(err);
//Redoc.displayError(err);
throw err;
});
};

View File

@ -1,26 +1,29 @@
import { NgModule } from '@angular/core';
import { NgModule, ErrorHandler } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Redoc, REDOC_DIRECTIVES } from './components/index';
import { Redoc, SecurityDefinitions, REDOC_DIRECTIVES } from './components/index';
import { REDOC_COMMON_DIRECTIVES } from './shared/components/index';
import { REDOC_PIPES } from './utils/pipes';
import { CustomErrorHandler } from './utils/'
import { OptionsService, RedocEventsService, MenuService,
ScrollService, Hash, WarningsService } from './services/index';
import { SpecManager } from './utils/SpecManager';
import { OptionsService, MenuService,
ScrollService, Hash, WarningsService, AppStateService } from './services/';
import { SpecManager } from './utils/spec-manager';
@NgModule({
imports: [ CommonModule ],
declarations: [ REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES, REDOC_PIPES],
bootstrap: [ Redoc ],
entryComponents: [SecurityDefinitions],
providers: [
SpecManager,
RedocEventsService,
ScrollService,
Hash,
MenuService,
WarningsService,
OptionsService
OptionsService,
AppStateService,
{ provide: ErrorHandler, useClass: CustomErrorHandler }
],
exports: [Redoc]
})

View File

@ -0,0 +1,11 @@
'use strict';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class AppStateService {
samplesLanguage = new Subject<string>();
error = new BehaviorSubject<any>(null);
}

View File

@ -1,8 +1,9 @@
'use strict';
var isSupported = document.queryCommandSupported && document.queryCommandSupported('copy');
export class Clipboard {
static isSupported():boolean {
return document.queryCommandSupported && document.queryCommandSupported('copy');
return isSupported;
}
static selectElement(element:any):void {

View File

@ -1,8 +0,0 @@
'use strict';
import { EventEmitter, Output } from '@angular/core';
export class RedocEventsService {
@Output() bootstrapped = new EventEmitter();
@Output() samplesLanguageChanged = new EventEmitter();
}

View File

@ -1,25 +1,22 @@
'use strict';
import {
inject
} from '@angular/core/testing';
import { RedocEventsService } from './events.service';
import { Hash } from './hash.service';
import { SpecManager } from '../utils/spec-manager';
describe('Hash Service', () => {
let events = new RedocEventsService();
let specMgr = new SpecManager();
let hashService;
beforeEach(() => {
hashService = new Hash(events);
});
afterEach(() => {
hashService.unbind();
});
beforeEach(inject([Hash], (_hash) => hashService = _hash));
it('should trigger changed event after ReDoc bootstrapped', (done) => {
spyOn(hashService.changed, 'next').and.callThrough();
events.bootstrapped.next({});
spyOn(hashService.value, 'next').and.callThrough();
specMgr.spec.next({});
setTimeout(() => {
expect(hashService.changed.next).toHaveBeenCalled();
expect(hashService.value.next).toHaveBeenCalled();
done();
});
});

View File

@ -1,32 +1,31 @@
'use strict';
import { Injectable, EventEmitter, Output } from '@angular/core';
import { BrowserDomAdapter as DOM } from '../utils/browser-adapter';
import { global } from '@angular/core/src/facade/lang';
import { Injectable } from '@angular/core';
import { PlatformLocation } from '@angular/common';
import { RedocEventsService } from './events.service';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { SpecManager } from '../utils/spec-manager';
@Injectable()
export class Hash {
@Output() changed = new EventEmitter();
private _cancel: any;
constructor(private events:RedocEventsService) {
public value = new BehaviorSubject<string>('');
constructor(private specMgr: SpecManager, private location: PlatformLocation) {
this.bind();
events.bootstrapped.subscribe(() => this.changed.next(this.hash));
}
get hash() {
return DOM.getLocation().hash;
}
bind() {
this._cancel = DOM.onAndCancel(global, 'hashchange', (evt) => {
this.changed.next(this.hash);
evt.preventDefault();
this.specMgr.spec.subscribe((spec) => {
if (!spec) return;
setTimeout(() => {
this.value.next(this.hash);
});
});
}
unbind() {
this._cancel();
get hash() {
return this.location.hash;
}
bind() {
this.location.onHashChange(() => {
this.value.next(this.hash);
});
}
}

View File

@ -1,6 +1,6 @@
'use strict';
export * from './events.service';
export * from './app-state.service';
export * from './options.service';
export * from './menu.service';
export * from './scroll.service';

View File

@ -9,7 +9,7 @@ import {
import { MenuService } from './menu.service';
import { Hash } from './hash.service';
import { ScrollService } from './scroll.service';
import { SpecManager } from '../utils/SpecManager';;
import { SpecManager } from '../utils/spec-manager';;
describe('Menu service', () => {
beforeEach(() => {
@ -36,39 +36,42 @@ describe('Menu service', () => {
it('should run hashScroll when hash changed', (done) => {
spyOn(menu, 'hashScroll').and.callThrough();
hashService.changed.subscribe(() => {
hashService.value.subscribe((hash) => {
if (!hash) return;
expect(menu.hashScroll).toHaveBeenCalled();
menu.hashScroll.and.callThrough();
done();
});
hashService.changed.next();
hashService.value.next('nonFalsy');
});
it('should scroll to method when location hash is present [jp]', (done) => {
let hash = '#tag/pet/paths/~1pet~1findByStatus/get';
spyOn(menu, 'hashScroll').and.callThrough();
spyOn(window, 'scrollTo').and.stub();
hashService.changed.subscribe(() => {
hashService.value.subscribe((hash) => {
if (!hash) return;
expect(menu.hashScroll).toHaveBeenCalled();
let scrollY = (<jasmine.Spy>window.scrollTo).calls.argsFor(0)[1];
expect(scrollY).toBeGreaterThan(0);
(<jasmine.Spy>window.scrollTo).and.callThrough();
done();
});
hashService.changed.next(hash);
hashService.value.next(hash);
});
it('should scroll to method when location hash is present [operation]', (done) => {
let hash = '#operation/getPetById';
spyOn(menu, 'hashScroll').and.callThrough();
spyOn(window, 'scrollTo').and.stub();
hashService.changed.subscribe(() => {
hashService.value.subscribe((hash) => {
if (!hash) return;
expect(menu.hashScroll).toHaveBeenCalled();
let scrollY = (<jasmine.Spy>window.scrollTo).calls.argsFor(0)[1];
expect(scrollY).toBeGreaterThan(0);
done();
});
hashService.changed.next(hash);
hashService.value.next(hash);
});
it('should select next/prev menu item when scrolled down/up', () => {

View File

@ -2,7 +2,7 @@
import { Injectable, EventEmitter } from '@angular/core';
import { ScrollService, INVIEW_POSITION } from './scroll.service';
import { Hash } from './hash.service';
import { SpecManager } from '../utils/SpecManager';
import { SpecManager } from '../utils/spec-manager';
import { SchemaHelper, MenuCategory } from './schema-helper.service';
const CHANGE = {
@ -30,7 +30,7 @@ export class MenuService {
this.changeActive(CHANGE.INITIAL);
this.hash.changed.subscribe((hash) => {
this.hash.value.subscribe((hash) => {
this.hashScroll(hash);
});
}

View File

@ -1,6 +1,6 @@
'use strict';
import { SchemaHelper } from './schema-helper.service';
import { SpecManager } from '../utils/SpecManager';
import { SpecManager } from '../utils/spec-manager';
describe('Spec Helper', () => {
describe('buildMenuTree method', () => {

View File

@ -1,6 +1,6 @@
'use strict';
import { JsonPointer } from '../utils/JsonPointer';
import { SpecManager } from '../utils/SpecManager';
import { SpecManager } from '../utils/spec-manager';
import {methods as swaggerMethods, keywordTypes} from '../utils/swagger-defs';
import { WarningsService } from './warnings.service';
import * as slugify from 'slugify';

View File

@ -1,6 +1,6 @@
'use strict';
import { SchemaNormalizer } from './schema-normalizer.service';
import { SpecManager } from '../utils/SpecManager';;
import { SpecManager } from '../utils/spec-manager';;
describe('Spec Helper', () => {
let specMgr:SpecManager = new SpecManager();

View File

@ -1,6 +1,6 @@
'use strict';
import { Injectable } from '@angular/core';
import { SpecManager } from '../utils/SpecManager';
import { SpecManager } from '../utils/spec-manager';
import { JsonPointer } from '../utils/JsonPointer';
import { defaults } from '../utils/helpers';
import { WarningsService } from './warnings.service';

View File

@ -18,7 +18,6 @@ export class ScrollService {
private prevOffsetY: number;
private _cancel:any;
constructor(optionsService:OptionsService) {
//events.bootstrapped.subscribe(() => this.hashScroll());
this.scrollYOffset = () => optionsService.options.scrollYOffset();
this.$scrollParent = optionsService.options.$scrollParent;
this.scroll = new EventEmitter();
@ -31,11 +30,12 @@ export class ScrollService {
/* returns 1 if element if above the view, 0 if in view and -1 below the view */
getElementPos($el) {
if (Math.floor($el.getBoundingClientRect().top) > this.scrollYOffset()) {
let scrollYOffset = this.scrollYOffset();
if (Math.floor($el.getBoundingClientRect().top) > scrollYOffset) {
return INVIEW_POSITION.ABOVE;
}
if ($el.getBoundingClientRect().bottom <= this.scrollYOffset()) {
if ($el.getBoundingClientRect().bottom <= scrollYOffset) {
return INVIEW_POSITION.BELLOW;
}
return INVIEW_POSITION.INVIEW;

View File

@ -1,10 +0,0 @@
/*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

@ -0,0 +1,12 @@
import { ErrorHandler, Injectable } from '@angular/core';
import { AppStateService } from '../services/app-state.service';
@Injectable()
export class CustomErrorHandler implements ErrorHandler {
constructor(private appState: AppStateService) {
}
handleError(error) {
console.log(error);
this.appState.error.next(error);
}
}

View File

@ -1,103 +0,0 @@
'use strict';
var Remarkable = require('remarkable');
var md = new Remarkable({
html: true,
linkify: true,
breaks: false,
typographer: false,
highlight: function (str, lang) {
if (lang === 'json')
lang = 'js';
var grammar = Prism.languages[lang];
if (!grammar)
return str;
return Prism.highlight(str, grammar);
}
});
function renderMd(rawText, headersHandler) {
var _origRule;
if (headersHandler) {
_origRule = {
open: md.renderer.rules.heading_open,
close: md.renderer.rules.heading_close
};
md.renderer.rules.heading_open = function (tokens, idx) {
if (tokens[idx].hLevel !== 1) {
return _origRule.open(tokens, idx);
}
else {
return headersHandler.open(tokens, idx);
}
};
md.renderer.rules.heading_close = function (tokens, idx) {
if (tokens[idx].hLevel !== 1) {
return _origRule.close(tokens, idx);
}
else {
return headersHandler.close(tokens, idx);
}
};
}
var res = md.render(rawText);
if (headersHandler) {
md.renderer.rules.heading_open = _origRule.open;
md.renderer.rules.heading_close = _origRule.close;
}
return res;
}
exports.renderMd = renderMd;
function statusCodeType(statusCode) {
if (statusCode < 100 || statusCode > 599) {
throw new Error('invalid HTTP code');
}
var res = 'success';
if (statusCode >= 300 && statusCode < 400) {
res = 'redirect';
}
else if (statusCode >= 400) {
res = 'error';
}
else if (statusCode < 200) {
res = 'info';
}
return res;
}
exports.statusCodeType = statusCodeType;
function defaults(target, src) {
var props = Object.keys(src);
var index = -1, length = props.length;
while (++index < length) {
var key = props[index];
if (target[key] === undefined) {
target[key] = src[key];
}
}
return target;
}
exports.defaults = defaults;
function safePush(obj, prop, val) {
if (!obj[prop])
obj[prop] = [];
obj[prop].push(val);
}
exports.safePush = safePush;
function throttle(fn, threshhold, scope) {
threshhold = threshhold || 250;
var last, deferTimer;
return function () {
var context = scope || this;
var now = +new Date, args = arguments;
if (last && now < last + threshhold) {
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
}
else {
last = now;
fn.apply(context, args);
}
};
}
exports.throttle = throttle;

1
lib/utils/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './custom-error-handler';

View File

@ -5,10 +5,13 @@ import { JsonPointer } from './JsonPointer';
import { renderMd, safePush } from './helpers';
import * as slugify from 'slugify';
import { parse as urlParse, resolve as urlResolve } from 'url';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export class SpecManager {
public _schema: any = {};
public apiUrl: string;
public spec = new BehaviorSubject<any|null>(null);
private _instance: any;
private _url: string;
@ -24,17 +27,19 @@ export class SpecManager {
SpecManager.prototype._instance = this;
}
load(url) {
load(urlOrObject: string|Object) {
this.schema = null;
let promise = new Promise((resolve, reject) => {
this._schema = {};
JsonSchemaRefParser.bundle(url, {http: {withCredentials: false}})
JsonSchemaRefParser.bundle(urlOrObject, {http: {withCredentials: false}})
.then(schema => {
if (typeof urlOrObject === 'string') {
this._url = urlOrObject;
}
this._schema = schema;
try {
this._url = url;
this._schema = schema;
this.init();
resolve(this._schema);
this.spec.next(this._schema);
} catch(err) {
reject(err);
}
@ -88,6 +93,11 @@ export class SpecManager {
return this._schema;
}
set schema(val:any) {
this._schema = val;
this.spec.next(this._schema);
}
byPointer(pointer) {
let res = null;
try {

View File

@ -19,11 +19,11 @@ require('zone.js/dist/jasmine-patch');
require('../lib/vendor');
var TestBed = require('@angular/core/testing').TestBed;
var ErrorHandler = require('@angular/core').ErrorHandler;
var BrowserDynamicTestingModule = require('@angular/platform-browser-dynamic/testing').BrowserDynamicTestingModule;
var platformBrowserDynamicTesting = require('@angular/platform-browser-dynamic/testing').platformBrowserDynamicTesting;
var services = require('../lib/services/index');
var SpecManager = require('../lib/utils/SpecManager').SpecManager;
var BrowserDomAdapter = require('@angular/platform-browser/src/browser/browser_adapter').BrowserDomAdapter;
var SpecManager = require('../lib/utils/spec-manager').SpecManager;
var REDOC_PIPES = require('../lib/utils/pipes').REDOC_PIPES;
var REDOC_COMMON_DIRECTIVES = require('../lib/shared/components/index').REDOC_COMMON_DIRECTIVES;
var REDOC_DIRECTIVES = require('../lib/components/index').REDOC_DIRECTIVES;
@ -36,15 +36,14 @@ TestBed.initTestEnvironment(
beforeEach(function() {
TestBed.configureTestingModule({
providers: [
BrowserDomAdapter,
SpecManager,
BrowserDomAdapter,
services.RedocEventsService,
services.AppStateService,
services.ScrollService,
services.Hash,
services.MenuService,
services.WarningsService,
services.OptionsService
services.OptionsService,
{ provide: ErrorHandler, useClass: services.CustomErrorHandler }
],
declarations: [REDOC_PIPES, REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES]
});

View File

@ -1,6 +1,6 @@
'use strict';
import { SpecManager } from '../../lib/utils/SpecManager';
import { SpecManager } from '../../lib/utils/spec-manager';
describe('Utils', () => {
describe('Schema manager', () => {
let specMgr;