mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-10-31 15:57:30 +03:00 
			
		
		
		
	Merge commit '937c2a10baef9d60da56d4ca4df390475b37e842' into releases
This commit is contained in:
		
						commit
						4ef3e33854
					
				
							
								
								
									
										85
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										85
									
								
								CHANGELOG.md
									
									
									
									
									
								
							|  | @ -1,35 +1,76 @@ | |||
| # 1.5.2 (2016-11-28) | ||||
| ### Bug fixes | ||||
| * Fix crashing on array without items ([#104](https://github.com/Rebilly/ReDoc/issues/104)) | ||||
| * Fix `allOf` within array items ([#136](https://github.com/Rebilly/ReDoc/issues/136)) | ||||
| * Fix reference resolution from external files ([#96](https://github.com/Rebilly/ReDoc/issues/96)) | ||||
| * Fix object to become an array ([#146](https://github.com/Rebilly/ReDoc/issues/146)) | ||||
| 
 | ||||
| ### Features/Improvements | ||||
| * Add support for Swagger `collectionFormat` | ||||
| * Wrap API version in span with class ([#145](https://github.com/Rebilly/ReDoc/issues/145)) | ||||
| * Update openapi-sampler to 0.3.3 | ||||
| 
 | ||||
| # 1.5.1 (2016-10-31) | ||||
| ### Bug fixes | ||||
| * Fix content scrolling on language switch ([#130](https://github.com/Rebilly/ReDoc/issues/130)) | ||||
| 
 | ||||
| ### Features/Improvements | ||||
| * Support for Swagger `pattern` property ([#42](https://github.com/Rebilly/ReDoc/issues/42)) | ||||
| * Add option to hide hostname in method definition (by @bfirsh) | ||||
| * Add Docker development environment (by @bfirsh) | ||||
| 
 | ||||
| # 1.5.0 (2016-10-31) | ||||
| ### Bug fixes | ||||
| * Fix side menu items wrong sync with description headers | ||||
| 
 | ||||
| ### Features/Improvements | ||||
| * Support for Security Definitions | ||||
| * Update angular2 to the 2.1.2 | ||||
| 
 | ||||
| ### Deprecations | ||||
| * Deprecate `x-traitTag` | ||||
| 
 | ||||
| ### Code refactoring | ||||
| * Separate RedocModule from AppModule | ||||
| * Get rid of angular facade/lang dependencies | ||||
| * Error handler refactor | ||||
| 
 | ||||
| # 1.4.1 (2016-10-18) | ||||
| ### Bug fixes | ||||
| * Emit helpers for module build | ||||
| 
 | ||||
| # 1.4.0 (2016-10-14) | ||||
| ### Bug fixes | ||||
| * Fix destroy/reinit | ||||
| * Fix minimum/maximum zero not rendered (#123) | ||||
| * Fix minimum/maximum zero not rendered ([#123](https://github.com/Rebilly/ReDoc/issues/123)) | ||||
| 
 | ||||
| ### Features/Improvements | ||||
| * Do spec load after bootstrap | ||||
| * Build and publish angular2 module (#126) | ||||
| * Build and publish angular2 module ([#126](https://github.com/Rebilly/ReDoc/issues/126)) | ||||
| 
 | ||||
| # 1.3.3 (2016-09-28) | ||||
| ### Features/Improvements | ||||
| * implemented x-extendedDiscriminator to workaround name clashes in big specs | ||||
| * Add engines to package.json (#83) | ||||
| * Fix npm start on windows (#119, #118) | ||||
| * Implemented x-extendedDiscriminator to workaround name clashes in big specs | ||||
| * Add engines to package.json ([#83](https://github.com/Rebilly/ReDoc/issues/83)) | ||||
| * Fix npm start on windows ([#119](https://github.com/Rebilly/ReDoc/issues/119), [#118](https://github.com/Rebilly/ReDoc/issues/118)) | ||||
| * Update webpack to latest beta | ||||
| * Update angular to 2.0.1 | ||||
| * Update local dev steps | ||||
| * Update openapi-sampler lib (#111) | ||||
| * Update openapi-sampler lib ([#111](https://github.com/Rebilly/ReDoc/issues/111)) | ||||
| 
 | ||||
| # 1.3.2 (2016-09-13) | ||||
| ### Bug fixes | ||||
| * Fix broken tabs styling for response samples | ||||
| * fix v1.x.x deployment | ||||
| * Fix v1.x.x deployment | ||||
| 
 | ||||
| # 1.3.1 (2016-09-13) | ||||
| ### Bug fixes | ||||
| * makes basePath optional (by @LeFnord) | ||||
| * fixed little typo (by @adamd) | ||||
| * Makes basePath optional (by @LeFnord) | ||||
| * Fixed little typo (by @adamd) | ||||
| * Typo s/IGNORRED/IGNORED (by @MikeRalphson) | ||||
| * Fixed indentation (by @bennyn) | ||||
| * Fix default hostname (#108) | ||||
| * Fix default value for falsy values is not displayed (#109) | ||||
| * Fix default hostname ([#108](https://github.com/Rebilly/ReDoc/issues/108)) | ||||
| * Fix default value for falsy values is not displayed ([#109](https://github.com/Rebilly/ReDoc/issues/109)) | ||||
| * Fix schema collapse after change discriminator | ||||
| 
 | ||||
| ### Features/Improvements | ||||
|  | @ -39,24 +80,24 @@ | |||
| 
 | ||||
| # 1.3.0 (2016-08-31) | ||||
| ### Bug fixes | ||||
| * Fix code samples are not shown for operations without body param (#93) | ||||
| * Fixed side menu overlapped site footer (#75) | ||||
| * Fix code samples are not shown for operations without body param ([#93](https://github.com/Rebilly/ReDoc/issues/93)) | ||||
| * Fixed side menu overlapped site footer ([#75](https://github.com/Rebilly/ReDoc/issues/75)) | ||||
| * Fix broken order in discriminator dropdown | ||||
| 
 | ||||
| ### Features/Improvements | ||||
| * Support "x-nullable" property by @kedashoe (#92) | ||||
| * Support "x-nullable" property by @kedashoe ([#92](https://github.com/Rebilly/ReDoc/issues/92)) | ||||
| 
 | ||||
| # 1.2.0 (2016-08-30) | ||||
| ### Bug fixes | ||||
| * Fix sticky sidebar top sticking (#75) | ||||
| * Fix array inside objects if referenced directly (#84) | ||||
| * Add banner to the bundle file (#89) | ||||
| * Fix sticky sidebar top sticking ([#75](https://github.com/Rebilly/ReDoc/issues/75)) | ||||
| * Fix array inside objects if referenced directly ([#84](https://github.com/Rebilly/ReDoc/issues/84)) | ||||
| * Add banner to the bundle file ([#89](https://github.com/Rebilly/ReDoc/issues/89)) | ||||
| * Fix broken additionalProperties | ||||
| * Fix version render issue (extra "v" letter) | ||||
| 
 | ||||
| ### Features/Improvements | ||||
| * Change the way discriminator is rendered | ||||
| * Created CDN major release 1.x.x (#87) | ||||
| * Created CDN major release 1.x.x ([#87](https://github.com/Rebilly/ReDoc/issues/87)) | ||||
| * Smaller bundle size (371KB gzipped) | ||||
| * Better start-up time due to [AoT](http://blog.mgechev.com/2016/08/14/ahead-of-time-compilation-angular-offline-precompilation/) | ||||
| 
 | ||||
|  | @ -67,12 +108,12 @@ | |||
| 
 | ||||
| # 1.1.2 (2016-08-21) | ||||
| ### Bug fixes | ||||
| * Revert "Fix markdown newlines to be GFM" (#82) | ||||
| * Revert "Fix markdown newlines to be GFM" ([#82](https://github.com/Rebilly/ReDoc/issues/82)) | ||||
| * Move license and contact info above description | ||||
| 
 | ||||
| # 1.1.1 (2016-08-21) | ||||
| ### Bug fixes | ||||
| * Fix markdown newlines to be GFM (#82) | ||||
| * Fix markdown newlines to be GFM ([#82](https://github.com/Rebilly/ReDoc/issues/82)) | ||||
| * Fix markdown code blocks in api description | ||||
| 
 | ||||
| # 1.1.0 (2016-08-12) | ||||
|  | @ -83,8 +124,8 @@ | |||
| 
 | ||||
| ### Features/Improvements | ||||
| 
 | ||||
| * Add Tuple support (arrays with separate schema for each value) (#69) | ||||
| * Add special representation for enum with one value (#70) | ||||
| * Add Tuple support (arrays with separate schema for each value) ([#69](https://github.com/Rebilly/ReDoc/issues/69)) | ||||
| * Add special representation for enum with one value ([#70](https://github.com/Rebilly/ReDoc/issues/70)) | ||||
| * Change `< * >` notation to `< anything >` | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,6 +13,8 @@ | |||
| 
 | ||||
| ## [Live demo](http://rebilly.github.io/ReDoc/) | ||||
| 
 | ||||
| [<img src="http://i.imgur.com/XWLWJvl.png" width="250">](https://github.com/Rebilly/generator-openapi-repo#generator-openapi-repo--) | ||||
| 
 | ||||
| ## Features | ||||
| - Extremely easy deployment | ||||
| - It’s free and open-source project under MIT license | ||||
|  | @ -114,6 +116,7 @@ ReDoc makes use of the following [vendor extensions](http://swagger.io/specifica | |||
|   * **selector**: selector of the element to be used for specifying the offset. The distance from the top of the page to the element's bottom will be used as offset; | ||||
|   * **function**: A getter function. Must return a number representing the offset (in pixels); | ||||
| * `suppress-warnings` - if set, warnings are not rendered at the top of documentation (they still are logged to the console). | ||||
| * `lazy-rendering` - if set, enables lazy rendering mode in ReDoc. This mode is useful for APIs with big number of operations (e.g. > 50). In this mode ReDoc shows initial screen ASAP and then renders the rest operations asynchronously while showing progress bar on the top. Check out the [demo](\\rebilly.github.io/ReDoc) for the example. | ||||
| * `hide-hostname` - if set, the protocol and hostname is not shown in the method definition. | ||||
| 
 | ||||
| ## Advanced usage | ||||
|  |  | |||
|  | @ -37,7 +37,9 @@ module.exports = { | |||
|   }, | ||||
| 
 | ||||
|   devServer: { | ||||
|     outputPath: root('dist'), | ||||
|     contentBase: root('demo'), | ||||
|     watchContentBase: true, | ||||
|     compress: true, | ||||
|     watchOptions: { | ||||
|       poll: true | ||||
|     }, | ||||
|  | @ -95,15 +97,15 @@ module.exports = { | |||
|       exclude: [/\.(spec|e2e)\.ts$/] | ||||
|     }, { | ||||
|       test: /lib[\\\/].*\.scss$/, | ||||
|       loaders: ['raw-loader', "sass"], | ||||
|       loaders: ['raw-loader', "sass-loader"], | ||||
|       exclude: [/redoc-initial-styles\.scss$/] | ||||
|     }, { | ||||
|       test: /\.scss$/, | ||||
|       loaders: ['style', 'css?-import', "sass"], | ||||
|       loaders: ['style-loader', 'css-loader?-import', "sass-loader"], | ||||
|       exclude: [/lib[\\\/](?!.*redoc-initial-styles).*\.scss$/] | ||||
|     }, { | ||||
|       test: /\.css$/, | ||||
|       loaders: ['style', 'css?-import'], | ||||
|       loaders: ['style-loader', 'css-loader?-import'], | ||||
|     }, { | ||||
|       test: /\.html$/, | ||||
|       loader: 'raw-loader' | ||||
|  |  | |||
|  | @ -70,7 +70,7 @@ const config = { | |||
|       exclude: [/redoc-initial-styles\.css$/] | ||||
|     }, { | ||||
|       test: /\.css$/, | ||||
|       loaders: ['style', 'css?-import'], | ||||
|       loaders: ['style-loader', 'css-loader?-import'], | ||||
|       exclude: [/lib[\\\/](?!.*redoc-initial-styles).*\.css$/] | ||||
|     }] | ||||
|   }, | ||||
|  |  | |||
|  | @ -61,7 +61,7 @@ module.exports = { | |||
|       exclude: [/redoc-initial-styles\.css$/] | ||||
|     }, { | ||||
|       test: /\.css$/, | ||||
|       loaders: ['style', 'css?-import'], | ||||
|       loaders: ['style-loader', 'css-loader?-import'], | ||||
|       exclude: [/lib[\\\/](?!.*redoc-initial-styles).*\.css$/] | ||||
|     }, { | ||||
|       test: /\.html$/, | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ | |||
|       frameborder="0" scrolling="0" width="130px" height="30px"></iframe> | ||||
|     </nav> | ||||
| 
 | ||||
|     <redoc scroll-y-offset="body > nav" spec-url='swagger.yaml'></redoc> | ||||
|     <redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering></redoc> | ||||
| 
 | ||||
|     <script src="main.js"> </script> | ||||
|     <script src="./dist/redoc.min.js"> </script> | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ | |||
|       frameborder="0" scrolling="0" width="130px" height="30px"></iframe> | ||||
|     </nav> | ||||
| 
 | ||||
|     <redoc scroll-y-offset="body > nav" spec-url='swagger.yaml'></redoc> | ||||
|     <redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering></redoc> | ||||
| 
 | ||||
|     <script src="main.js"> </script> | ||||
|     <script src="/webpack-dev-server.js"></script> | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
|   var url = window.location.search.match(/url=([^&]+)/); | ||||
|   if (url && url.length > 1) { | ||||
|     url = decodeURIComponent(url[1]); | ||||
|     document.getElementsByTagName('redoc')[0].setAttribute('spec-url', url); | ||||
|     document.getElementsByTagName('redoc')[0].setAttribute('spec-url', '\\\\cors.apis.guru/' + url); | ||||
|   } | ||||
| 
 | ||||
|   function updateQueryStringParameter(uri, key, value) { | ||||
|  |  | |||
|  | @ -18,23 +18,28 @@ describe('Redoc components', () => { | |||
|     let component; | ||||
|     let fixture; | ||||
|     let opts; | ||||
|     let specMgr; | ||||
|     beforeEach(() => { | ||||
|       TestBed.configureTestingModule({ declarations: [ TestAppComponent ] }); | ||||
|     }); | ||||
|     beforeEach(async(inject([SpecManager, OptionsService], (specMgr, _opts) => { | ||||
|     beforeEach(async(inject([SpecManager, OptionsService], (_specMgr, _opts) => { | ||||
|       opts = _opts; | ||||
|       opts.options = { | ||||
|         scrollYOffset: () => 0, | ||||
|         $scrollParent: window | ||||
|       }; | ||||
|       return specMgr.load('/tests/schemas/api-info-test.json'); | ||||
|       specMgr = _specMgr; | ||||
|     }))); | ||||
| 
 | ||||
|     beforeEach(() => { | ||||
|     beforeEach(done => { | ||||
|       specMgr.load('/tests/schemas/api-info-test.json').then(done, done.fail); | ||||
|     }); | ||||
| 
 | ||||
|     beforeEach(async(() => { | ||||
|       fixture = TestBed.createComponent(TestAppComponent); | ||||
|       component = getChildDebugElement(fixture.debugElement, 'api-info').componentInstance; | ||||
|       fixture.detectChanges(); | ||||
|     }); | ||||
|     })); | ||||
| 
 | ||||
| 
 | ||||
|     it('should init component data', () => { | ||||
|  |  | |||
|  | @ -24,10 +24,15 @@ describe('Redoc components', () => { | |||
|     beforeEach(() => { | ||||
|       TestBed.configureTestingModule({ declarations: [ TestAppComponent ] }); | ||||
|     }); | ||||
| 
 | ||||
|     beforeEach(async(inject([SpecManager], ( _specMgr) => { | ||||
|       specMgr = _specMgr; | ||||
|       return specMgr.load(schemaUrl); | ||||
|     }))); | ||||
| 
 | ||||
|     beforeEach(done => { | ||||
|       specMgr.load(schemaUrl).then(done, done.fail); | ||||
|     }); | ||||
| 
 | ||||
|     beforeEach(() => { | ||||
|       fixture = TestBed.createComponent(TestAppComponent); | ||||
|       component = getChildDebugElement(fixture.debugElement, 'api-logo').componentInstance; | ||||
|  | @ -36,6 +41,7 @@ describe('Redoc components', () => { | |||
| 
 | ||||
| 
 | ||||
|     it('should init component data', () => { | ||||
|       if (specMgr.a) return; | ||||
|       expect(component).not.toBeNull(); | ||||
|       expect(component.logo).not.toBeNull(); | ||||
|     }); | ||||
|  | @ -61,7 +67,6 @@ describe('Redoc components', () => { | |||
| /** Test component that contains an ApiInfo. */ | ||||
| @Component({ | ||||
|   selector: 'test-app', | ||||
|   providers: [SpecManager], | ||||
|   template: | ||||
|       `<api-logo></api-logo>` | ||||
| }) | ||||
|  |  | |||
|  | @ -27,6 +27,10 @@ $sub-schema-offset: ($bullet-size / 2) + $bullet-margin; | |||
|   width: 75%; | ||||
| 
 | ||||
|   box-sizing: border-box; | ||||
| 
 | ||||
|   > div { | ||||
|     line-height: 1; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .param-range { | ||||
|  | @ -41,7 +45,7 @@ $sub-schema-offset: ($bullet-size / 2) + $bullet-margin; | |||
| } | ||||
| 
 | ||||
| .param-description { | ||||
|   font-size: 13px; | ||||
|   //font-size: 14px; | ||||
| } | ||||
| 
 | ||||
| .param-required { | ||||
|  | @ -220,7 +224,6 @@ $sub-schema-offset: ($bullet-size / 2) + $bullet-margin; | |||
|     margin: 0 3px; | ||||
|     font-size: 1.2em; | ||||
|     font-weight: bold; | ||||
|     vertical-align: bottom; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,7 +12,8 @@ var cache = {}; | |||
| @Component({ | ||||
|   selector: 'json-schema-lazy', | ||||
|   entryComponents: [ JsonSchema ], | ||||
|   template: '' | ||||
|   template: '', | ||||
|   styles: [':host { display:none }'] | ||||
| }) | ||||
| export class JsonSchemaLazy implements OnDestroy, AfterViewInit { | ||||
|   @Input() pointer: string; | ||||
|  | @ -66,7 +67,7 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit { | |||
|         this._loadAfterSelf(); | ||||
|         return; | ||||
|       } | ||||
|       insertAfter($element.cloneNode(true), this.elementRef.nativeElement); | ||||
|       //insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
 | ||||
|       this.loaded = true; | ||||
|     } else { | ||||
|       cache[this.pointer] = this._loadAfterSelf(); | ||||
|  |  | |||
|  | @ -62,7 +62,6 @@ describe('Redoc components', () => { | |||
| /** Test component that contains a Method. */ | ||||
| @Component({ | ||||
|   selector: 'test-app', | ||||
|   providers: [SpecManager], | ||||
|   template: | ||||
|       `<json-schema></json-schema>` | ||||
| }) | ||||
|  |  | |||
							
								
								
									
										49
									
								
								lib/components/LoadingBar/loading-bar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lib/components/LoadingBar/loading-bar.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| 'use strict'; | ||||
| import { Input, HostBinding, Component, OnInit, ChangeDetectionStrategy, ElementRef, ChangeDetectorRef } from '@angular/core'; | ||||
| import JsonPointer from '../../utils/JsonPointer'; | ||||
| import { BaseComponent, SpecManager } from '../base'; | ||||
| import { SchemaHelper } from '../../services/schema-helper.service'; | ||||
| import { OptionsService, AppStateService } from '../../services/'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'loading-bar', | ||||
|   template: ` | ||||
|   <span [style.width]='progress + "%"'> </span> | ||||
|   `,
 | ||||
|   styles: [` | ||||
|     :host { | ||||
|       position: fixed; | ||||
|       top: 0; | ||||
|       left: 0; | ||||
|       right: 0; | ||||
|       display: block; | ||||
| 
 | ||||
|       height: 5px; | ||||
|       z-index: 100; | ||||
|     } | ||||
| 
 | ||||
|     span { | ||||
|       display: block; | ||||
|       position: absolute; | ||||
|       left: 0; | ||||
|       top: 0; | ||||
|       bottom: 0; | ||||
|       right: attr(progress percentage); | ||||
|       background-color: #5f7fc3; | ||||
|       transition: right 0.2s linear; | ||||
|     } | ||||
|   `],
 | ||||
|   //changeDetection: ChangeDetectionStrategy.OnPush
 | ||||
| }) | ||||
| export class LoadingBar { | ||||
|   @Input() progress:number = 0; | ||||
|   @HostBinding('style.display') display = 'block'; | ||||
| 
 | ||||
|   ngOnChanges(ch) { | ||||
|     if (ch.progress.currentValue === 100) { | ||||
|       setTimeout(() => { | ||||
|         this.display = 'none'; | ||||
|       }, 500); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -1,18 +1,18 @@ | |||
| <div class="method"> | ||||
|     <div class="method-content"> | ||||
|         <h2 class="method-header sharable-header"> | ||||
|             <a class="share-link" href="#{{method.anchor}}"></a>{{method.summary}} | ||||
|         </h2> | ||||
|         <div class="method-tags" *ngIf="method.info.tags.length"> | ||||
|             <a *ngFor="let tag of method.info.tags" attr.href="#tag/{{tag}}"> {{tag}} </a> | ||||
|         </div> | ||||
|         <p *ngIf="method.info.description" class="method-description" | ||||
|         [innerHtml]="method.info.description | marked"> | ||||
| <div class="method" *ngIf="method"> | ||||
|   <div class="method-content"> | ||||
|     <h2 class="method-header sharable-header"> | ||||
|         <a class="share-link" href="#{{method.anchor}}"></a>{{method.summary}} | ||||
|     </h2> | ||||
|     <div class="method-tags" *ngIf="method.info.tags.length"> | ||||
|         <a *ngFor="let tag of method.info.tags" attr.href="#tag/{{tag}}"> {{tag}} </a> | ||||
|     </div> | ||||
|     <p *ngIf="method.info.description" class="method-description" | ||||
|     [innerHtml]="method.info.description | marked"> | ||||
|     </p> | ||||
|     <params-list pointer="{{pointer}}/parameters"> </params-list> | ||||
|     <responses-list pointer="{{pointer}}/responses"> </responses-list> | ||||
| </div> | ||||
| <div class="method-samples"> | ||||
|   </div> | ||||
|   <div class="method-samples"> | ||||
|     <h4 class="method-params-subheader">Definition</h4> | ||||
| 
 | ||||
|     <div class="method-endpoint"> | ||||
|  | @ -30,5 +30,5 @@ | |||
|         <br> | ||||
|         <responses-samples pointer="{{pointer}}/responses"> </responses-samples> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| <div> | ||||
|  |  | |||
|  | @ -6,18 +6,20 @@ | |||
|   display: block; | ||||
|   border-bottom: 1px solid rgba(127, 127, 127, 0.25); | ||||
|   margin-top: 1em; | ||||
|   transform: translateZ(0); | ||||
|   z-index: 2; | ||||
| } | ||||
| 
 | ||||
| :host:last-of-type { | ||||
|   border-bottom: 0; | ||||
| } | ||||
| // :host:last-of-type { | ||||
| //   border-bottom: 0; | ||||
| // } | ||||
| 
 | ||||
| .method-header { | ||||
|   margin-bottom: .9em; | ||||
|   margin-bottom: calc(1em - 6px); | ||||
| } | ||||
| 
 | ||||
| .method-endpoint { | ||||
|   margin: 0 0 2em 0; | ||||
|   //margin: 0 0 2px 0; | ||||
|   padding: 10px 20px; | ||||
|   border-radius: $border-radius*2; | ||||
|   background-color: darken($black, 2%); | ||||
|  | @ -31,8 +33,8 @@ | |||
|   padding-top: 1px; | ||||
|   padding-bottom: 0; | ||||
|   margin: 0; | ||||
|   font-size: .8em; | ||||
|     color: $black; | ||||
|   font-size: 12/14em; | ||||
|   color: $black; | ||||
|   vertical-align: middle; | ||||
|   display: inline-block; | ||||
|   border-radius: $border-radius; | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import { getChildDebugElement } from '../../../tests/helpers'; | |||
| 
 | ||||
| import { Method } from './method'; | ||||
| import { SpecManager } from '../../utils/spec-manager';; | ||||
| import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';; | ||||
| 
 | ||||
| describe('Redoc components', () => { | ||||
|   beforeEach(() => { | ||||
|  | @ -19,12 +20,17 @@ describe('Redoc components', () => { | |||
|   describe('Method Component', () => { | ||||
|     let builder; | ||||
|     let component; | ||||
|     let specMgr; | ||||
| 
 | ||||
|     beforeEach(async(inject([SpecManager], ( specMgr) => { | ||||
| 
 | ||||
|       return specMgr.load('/tests/schemas/extended-petstore.yml'); | ||||
|     beforeEach(async(inject([SpecManager, LazyTasksService], (_specMgr, lazyTasks) => { | ||||
|       lazyTasks.sync = true; | ||||
|       specMgr = _specMgr; | ||||
|     }))); | ||||
| 
 | ||||
|     beforeEach(done => { | ||||
|       specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail); | ||||
|     }); | ||||
| 
 | ||||
|     beforeEach(() => { | ||||
|       let fixture = TestBed.createComponent(TestAppComponent); | ||||
|       component = getChildDebugElement(fixture.debugElement, 'method').componentInstance; | ||||
|  | @ -53,7 +59,6 @@ describe('Redoc components', () => { | |||
| /** Test component that contains a Method. */ | ||||
| @Component({ | ||||
|   selector: 'test-app', | ||||
|   providers: [SpecManager], | ||||
|   template: | ||||
|       `<method pointer='#/paths/~1user~1{username}/put'></method>` | ||||
| }) | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| 'use strict'; | ||||
| import { Input, Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; | ||||
| import { Input, Component, OnInit, ChangeDetectionStrategy, ElementRef, ChangeDetectorRef } from '@angular/core'; | ||||
| import JsonPointer from '../../utils/JsonPointer'; | ||||
| import { BaseComponent, SpecManager } from '../base'; | ||||
| import { SchemaHelper } from '../../services/schema-helper.service'; | ||||
| import { OptionsService } from '../../services/options.service'; | ||||
| import { OptionsService, AppStateService } from '../../services/'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'method', | ||||
|  | @ -14,10 +14,14 @@ import { OptionsService } from '../../services/options.service'; | |||
| export class Method extends BaseComponent implements OnInit { | ||||
|   @Input() pointer:string; | ||||
|   @Input() tag:string; | ||||
|   @Input() posInfo: any; | ||||
| 
 | ||||
|   hidden = true; | ||||
| 
 | ||||
|   method:any; | ||||
| 
 | ||||
|   constructor(specMgr:SpecManager, private optionsService: OptionsService) { | ||||
|   constructor(specMgr:SpecManager, private optionsService: OptionsService, private chDetector: ChangeDetectorRef, | ||||
|   private appState: AppStateService, private el: ElementRef) { | ||||
|     super(specMgr); | ||||
|   } | ||||
| 
 | ||||
|  | @ -53,6 +57,14 @@ export class Method extends BaseComponent implements OnInit { | |||
|     return bodyParam; | ||||
|   } | ||||
| 
 | ||||
|   show(res) { | ||||
|     if (res) { | ||||
|       this.el.nativeElement.firstElementChild.removeAttribute('hidden'); | ||||
|     } else { | ||||
|       this.el.nativeElement.firstElementChild.setAttribute('hidden', 'hidden'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|     this.preinit(); | ||||
|   } | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| <div class="methods"> | ||||
|   <div class="tag" *ngFor="let tag of tags;trackBy:trackByTagName"> | ||||
|   <div class="tag" *ngFor="let tag of tags;let catIdx = index; trackBy:trackByTagName"> | ||||
|     <div class="tag-info" [attr.section]="tag.id" *ngIf="!tag.headless"> | ||||
|       <h1 class="sharable-header"> <a class="share-link" href="#tag/{{tag.name | encodeURIComponent}}"></a>{{tag.name}} </h1> | ||||
|       <p *ngIf="tag.description" [innerHtml]="tag.description | marked"> </p> | ||||
|     </div> | ||||
|     <method *ngFor="let method of tag.methods;trackBy:trackByPointer" [pointer]="method.pointer" [attr.pointer]="method.pointer" | ||||
|     <method *lazyFor="let method of tag.methods;let show = show;" [hidden]="!show" [pointer]="method.pointer" [attr.pointer]="method.pointer" | ||||
|     [attr.section]="method.tag" [tag]="method.tag" [attr.operation-id]="method.operationId"></method> | ||||
|   </div> | ||||
| </div> | ||||
|  |  | |||
|  | @ -4,6 +4,11 @@ | |||
|   display: block; | ||||
|   overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| :host [hidden] { | ||||
|   display: none; | ||||
| } | ||||
| 
 | ||||
| .tag-info { | ||||
|   padding: $section-spacing; | ||||
|   box-sizing: border-box; | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ import { getChildDebugElement } from '../../../tests/helpers'; | |||
| import { MethodsList } from './methods-list'; | ||||
| import { SpecManager } from '../../utils/spec-manager'; | ||||
| 
 | ||||
| describe('Redoc components', () => { | ||||
| describe('Redoc components', () => {  | ||||
|   beforeEach(() => { | ||||
|     TestBed.configureTestingModule({ declarations: [ TestAppComponent ] }); | ||||
|   }); | ||||
|  | @ -21,11 +21,16 @@ describe('Redoc components', () => { | |||
|     let builder; | ||||
|     let component; | ||||
|     let fixture; | ||||
|     let specMgr; | ||||
| 
 | ||||
|     beforeEach(async(inject([SpecManager], ( specMgr) => { | ||||
| 
 | ||||
|       return specMgr.load('/tests/schemas/methods-list-component.json'); | ||||
|     beforeEach(async(inject([SpecManager], (_specMgr) => { | ||||
|       specMgr = _specMgr; | ||||
|     }))); | ||||
| 
 | ||||
|     beforeEach(done => { | ||||
|       specMgr.load('/tests/schemas/methods-list-component.json').then(done, done.fail); | ||||
|     }); | ||||
| 
 | ||||
|     beforeEach(() => { | ||||
|       fixture = TestBed.createComponent(TestAppComponent); | ||||
|       component = getChildDebugElement(fixture.debugElement, 'methods-list').componentInstance; | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ | |||
|     {{paramType.place}} Parameters | ||||
|     <span class="hint--top-right hint--large" [attr.data-hint]="paramType.placeHint">?</span> | ||||
|   </header> | ||||
|   <br> | ||||
|   <div class="params-wrap"> | ||||
|     <div *ngFor="let param of paramType.params" class="param"> | ||||
|         <div class="param-name"> | ||||
|  |  | |||
|  | @ -2,10 +2,14 @@ | |||
| 
 | ||||
| $hint-color: #999999; | ||||
| 
 | ||||
| :host { | ||||
|   display: block; | ||||
| } | ||||
| 
 | ||||
| .param-list-header { | ||||
|   border-bottom: 1px solid rgba($text-color, .3); | ||||
|   padding: 0.2em 0; | ||||
|   margin: 3.5em 0 .8em 0; | ||||
| //  padding: 0.2em 0; | ||||
|   margin: 3em 0 1em 0; | ||||
|   color: rgba($text-color, .5); | ||||
|   font-weight: normal; | ||||
|   text-transform: uppercase; | ||||
|  | @ -14,7 +18,7 @@ $hint-color: #999999; | |||
| @import '../JsonSchema/json-schema-common'; | ||||
| 
 | ||||
| header.paramType { | ||||
|   margin: 10px 0; | ||||
|   margin: 25px 0 5px 0; | ||||
|   text-transform: capitalize; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,11 @@ | |||
|   <h1>Oops... ReDoc failed to render this spec</h1> | ||||
|   <div class='redoc-error-details'>{{error.message}}</div> | ||||
| </div> | ||||
| <loading-bar *ngIf="options.lazyRendering" [progress]="loadingProgress"> </loading-bar> | ||||
| <div class="redoc-wrap" *ngIf="specLoaded && !error"> | ||||
|   <div class="background"> | ||||
|     <div class="background-actual"> </div> | ||||
|   </div> | ||||
|   <div class="menu-content" sticky-sidebar [scrollParent]="options.$scrollParent" [scrollYOffset]="options.scrollYOffset"> | ||||
|       <api-logo> </api-logo> | ||||
|       <side-menu> </side-menu> | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ | |||
| } | ||||
| 
 | ||||
| .redoc-wrap { | ||||
|   z-index: 0; | ||||
|   position: relative; | ||||
|   font-family: $base-font, $base-font-family; | ||||
|   font-size: $em-size; | ||||
|  | @ -36,14 +37,19 @@ | |||
|   color: $text-color; | ||||
| } | ||||
| 
 | ||||
| .menu-content { | ||||
|   overflow: hidden; | ||||
| } | ||||
| 
 | ||||
| [sticky-sidebar] { | ||||
|   width: $side-bar-width; | ||||
|   background-color: $side-bar-bg-color; | ||||
|   overflow-y: auto; | ||||
|   overflow-x: hidden; | ||||
|   transform: translateZ(0); | ||||
|   z-index: 75; | ||||
| 
 | ||||
|   @media (max-width: $side-menu-mobile-breakpoint) { | ||||
|     z-index: 1; | ||||
|     width: 100%; | ||||
|     bottom: auto !important; | ||||
|   } | ||||
|  | @ -51,22 +57,33 @@ | |||
| 
 | ||||
| .api-content { | ||||
|   margin-left: $side-bar-width; | ||||
|   z-index: 50; | ||||
|   position: relative; | ||||
|   // height: 100vh; | ||||
|   // overflow: scroll; | ||||
|   top: 0; | ||||
|   @media (max-width: $side-menu-mobile-breakpoint) { | ||||
|     padding-top: 3em; | ||||
|     margin-left: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .api-content:before { | ||||
|   content: ""; | ||||
|   background: $samples-panel-bg-color; | ||||
|   height: 100%; | ||||
|   width: $samples-panel-width; | ||||
| .background { | ||||
|   position: fixed; | ||||
|   top: 0; | ||||
|   bottom: 0; | ||||
|   right: 0; | ||||
|   position: absolute; | ||||
|   z-index: -1; | ||||
|   left: $side-bar-width; | ||||
|   z-index: 1; | ||||
| 
 | ||||
|   &-actual { | ||||
|     background: $samples-panel-bg-color; | ||||
|     left: 100% - $samples-panel-width; | ||||
|     right: 0; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     position: absolute; | ||||
|   } | ||||
| 
 | ||||
|   @media (max-width: $right-panel-squash-breakpoint) { | ||||
|     display: none; | ||||
|  | @ -98,7 +115,8 @@ | |||
|     font-family: $headers-font, $headers-font-family; | ||||
|     color: $secondary-color; | ||||
|     font-weight: $headers-font-weight; | ||||
|     line-height: 1.4em; | ||||
|     line-height: 1.5; | ||||
|     margin-bottom: 0.5em; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -107,7 +125,7 @@ | |||
|   h2 { font-size: $h2; } | ||||
|   h3 { font-size: $h3; } | ||||
|   h4 { font-size: $h4; } | ||||
|   h5 { font-size: $h5; } | ||||
|   h5 { font-size: $h5; line-height: 20px; } | ||||
| 
 | ||||
|   p { | ||||
|     font-family: $base-font, $base-font-family; | ||||
|  |  | |||
|  | @ -29,9 +29,11 @@ describe('Redoc components', () => { | |||
|       optsMgr = _optsMgr; | ||||
| 
 | ||||
|       specMgr = _specMgr; | ||||
|       return specMgr.load('/tests/schemas/extended-petstore.yml'); | ||||
|     }))); | ||||
| 
 | ||||
|     beforeEach(done => { | ||||
|       specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail); | ||||
|     }) | ||||
| 
 | ||||
|     it('should init component', () => { | ||||
|       let fixture = TestBed.createComponent(TestAppComponent); | ||||
|  |  | |||
|  | @ -16,14 +16,15 @@ import { BaseComponent } from '../base'; | |||
| import * as detectScollParent from 'scrollparent'; | ||||
| 
 | ||||
| import { SpecManager } from '../../utils/spec-manager'; | ||||
| import { OptionsService, Hash, MenuService, AppStateService } from '../../services/index'; | ||||
| import { OptionsService, Hash, AppStateService, SchemaHelper } from '../../services/index'; | ||||
| import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for'; | ||||
| import { CustomErrorHandler } from '../../utils/'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'redoc', | ||||
|   templateUrl: './redoc.html', | ||||
|   styleUrls: ['./redoc.css'], | ||||
|   changeDetection: ChangeDetectionStrategy.OnPush | ||||
|   //changeDetection: ChangeDetectionStrategy.OnPush
 | ||||
| }) | ||||
| export class Redoc extends BaseComponent implements OnInit { | ||||
|   static _preOptions: any; | ||||
|  | @ -34,6 +35,8 @@ export class Redoc extends BaseComponent implements OnInit { | |||
|   specLoaded: boolean; | ||||
|   options: any; | ||||
| 
 | ||||
|   loadingProgress: number; | ||||
| 
 | ||||
|   @Input() specUrl: string; | ||||
|   @HostBinding('class.loading') specLoading: boolean = false; | ||||
|   @HostBinding('class.loading-remove') specLoadingRemove: boolean = false; | ||||
|  | @ -43,9 +46,12 @@ export class Redoc extends BaseComponent implements OnInit { | |||
|     optionsMgr: OptionsService, | ||||
|     elementRef: ElementRef, | ||||
|     private changeDetector: ChangeDetectorRef, | ||||
|     private appState: AppStateService | ||||
|     private appState: AppStateService, | ||||
|     private lazyTasksService: LazyTasksService, | ||||
|     private hash: Hash | ||||
|   ) { | ||||
|     super(specMgr); | ||||
|     SchemaHelper.setSpecManager(specMgr); | ||||
|     // merge options passed before init
 | ||||
|     optionsMgr.options = Redoc._preOptions || {}; | ||||
| 
 | ||||
|  | @ -56,14 +62,22 @@ export class Redoc extends BaseComponent implements OnInit { | |||
|     if (scrollParent === DOM.defaultDoc().body) scrollParent = window; | ||||
|     optionsMgr.options.$scrollParent = scrollParent; | ||||
|     this.options = optionsMgr.options; | ||||
|     this.lazyTasksService.allSync = !this.options.lazyRendering; | ||||
|   } | ||||
| 
 | ||||
|   hideLoadingAnimation() { | ||||
|     this.specLoadingRemove = true; | ||||
|     setTimeout(() => { | ||||
|     requestAnimationFrame(() => { | ||||
|       this.specLoadingRemove = true; | ||||
|       this.specLoading = false; | ||||
|     }, 400); | ||||
|       setTimeout(() => { | ||||
|         this.specLoadingRemove = false; | ||||
|         this.specLoading = false; | ||||
|       }, 400); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   showLoadingAnimation() { | ||||
|     this.specLoading = true; | ||||
|     this.specLoadingRemove = false; | ||||
|   } | ||||
| 
 | ||||
|   load() { | ||||
|  | @ -71,19 +85,30 @@ export class Redoc extends BaseComponent implements OnInit { | |||
|       throw err; | ||||
|     }); | ||||
| 
 | ||||
|     this.appState.loading.subscribe(loading => { | ||||
|       if (loading) { | ||||
|         this.showLoadingAnimation(); | ||||
|       } else { | ||||
|         this.hideLoadingAnimation(); | ||||
|       } | ||||
|     }); | ||||
| 
 | ||||
|     this.specMgr.spec.subscribe((spec) => { | ||||
|       if (!spec) { | ||||
|         this.specLoading = true; | ||||
|         this.specLoaded = false; | ||||
|         this.appState.startLoading(); | ||||
|       } else { | ||||
|         this.specLoaded = true; | ||||
|         this.hideLoadingAnimation(); | ||||
|         this.changeDetector.markForCheck(); | ||||
|         this.changeDetector.detectChanges(); | ||||
|         this.specLoaded = true; | ||||
|         setTimeout(() => { | ||||
|           this.hash.start(); | ||||
|         }); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|     this.lazyTasksService.loadProgress.subscribe(progress => this.loadingProgress = progress) | ||||
|     this.appState.error.subscribe(_err => { | ||||
|       if (!_err) return; | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,31 +6,26 @@ | |||
| } | ||||
| 
 | ||||
| .action-buttons { | ||||
|   display: block; | ||||
|   opacity: 0; | ||||
|   transition: opacity 0.3s ease; | ||||
|   transform: translateY(100%); | ||||
| 
 | ||||
|   > span { | ||||
|     float: right; | ||||
|   } | ||||
|   z-index: 3; | ||||
|   position: relative; | ||||
|   height: 2em; | ||||
|   line-height: 2em; | ||||
|   padding-right: 10px; | ||||
|   text-align: right; | ||||
|   margin-top: -1em; | ||||
| 
 | ||||
|   > span > a { | ||||
|     padding: 2px 10px; | ||||
|     color: #ffffff; | ||||
|     cursor: pointer; | ||||
|     background-color: darken($black, 4%); | ||||
| 
 | ||||
|     &:hover { | ||||
|       background-color: $black; | ||||
|       background-color: lighten($black, 15%); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &:after { | ||||
|     display: block; | ||||
|     content: ''; | ||||
|     clear: both; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .code-sample:hover > .action-buttons { | ||||
|  | @ -45,6 +40,7 @@ header { | |||
|   color: $sample-panel-headers-color; | ||||
|   text-transform: uppercase; | ||||
|   font-weight: normal; | ||||
|   margin-top: 20px; | ||||
| } | ||||
| 
 | ||||
| :host /deep/ > tabs > ul li { | ||||
|  | @ -53,7 +49,7 @@ header { | |||
|   border-radius: $border-radius; | ||||
|   margin: 2px 0; | ||||
|   padding: 3px 10px 2px 10px; | ||||
|   line-height: 1.25; | ||||
|   line-height: 16px; | ||||
|   color: $sample-panel-headers-color; | ||||
| 
 | ||||
|   &:hover { | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ header { | |||
|   margin: 2px 0; | ||||
|   padding: 2px 8px 3px 8px; | ||||
|   color: $sample-panel-headers-color; | ||||
|   line-height: 1.25; | ||||
|   line-height: 16px; | ||||
| 
 | ||||
|   &:hover { | ||||
|     color: #ffffff; | ||||
|  |  | |||
|  | @ -2,9 +2,9 @@ | |||
|   <!-- in case sample is not available for some reason --> | ||||
|   <pre *ngIf="sample == undefined"> Sample unavailable </pre> | ||||
|   <div class="action-buttons"> | ||||
|     <span> <a *ngIf="enableButtons" (click)="collapseAll()">Collapse all</a> </span> | ||||
|     <span copy-button [copyText]="sample" class="hint--top-left hint--inversed"> <a>Copy</a> </span> | ||||
|     <span> <a *ngIf="enableButtons" (click)="expandAll()">Expand all</a> </span> | ||||
|     <span copy-button [copyText]="sample | json" class="hint--top hint--inversed"> <a>Copy</a> </span> | ||||
|     <span> <a *ngIf="enableButtons" (click)="collapseAll()">Collapse all</a> </span> | ||||
|   </div> | ||||
|   <pre [innerHtml]="sample | jsonFormatter"></pre> | ||||
| </div> | ||||
|  |  | |||
|  | @ -13,46 +13,26 @@ pre { | |||
| } | ||||
| 
 | ||||
| .action-buttons { | ||||
|   display: block; | ||||
|   opacity: 0; | ||||
|   transition: opacity 0.3s ease; | ||||
|   transform: translateY(100%); | ||||
|   z-index: 1; | ||||
|   z-index: 3; | ||||
|   position: relative; | ||||
| 
 | ||||
|   > span { | ||||
|     float: right; | ||||
| 
 | ||||
|     &:last-child > a:before { | ||||
|       display: none; | ||||
|     } | ||||
|   } | ||||
|   height: 2em; | ||||
|   line-height: 2em; | ||||
|   padding-right: 10px; | ||||
|   text-align: right; | ||||
|   margin-top: -1em; | ||||
| 
 | ||||
|   > span > a { | ||||
|     padding: 2px 10px; | ||||
|     color: #ffffff; | ||||
|     cursor: pointer; | ||||
| 
 | ||||
|     &:before { | ||||
|       content: '|'; | ||||
|       display: inline-block; | ||||
|       transform: translateX(-10px); | ||||
|     } | ||||
| 
 | ||||
|     &:first-child { | ||||
|       margin-right: 0; | ||||
|     } | ||||
| 
 | ||||
|     &:hover { | ||||
|       background-color: $black; | ||||
|       background-color: lighten($black, 15%); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &:after { | ||||
|     display: block; | ||||
|     content: ''; | ||||
|     clear: both; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .snippet:hover .action-buttons { | ||||
|  | @ -135,6 +115,7 @@ pre { | |||
| 
 | ||||
|   li { | ||||
|   	position: relative; | ||||
|     display: block; | ||||
|   } | ||||
| 
 | ||||
|   .hoverable { | ||||
|  |  | |||
|  | @ -98,7 +98,7 @@ export class SchemaSample extends BaseComponent implements OnInit { | |||
|     if (this.skipReadOnly && this.componentSchema['x-redoc-ro-sample']) { | ||||
|       this.sample = this.componentSchema['x-redoc-ro-sample']; | ||||
|       return true; | ||||
|     } else if (this.componentSchema['x-redoc-rw-sample']) { | ||||
|     } else if (!this.skipReadOnly && this.componentSchema['x-redoc-rw-sample']) { | ||||
|       this.sample = this.componentSchema['x-redoc-rw-sample']; | ||||
|       return true; | ||||
|     } | ||||
|  |  | |||
|  | @ -10,10 +10,10 @@ | |||
|   <div *ngFor="let cat of categories; let idx = index" class="menu-cat"> | ||||
| 
 | ||||
|     <label class="menu-cat-header" (click)="activateAndScroll(idx, -1)" [hidden]="cat.headless" | ||||
|     [ngClass]="{active: cat.active}"> {{cat.name}}</label> | ||||
|     [ngClass]="{active: cat.active, disabled: !cat.ready }"> {{cat.name}}</label> | ||||
|     <ul class="menu-subitems" [@itemAnimation]="cat.active ? 'expanded' : 'collapsed'"> | ||||
|       <li *ngFor="let method of cat.methods; trackBy:summary; let methIdx = index" | ||||
|         [ngClass]="{active: method.active}" | ||||
|         [ngClass]="{active: method.active, disabled: !method.ready}" | ||||
|         (click)="activateAndScroll(idx, methIdx)"> | ||||
|         {{method.summary}} | ||||
|       </li> | ||||
|  |  | |||
|  | @ -38,6 +38,11 @@ $mobile-menu-compact-breakpoint: 550px; | |||
|   &[hidden] { | ||||
|     display: none; | ||||
|   } | ||||
| 
 | ||||
|   &.disabled, &.disabled:hover { | ||||
|     cursor: default; | ||||
|     color: lighten($text-color, 60%); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .menu-subitems { | ||||
|  | @ -72,6 +77,11 @@ $mobile-menu-compact-breakpoint: 550px; | |||
|   & li.active { | ||||
|     background: darken($side-menu-active-bg-color, 6%); | ||||
|   } | ||||
| 
 | ||||
|   &.disabled, &.disabled:hover { | ||||
|     cursor: default; | ||||
|     color: lighten($text-color, 60%); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,23 +19,28 @@ let testOptions; | |||
| 
 | ||||
| describe('Redoc components', () => { | ||||
|   beforeEach(() => { | ||||
|     TestBed.configureTestingModule({ declarations: [ TestAppComponent ] }); | ||||
|     TestBed.configureTestingModule({ declarations: [ TestAppComponent, MethodsList ] }); | ||||
|   }); | ||||
|   describe('SideMenu Component', () => { | ||||
|     let builder; | ||||
|     let component; | ||||
|     let fixture; | ||||
|     let specMgr; | ||||
| 
 | ||||
|     beforeEach(async(inject([SpecManager, OptionsService], | ||||
|       ( specMgr, opts) => { | ||||
|     beforeEach(inject([SpecManager, OptionsService], | ||||
|       (_specMgr, opts) => { | ||||
| 
 | ||||
|       testOptions = opts; | ||||
|       testOptions.options = { | ||||
|         scrollYOffset: () => 0, | ||||
|         $scrollParent: window | ||||
|       }; | ||||
|       return specMgr.load('/tests/schemas/extended-petstore.yml'); | ||||
|     }))); | ||||
|       specMgr = _specMgr; | ||||
|     })); | ||||
| 
 | ||||
|     beforeEach(done => { | ||||
|       specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail); | ||||
|     }); | ||||
| 
 | ||||
|     beforeEach(() => { | ||||
|       fixture = TestBed.createComponent(TestAppComponent); | ||||
|  |  | |||
|  | @ -38,6 +38,8 @@ export class SideMenu extends BaseComponent implements OnInit { | |||
|   private $resourcesNav: any; | ||||
|   private $scrollParent: any; | ||||
| 
 | ||||
|   private firstChange = true; | ||||
| 
 | ||||
|   constructor(specMgr:SpecManager, elementRef:ElementRef, | ||||
|   private scrollService:ScrollService, private menuService:MenuService, private hash:Hash, | ||||
|   optionsService:OptionsService, private detectorRef:ChangeDetectorRef) { | ||||
|  | @ -52,20 +54,37 @@ export class SideMenu extends BaseComponent implements OnInit { | |||
|     this.menuService.changed.subscribe((evt) => this.changed(evt)); | ||||
|   } | ||||
| 
 | ||||
|   changed({cat, item}) { | ||||
|     this.activeCatCaption = cat.name || ''; | ||||
|     this.activeItemCaption = item && item.summary || ''; | ||||
|   changed(newItem) { | ||||
|     if (newItem) { | ||||
|       let {cat, item} = newItem; | ||||
|       this.activeCatCaption = cat.name || ''; | ||||
|       this.activeItemCaption = item && item.summary || ''; | ||||
|     } | ||||
| 
 | ||||
|     //safari doesn't update bindings if not run changeDetector manually :(
 | ||||
| 
 | ||||
|     this.detectorRef.detectChanges(); | ||||
|     if (this.firstChange) { | ||||
|       this.scrollActiveIntoView(); | ||||
|       this.firstChange = false; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   activateAndScroll(idx, methodIdx) { | ||||
|   scrollActiveIntoView() { | ||||
|     let $item = this.$element.querySelector('li.active, label.active'); | ||||
|     if ($item) $item.scrollIntoView(); | ||||
|   } | ||||
| 
 | ||||
|   activateAndScroll(catIdx, methodIdx) { | ||||
|     if (this.mobileMode()) { | ||||
|       this.toggleMobileNav(); | ||||
|     } | ||||
|     this.menuService.activate(idx, methodIdx); | ||||
|     let menu = this.categories; | ||||
| 
 | ||||
|     if (!menu[catIdx].ready) return; | ||||
|     if (menu[catIdx].methods && menu[catIdx].methods.length && (methodIdx >= 0) && | ||||
|     !menu[catIdx].methods[methodIdx].ready) return; | ||||
| 
 | ||||
|     this.menuService.activate(catIdx, methodIdx); | ||||
|     this.menuService.scrollToActive(); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,13 +14,16 @@ import { MethodsList } from './MethodsList/methods-list'; | |||
| import { Method } from './Method/method'; | ||||
| import { Warnings } from './Warnings/warnings'; | ||||
| import { SecurityDefinitions } from './SecurityDefinitions/security-definitions'; | ||||
| import { LoadingBar } from './LoadingBar/loading-bar'; | ||||
| 
 | ||||
| import { Redoc } from './Redoc/redoc'; | ||||
| 
 | ||||
| export const REDOC_DIRECTIVES = [ | ||||
|   ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList, | ||||
|   ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions | ||||
|   ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions, | ||||
|   LoadingBar | ||||
| ]; | ||||
| 
 | ||||
| export { ApiInfo, ApiLogo, JsonSchema, JsonSchemaLazy, ParamsList, RequestSamples, ResponsesList, | ||||
| ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions } | ||||
| ResponsesSamples, SchemaSample, SideMenu, MethodsList, Method, Warnings, Redoc, SecurityDefinitions, | ||||
| LoadingBar } | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| import 'core-js/es7/reflect'; | ||||
| import 'zone.js/dist/zone'; | ||||
| 
 | ||||
| import 'core-js/es6/symbol'; | ||||
| import 'core-js/es6/object'; | ||||
| import 'core-js/es6/function'; | ||||
|  | @ -18,9 +21,6 @@ import 'core-js/es6/reflect'; | |||
| // see issue https://github.com/AngularClass/angular2-webpack-starter/issues/709
 | ||||
| // import 'core-js/es6/promise';
 | ||||
| 
 | ||||
| import 'core-js/es7/reflect'; | ||||
| import 'zone.js/dist/zone'; | ||||
| 
 | ||||
| // Typescript emit helpers polyfill
 | ||||
| import 'ts-helpers'; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,10 +1,11 @@ | |||
| import { NgModule, ErrorHandler } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| 
 | ||||
| import { Redoc, SecurityDefinitions, REDOC_DIRECTIVES } from './components/index'; | ||||
| import { Redoc, SecurityDefinitions, Method, REDOC_DIRECTIVES } from './components/index'; | ||||
| import { REDOC_COMMON_DIRECTIVES, DynamicNg2Wrapper } from './shared/components/index'; | ||||
| import { REDOC_PIPES, KeysPipe } from './utils/pipes'; | ||||
| import { CustomErrorHandler } from './utils/' | ||||
| import { LazyTasksService } from './shared/components/LazyFor/lazy-for'; | ||||
| 
 | ||||
| import { | ||||
|   OptionsService, | ||||
|  | @ -22,7 +23,7 @@ import { SpecManager } from './utils/spec-manager'; | |||
|   imports: [ CommonModule ], | ||||
|   declarations: [ REDOC_DIRECTIVES, REDOC_COMMON_DIRECTIVES, REDOC_PIPES ], | ||||
|   bootstrap: [ Redoc ], | ||||
|   entryComponents: [ SecurityDefinitions, DynamicNg2Wrapper ], | ||||
|   entryComponents: [ SecurityDefinitions, DynamicNg2Wrapper, Method ], | ||||
|   providers: [ | ||||
|     SpecManager, | ||||
|     ScrollService, | ||||
|  | @ -33,6 +34,7 @@ import { SpecManager } from './utils/spec-manager'; | |||
|     AppStateService, | ||||
|     ComponentParser, | ||||
|     ContentProjector, | ||||
|     LazyTasksService, | ||||
|     { provide: ErrorHandler, useClass: CustomErrorHandler }, | ||||
|     { provide: COMPONENT_PARSER_ALLOWED, useValue: { 'security-definitions': SecurityDefinitions} } | ||||
|   ], | ||||
|  |  | |||
|  | @ -1,11 +1,24 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { Injectable, NgZone } from '@angular/core'; | ||||
| import { Subject } from 'rxjs/Subject'; | ||||
| import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | ||||
| import { Injector } from '@angular/core'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class AppStateService { | ||||
|   samplesLanguage = new Subject<string>(); | ||||
|   error = new BehaviorSubject<any>(null); | ||||
|   loading = new Subject<boolean>(); | ||||
| 
 | ||||
|   startLoading() { | ||||
|     this.loading.next(true); | ||||
|   } | ||||
| 
 | ||||
|   stopLoading() { | ||||
|     this.loading.next(false); | ||||
|   } | ||||
| 
 | ||||
|   constructor(private injector: Injector, private zone: NgZone) { | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -4,20 +4,18 @@ import { | |||
| } from '@angular/core/testing'; | ||||
| 
 | ||||
| import { Hash } from './hash.service'; | ||||
| import { SpecManager } from '../utils/spec-manager'; | ||||
| 
 | ||||
| describe('Hash Service', () => { | ||||
|   let specMgr = new SpecManager(); | ||||
|   let hashService; | ||||
| 
 | ||||
|   beforeEach(inject([Hash], (_hash) => hashService = _hash)); | ||||
|   beforeEach(inject([Hash], (_hash) => { | ||||
|     hashService = _hash; | ||||
|   })); | ||||
| 
 | ||||
|   it('should trigger changed event after ReDoc bootstrapped', (done) => { | ||||
|     spyOn(hashService.value, 'next').and.callThrough(); | ||||
|     specMgr.spec.next({}); | ||||
|     setTimeout(() => { | ||||
|       expect(hashService.value.next).toHaveBeenCalled(); | ||||
|       done(); | ||||
|     }); | ||||
|   it('should trigger changed event when method start is called', () => { | ||||
|     spyOn(hashService.value, 'next').and.stub(); | ||||
|     hashService.start(); | ||||
|     expect(hashService.value.next).toHaveBeenCalled(); | ||||
|     hashService.value.next.and.callThrough(); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -3,20 +3,16 @@ import { Injectable } from '@angular/core'; | |||
| import { PlatformLocation } from '@angular/common'; | ||||
| 
 | ||||
| import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | ||||
| import { SpecManager } from '../utils/spec-manager'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class Hash { | ||||
|   public value = new BehaviorSubject<string>(''); | ||||
|   constructor(private specMgr: SpecManager, private location: PlatformLocation) { | ||||
|   public value = new BehaviorSubject<string | null>(null); | ||||
|   constructor(private location: PlatformLocation) { | ||||
|     this.bind(); | ||||
|   } | ||||
| 
 | ||||
|     this.specMgr.spec.subscribe((spec) => { | ||||
|       if (!spec) return; | ||||
|       setTimeout(() => { | ||||
|         this.value.next(this.hash); | ||||
|       }); | ||||
|     }); | ||||
|   start() { | ||||
|     this.value.next(this.hash); | ||||
|   } | ||||
| 
 | ||||
|   get hash() { | ||||
|  |  | |||
|  | @ -1,57 +1,52 @@ | |||
| 'use strict'; | ||||
| import { Component } from '@angular/core'; | ||||
| import { Component  } from '@angular/core'; | ||||
| import { | ||||
|   inject, | ||||
|   async, | ||||
|   TestBed | ||||
| } from '@angular/core/testing'; | ||||
| 
 | ||||
| import { MethodsList } from '../components/MethodsList/methods-list'; | ||||
| import { MenuService } from './menu.service'; | ||||
| import { Hash } from './hash.service'; | ||||
| import { ScrollService } from './scroll.service'; | ||||
| import { LazyTasksService } from '../shared/components/LazyFor/lazy-for'; | ||||
| import { ScrollService  } from './scroll.service'; | ||||
| import { SchemaHelper } from './schema-helper.service'; | ||||
| import { SpecManager } from '../utils/spec-manager';; | ||||
| 
 | ||||
| describe('Menu service', () => { | ||||
|   beforeEach(() => { | ||||
|     TestBed.configureTestingModule({ declarations: [ TestAppComponent ] }); | ||||
|     TestBed.configureTestingModule({ declarations: [ TestAppComponent, MethodsList ] }); | ||||
|   }); | ||||
| 
 | ||||
|   let menu, hashService, scroll; | ||||
|   let menu, hashService, scroll, tasks; | ||||
|   let specMgr; | ||||
| 
 | ||||
|   beforeEach(async(inject([SpecManager, Hash, ScrollService], | ||||
|   ( _specMgr, _hash, _scroll, _menu) => { | ||||
|   beforeEach(inject([SpecManager, Hash, ScrollService, LazyTasksService], | ||||
|   ( _specMgr, _hash, _scroll, _tasks) => { | ||||
|     hashService = _hash; | ||||
|     scroll = _scroll; | ||||
| 
 | ||||
|     tasks = _tasks; | ||||
|     specMgr = _specMgr; | ||||
|     return specMgr.load('/tests/schemas/extended-petstore.yml'); | ||||
|   }))); | ||||
|     SchemaHelper.setSpecManager(specMgr); | ||||
|   })); | ||||
| 
 | ||||
|   beforeEach(done => { | ||||
|     specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail); | ||||
|   }); | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     menu = new MenuService(hashService, scroll, specMgr); | ||||
|     menu = TestBed.get(MenuService); | ||||
|     let fixture = TestBed.createComponent(TestAppComponent); | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should run hashScroll when hash changed', (done) => { | ||||
|     spyOn(menu, 'hashScroll').and.callThrough(); | ||||
|     hashService.value.subscribe((hash) => { | ||||
|       if (!hash) return; | ||||
|       expect(menu.hashScroll).toHaveBeenCalled(); | ||||
|       menu.hashScroll.and.callThrough(); | ||||
|       done(); | ||||
|     }); | ||||
|     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(menu, 'scrollToActive').and.callThrough(); | ||||
|     spyOn(window, 'scrollTo').and.stub(); | ||||
|     hashService.value.subscribe((hash) => { | ||||
|       if (!hash) return; | ||||
|       expect(menu.hashScroll).toHaveBeenCalled(); | ||||
|       expect(menu.scrollToActive).toHaveBeenCalled(); | ||||
|       let scrollY = (<jasmine.Spy>window.scrollTo).calls.argsFor(0)[1]; | ||||
|       expect(scrollY).toBeGreaterThan(0); | ||||
|       (<jasmine.Spy>window.scrollTo).and.callThrough(); | ||||
|  | @ -59,14 +54,14 @@ describe('Menu service', () => { | |||
|     }); | ||||
|     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(menu, 'scrollToActive').and.callThrough(); | ||||
|     spyOn(window, 'scrollTo').and.stub(); | ||||
|     hashService.value.subscribe((hash) => { | ||||
|       if (!hash) return; | ||||
|       expect(menu.hashScroll).toHaveBeenCalled(); | ||||
|       expect(menu.scrollToActive).toHaveBeenCalled(); | ||||
|       let scrollY = (<jasmine.Spy>window.scrollTo).calls.argsFor(0)[1]; | ||||
|       expect(scrollY).toBeGreaterThan(0); | ||||
|       done(); | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| 'use strict'; | ||||
| import { Injectable, EventEmitter } from '@angular/core'; | ||||
| import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | ||||
| import { ScrollService, INVIEW_POSITION } from './scroll.service'; | ||||
| import { Hash } from './hash.service'; | ||||
| import { SpecManager } from '../utils/spec-manager'; | ||||
| import { SchemaHelper, MenuCategory } from './schema-helper.service'; | ||||
| import { AppStateService } from './app-state.service'; | ||||
| import { LazyTasksService } from '../shared/components/LazyFor/lazy-for'; | ||||
| 
 | ||||
| const CHANGE = { | ||||
|   NEXT : 1, | ||||
|  | @ -14,13 +17,19 @@ const CHANGE = { | |||
| @Injectable() | ||||
| export class MenuService { | ||||
|   changed: EventEmitter<any> = new EventEmitter(); | ||||
|   ready: BehaviorSubject<boolean> = new BehaviorSubject(false); | ||||
|   categories: Array<MenuCategory>; | ||||
| 
 | ||||
|   activeCatIdx: number = 0; | ||||
|   activeMethodIdx: number = -1; | ||||
|   activeMethodPtr: string; | ||||
| 
 | ||||
|   constructor(private hash:Hash, private scrollService:ScrollService, specMgr:SpecManager) { | ||||
|   constructor( | ||||
|     private hash:Hash, | ||||
|     private tasks: LazyTasksService, | ||||
|     private scrollService: ScrollService, | ||||
|     private appState: AppStateService, | ||||
|     specMgr:SpecManager | ||||
|   ) { | ||||
|     this.hash = hash; | ||||
|     this.categories = SchemaHelper.buildMenuTree(specMgr.schema); | ||||
| 
 | ||||
|  | @ -28,13 +37,45 @@ export class MenuService { | |||
|       this.scrollUpdate(evt.isScrolledDown); | ||||
|     }); | ||||
| 
 | ||||
|     this.changeActive(CHANGE.INITIAL); | ||||
|     //this.changeActive(CHANGE.INITIAL);
 | ||||
| 
 | ||||
|     this.hash.value.subscribe((hash) => { | ||||
|       this.hashScroll(hash); | ||||
|       if (hash == undefined) return; | ||||
|       this.setActiveByHash(hash); | ||||
|       if (!this.tasks.empty) { | ||||
|         this.tasks.start(this.activeCatIdx, this.activeMethodIdx, this); | ||||
|         this.scrollService.setStickElement(this.getCurrentMethodEl()); | ||||
|         if (hash) this.scrollToActive(); | ||||
|         this.appState.stopLoading(); | ||||
|       } else { | ||||
|         if (hash) this.scrollToActive(); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   enableItem(catIdx, methodIdx, skipUpdate = false) { | ||||
|     let cat = this.categories[catIdx]; | ||||
|     cat.ready = true; | ||||
|     if (cat.methods.length) cat.methods[methodIdx].ready = true; | ||||
|     let prevCat = this.categories[catIdx - 1]; | ||||
|     if (prevCat && !prevCat.ready && (prevCat.virtual || !prevCat.methods.length)) { | ||||
|       this.enableItem(catIdx - 1, -1, true); | ||||
|     } | ||||
| 
 | ||||
|     if (skipUpdate) return; | ||||
|     this.changed.next(); | ||||
|   } | ||||
| 
 | ||||
|   get activeMethodPtr() { | ||||
|     let cat = this.categories[this.activeCatIdx]; | ||||
|     let ptr = null; | ||||
|     if (cat && cat.methods.length) { | ||||
|       let mtd = cat.methods[this.activeMethodIdx]; | ||||
|       ptr = mtd && mtd.pointer || null; | ||||
|     } | ||||
|     return ptr; | ||||
|   } | ||||
| 
 | ||||
|   scrollUpdate(isScrolledDown) { | ||||
|     let stable = false; | ||||
|     while(!stable) { | ||||
|  | @ -44,6 +85,7 @@ export class MenuService { | |||
|       if(isScrolledDown) { | ||||
|         //&& elementInViewPos === INVIEW_POSITION.BELLOW
 | ||||
|         let $nextEl = this.getRelativeCatOrItem(1); | ||||
|         if (!$nextEl) return; | ||||
|         let nextInViewPos = this.scrollService.getElementPos($nextEl, true); | ||||
|         if (elementInViewPos === INVIEW_POSITION.BELLOW && nextInViewPos === INVIEW_POSITION.ABOVE) { | ||||
|           stable = this.changeActive(CHANGE.NEXT); | ||||
|  | @ -93,6 +135,8 @@ export class MenuService { | |||
|   } | ||||
| 
 | ||||
|   activate(catIdx, methodIdx) { | ||||
|     if (catIdx < 0) return; | ||||
| 
 | ||||
|     let menu = this.categories; | ||||
| 
 | ||||
|     menu[this.activeCatIdx].active = false; | ||||
|  | @ -105,12 +149,10 @@ export class MenuService { | |||
|     this.activeCatIdx = catIdx; | ||||
|     this.activeMethodIdx = methodIdx; | ||||
|     menu[catIdx].active = true; | ||||
|     this.activeMethodPtr = null; | ||||
|     let currentItem; | ||||
|     if (menu[catIdx].methods.length && (methodIdx > -1)) { | ||||
|       currentItem = menu[catIdx].methods[methodIdx]; | ||||
|       currentItem.active = true; | ||||
|       this.activeMethodPtr = currentItem.pointer; | ||||
|     } | ||||
| 
 | ||||
|     this.changed.next({cat: menu[catIdx], item: currentItem}); | ||||
|  | @ -156,24 +198,31 @@ export class MenuService { | |||
|     this.scrollService.scrollTo(this.getCurrentMethodEl()); | ||||
|   } | ||||
| 
 | ||||
|   hashScroll(hash) { | ||||
|     if (!hash) return; | ||||
| 
 | ||||
|     let $el; | ||||
|   setActiveByHash(hash) { | ||||
|     if (!hash) { | ||||
|       return; | ||||
|     } | ||||
|     let catIdx, methodIdx; | ||||
|     hash = hash.substr(1); | ||||
|     let namespace = hash.split('/')[0]; | ||||
|     let ptr = decodeURIComponent(hash.substr(namespace.length + 1)); | ||||
|     if (namespace === 'operation') { | ||||
|       $el = this.getMethodElByOperId(ptr); | ||||
|     } else if (namespace === 'tag') { | ||||
|     if (namespace === 'section' || namespace === 'tag') { | ||||
|       let sectionId = ptr.split('/')[0]; | ||||
|       catIdx = this.categories.findIndex(cat => cat.id === namespace + '/' + sectionId); | ||||
|       let cat = this.categories[catIdx]; | ||||
|       ptr = ptr.substr(sectionId.length) || null; | ||||
|       sectionId = namespace + (sectionId ? '/' + sectionId : ''); | ||||
|       $el = this.getMethodElByPtr(ptr, sectionId); | ||||
|       methodIdx = cat.methods.findIndex(method => method.pointer === ptr); | ||||
|     } else { | ||||
|       $el = this.getMethodElByPtr(null, namespace + '/' + ptr); | ||||
|       catIdx = this.categories.findIndex(cat => { | ||||
|         if (!cat.methods.length) return false; | ||||
|         methodIdx = cat.methods.findIndex(method => method.operationId === ptr || method.pointer === ptr); | ||||
|         if (methodIdx >= 0) { | ||||
|           return true; | ||||
|         } else { | ||||
|           return false; | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     if ($el) this.scrollService.scrollTo($el); | ||||
|     this.activate(catIdx, methodIdx); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -8,25 +8,43 @@ const defaults = { | |||
|   disableLazySchemas: false | ||||
| }; | ||||
| 
 | ||||
| const OPTION_NAMES = new Set(['scrollYOffset', 'disableLazySchemas', 'specUrl', 'suppressWarnings', 'hideHostname']); | ||||
| const OPTION_NAMES = new Set([ | ||||
|   'scrollYOffset', | ||||
|   'disableLazySchemas', | ||||
|   'specUrl', | ||||
|   'suppressWarnings', | ||||
|   'hideHostname', | ||||
|   'lazyRendering' | ||||
| ]); | ||||
| 
 | ||||
| interface Options { | ||||
|   scrollYOffset?: any; | ||||
|   disableLazySchemas?: boolean; | ||||
|   specUrl?: string; | ||||
|   suppressWarnings?: boolean; | ||||
|   hideHostname?: boolean; | ||||
|   lazyRendering?: boolean; | ||||
|   $scrollParent?: HTMLElement | Window; | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| export class OptionsService { | ||||
|   private _options: any; | ||||
|   private _options: Options; | ||||
| 
 | ||||
|   constructor() { | ||||
|     this._options = defaults; | ||||
|     this._normalizeOptions(); | ||||
|   } | ||||
| 
 | ||||
|   get options() { | ||||
|   get options():Options { | ||||
|     return this._options; | ||||
|   } | ||||
| 
 | ||||
|   set options(opts) { | ||||
|   set options(opts:Options) { | ||||
|     this._options = Object.assign(this._options, opts); | ||||
|   } | ||||
| 
 | ||||
|   parseOptions(el) { | ||||
|   parseOptions(el:HTMLElement):void { | ||||
|     let parsedOpts; | ||||
|     let attributesMap = DOM.attributeMap(el); | ||||
|     parsedOpts = {}; | ||||
|  | @ -46,7 +64,7 @@ export class OptionsService { | |||
|     this._normalizeOptions(); | ||||
|   } | ||||
| 
 | ||||
|   _normalizeOptions() { | ||||
|   _normalizeOptions():void { | ||||
|     // modify scrollYOffset to always be a function
 | ||||
|     if (!isFunction(this._options.scrollYOffset)) { | ||||
|       if (isFinite(this._options.scrollYOffset)) { | ||||
|  | @ -70,5 +88,6 @@ export class OptionsService { | |||
|     if (isString(this._options.disableLazySchemas)) this._options.disableLazySchemas = true; | ||||
|     if (isString(this._options.suppressWarnings)) this._options.suppressWarnings = true; | ||||
|     if (isString(this._options.hideHostname)) this._options.hideHostname = true; | ||||
|     if (isString(this._options.lazyRendering)) this._options.lazyRendering = true; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| 'use strict'; | ||||
| import { JsonPointer } from '../utils/JsonPointer'; | ||||
| 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'; | ||||
|  | @ -15,6 +14,8 @@ export interface MenuMethod { | |||
|   summary: string; | ||||
|   tag: string; | ||||
|   pointer: string; | ||||
|   operationId: string; | ||||
|   ready: boolean; | ||||
| } | ||||
| 
 | ||||
| export interface MenuCategory { | ||||
|  | @ -26,8 +27,12 @@ export interface MenuCategory { | |||
|   description?: string; | ||||
|   empty?: string; | ||||
|   virtual?: boolean; | ||||
|   ready: boolean; | ||||
| } | ||||
| 
 | ||||
| // global var for this module
 | ||||
| var specMgrInstance; | ||||
| 
 | ||||
| const injectors = { | ||||
|   notype: { | ||||
|     check: (propertySchema) => !propertySchema.type, | ||||
|  | @ -186,8 +191,8 @@ const injectors = { | |||
|         parentPtr = JsonPointer.dirName(hostPointer, 3); | ||||
|       } | ||||
| 
 | ||||
|       let parentParam = SpecManager.instance().byPointer(parentPtr); | ||||
|       let root = SpecManager.instance().schema; | ||||
|       let parentParam = specMgrInstance.byPointer(parentPtr); | ||||
|       let root =specMgrInstance.schema; | ||||
|       injectTo._produces = parentParam && parentParam.produces || root.produces; | ||||
|       injectTo._consumes = parentParam && parentParam.consumes || root.consumes; | ||||
|       injectTo._widgetType = 'file'; | ||||
|  | @ -196,6 +201,10 @@ const injectors = { | |||
| }; | ||||
| 
 | ||||
| export class SchemaHelper { | ||||
|   static setSpecManager(specMgr) { | ||||
|     specMgrInstance = specMgr; | ||||
|   } | ||||
| 
 | ||||
|   static preprocess(schema, pointer, hostPointer?) { | ||||
|     //propertySchema = Object.assign({}, propertySchema);
 | ||||
|     if (schema['x-redoc-schema-precompiled']) { | ||||
|  | @ -291,13 +300,15 @@ export class SchemaHelper { | |||
|   } | ||||
| 
 | ||||
|   static buildMenuTree(schema):Array<MenuCategory> { | ||||
|     var catIdx = 0; | ||||
|     let tag2MethodMapping = {}; | ||||
| 
 | ||||
|     for (let header of (<Array<string>>(schema.info && schema.info['x-redoc-markdown-headers'] || []))) { | ||||
|       let id = 'section/' + slugify(header); | ||||
|       tag2MethodMapping[id] = { | ||||
|         name: header, id: id, virtual: true, methods: [] | ||||
|         name: header, id: id, virtual: true, methods: [], idx: catIdx | ||||
|       }; | ||||
|       catIdx++; | ||||
|     } | ||||
| 
 | ||||
|     for (let tag of schema.tags || []) { | ||||
|  | @ -309,7 +320,9 @@ export class SchemaHelper { | |||
|         headless: tag.name === '', | ||||
|         empty: !!tag['x-traitTag'], | ||||
|         methods: [], | ||||
|         idx: catIdx | ||||
|       }; | ||||
|       catIdx++; | ||||
|     } | ||||
| 
 | ||||
|     let paths = schema.paths; | ||||
|  | @ -331,9 +344,11 @@ export class SchemaHelper { | |||
|             tagDetails = { | ||||
|               name: tag, | ||||
|               id: id, | ||||
|               headless: tag === '' | ||||
|               headless: tag === '', | ||||
|               idx: catIdx | ||||
|             }; | ||||
|             tag2MethodMapping[id] = tagDetails; | ||||
|             catIdx++; | ||||
|           } | ||||
|           if (tagDetails.empty) continue; | ||||
|           if (!tagDetails.methods) tagDetails.methods = []; | ||||
|  | @ -341,7 +356,9 @@ export class SchemaHelper { | |||
|             pointer: methodPointer, | ||||
|             summary: methodSummary, | ||||
|             operationId: methodInfo.operationId, | ||||
|             tag: tag | ||||
|             tag: tag, | ||||
|             idx: tagDetails.methods.length, | ||||
|             catIdx: tagDetails.idx | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| 'use strict'; | ||||
| import { Injectable, EventEmitter, Output } from '@angular/core'; | ||||
| import { Injectable, EventEmitter } from '@angular/core'; | ||||
| import { BrowserDomAdapter as DOM } from '../utils/browser-adapter'; | ||||
| import { OptionsService } from './options.service'; | ||||
| import { throttle } from '../utils/helpers'; | ||||
|  | @ -14,14 +14,19 @@ export const INVIEW_POSITION = { | |||
| export class ScrollService { | ||||
|   scrollYOffset: any; | ||||
|   $scrollParent: any; | ||||
|   @Output() scroll = new EventEmitter(); | ||||
|   scroll = new EventEmitter(); | ||||
|   private prevOffsetY: number; | ||||
|   private _cancel:any; | ||||
|   constructor(optionsService:OptionsService) { | ||||
|   private _savedPosition:number; | ||||
|   private _stickElement: HTMLElement; | ||||
|   constructor(private optionsService:OptionsService) { | ||||
|     this.scrollYOffset = () => optionsService.options.scrollYOffset(); | ||||
|     this.$scrollParent = optionsService.options.$scrollParent; | ||||
|     this.$scrollParent = optionsService.options.$scrollParent || window; | ||||
|     this.scroll = new EventEmitter(); | ||||
|     this.bind(); | ||||
|     if ('scrollRestoration' in history) { | ||||
|       history.scrollRestoration = 'manual'; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   scrollY() { | ||||
|  | @ -42,20 +47,45 @@ export class ScrollService { | |||
|     return INVIEW_POSITION.INVIEW; | ||||
|   } | ||||
| 
 | ||||
|   scrollTo($el, offset:number = 0) { | ||||
|     // TODO: rewrite this to use offsetTop as more reliable solution
 | ||||
|     let subjRect = $el.getBoundingClientRect(); | ||||
|     let posY = this.scrollY() + subjRect.top - this.scrollYOffset() + offset + 1; | ||||
|   scrollToPos(posY: number) { | ||||
|     if (this.$scrollParent.scrollTo) { | ||||
|       this.$scrollParent.scrollTo(0, posY); | ||||
|       this.$scrollParent.scrollTo(0, Math.floor(posY)); | ||||
|     } else { | ||||
|       this.$scrollParent.scrollTop = posY; | ||||
|     } | ||||
|   } | ||||
|   scrollTo($el, offset:number = 0) { | ||||
|     if (!$el) return; | ||||
|     // TODO: rewrite this to use offsetTop as more reliable solution
 | ||||
|     let subjRect = $el.getBoundingClientRect(); | ||||
|     let posY = this.scrollY() + subjRect.top - this.scrollYOffset() + offset + 1; | ||||
|     this.scrollToPos(posY); | ||||
|     return posY; | ||||
|   } | ||||
| 
 | ||||
|   saveScroll() { | ||||
|     let $el = this._stickElement; | ||||
|     if (!$el) return; | ||||
|     let offsetParent = $el.offsetParent; | ||||
|     this._savedPosition = $el.offsetTop + (<any>offsetParent).offsetTop; | ||||
|   } | ||||
| 
 | ||||
|   setStickElement($el) { | ||||
|     this._stickElement = $el; | ||||
|   } | ||||
| 
 | ||||
|   restoreScroll() { | ||||
|     let $el = this._stickElement; | ||||
|     if (!$el) return; | ||||
|     let offsetParent = $el.offsetParent; | ||||
|     let currentPosition = $el.offsetTop + (<any>offsetParent).offsetTop; | ||||
|     let newY = this.scrollY() + (currentPosition - this._savedPosition); | ||||
|     this.scrollToPos(newY); | ||||
|   } | ||||
| 
 | ||||
|   relativeScrollPos($el) { | ||||
|     let subjRect = $el.getBoundingClientRect(); | ||||
|     return - subjRect.top + this.scrollYOffset() - 1; | ||||
|     return -subjRect.top + this.scrollYOffset() - 1; | ||||
|   } | ||||
| 
 | ||||
|   scrollHandler(evt) { | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ export class CopyButton implements OnInit { | |||
|   onClick() { | ||||
|     let copied; | ||||
|     if (this.copyText) { | ||||
|       copied = Clipboard.copyCustom(this.copyText); | ||||
|       copied = Clipboard.copyCustom(JSON.stringify(this.copyText)); | ||||
|     } else { | ||||
|       copied = Clipboard.copyElement(this.copyElement); | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										172
									
								
								lib/shared/components/LazyFor/lazy-for.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								lib/shared/components/LazyFor/lazy-for.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,172 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| import { | ||||
|   Directive, | ||||
|   Input, | ||||
|   TemplateRef, | ||||
|   ChangeDetectorRef, | ||||
|   ViewContainerRef, | ||||
|   Injectable, | ||||
|   NgZone | ||||
| } from '@angular/core'; | ||||
| 
 | ||||
| import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | ||||
| 
 | ||||
| import { ScrollService } from '../../../services/scroll.service'; | ||||
| import { OptionsService } from '../../../services/options.service'; | ||||
| 
 | ||||
| import { isSafari } from '../../../utils/helpers'; | ||||
| 
 | ||||
| export class LazyForRow { | ||||
|   constructor(public $implicit: any, public index: number, public show: boolean) {} | ||||
| 
 | ||||
|   get first(): boolean { return this.index === 0; } | ||||
| 
 | ||||
|   get even(): boolean { return this.index % 2 === 0; } | ||||
| 
 | ||||
|   get odd(): boolean { return !this.even; } | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| export class LazyTasksService { | ||||
|   private _tasks = []; | ||||
|   private _current: number = 0; | ||||
|   private _syncCount: number = 0; | ||||
|   private menuService; | ||||
| 
 | ||||
|   public loadProgress = new BehaviorSubject<number>(0); | ||||
|   public allSync = false; | ||||
|   constructor(public optionsService: OptionsService, private zone: NgZone) { | ||||
|   } | ||||
| 
 | ||||
|   get empty() { | ||||
|     return this._current === this._tasks.length; | ||||
|   } | ||||
| 
 | ||||
|   set syncCount(n: number) { | ||||
|     this._syncCount = n; | ||||
|   } | ||||
| 
 | ||||
|   set lazy(sync:boolean) { | ||||
|     this.allSync = sync; | ||||
|   } | ||||
| 
 | ||||
|   addTasks(tasks:any[], callback:Function) { | ||||
|     tasks.forEach((task) => { | ||||
|       let taskCopy = Object.assign({_callback: callback}, task); | ||||
|       this._tasks.push(taskCopy); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   nextTaskSync() { | ||||
|     let task = this._tasks[this._current]; | ||||
|     if (!task) return; | ||||
|     task._callback(task.idx, true); | ||||
|     this._current++; | ||||
|     this.menuService.enableItem(task.catIdx, task.idx); | ||||
|     this.loadProgress.next(this._current / this._tasks.length * 100); | ||||
|   } | ||||
| 
 | ||||
|   nextTask() { | ||||
|     requestAnimationFrame(() => { | ||||
|       let task = this._tasks[this._current]; | ||||
|       if (!task) return; | ||||
|       task._callback(task.idx, false).then(() => { | ||||
|         this._current++; | ||||
|         this.menuService.enableItem(task.catIdx, task.idx); | ||||
|         setTimeout(()=> this.nextTask()); | ||||
|         this.loadProgress.next(this._current / this._tasks.length * 100); | ||||
|       }).catch(err => console.error(err)); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   sortTasks(catIdx, metIdx) { | ||||
|     let idxMap = {}; | ||||
|     this._tasks.forEach((task, idx) => { | ||||
|       idxMap[task.catIdx + '_' +  task.idx] = idx; | ||||
|     }); | ||||
|     metIdx  = metIdx < 0 ? 0 : metIdx; | ||||
|     let destIdx = idxMap[catIdx + '_' + metIdx] || 0; | ||||
|     this._tasks.sort((a, b) => { | ||||
|       let aIdx = idxMap[a.catIdx + '_' +  a.idx]; | ||||
|       let bIdx = idxMap[b.catIdx + '_' +  b.idx]; | ||||
|       return Math.abs(aIdx - destIdx) - Math.abs(bIdx - destIdx); | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   start(catIdx, metIdx, menuService) { | ||||
|     this.menuService = menuService; | ||||
|     let syncCount = 5; | ||||
|     // I know this is bad practice to detect browsers but there is an issue on Safari only
 | ||||
|     // http://stackoverflow.com/questions/40692365/maintaining-scroll-position-while-inserting-elements-above-glitching-only-in-sa
 | ||||
|     if (isSafari && this.optionsService.options.$scrollParent === window) { | ||||
|       syncCount = (metIdx >= 0) ? | ||||
|           this._tasks.findIndex(task => (task.catIdx === catIdx) && (task.idx === metIdx)) | ||||
|         : this._tasks.findIndex(task => task.catIdx === catIdx); | ||||
|       syncCount += 1; | ||||
|     } else { | ||||
|       this.sortTasks(catIdx, metIdx); | ||||
|     } | ||||
|     if (this.allSync) syncCount = this._tasks.length; | ||||
|     for (var i = this._current; i < syncCount; i++) { | ||||
|       this.nextTaskSync(); | ||||
|     } | ||||
|     this.nextTask(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| export class LazyTasksServiceSync extends LazyTasksService { | ||||
|   constructor(optionsService: OptionsService, zone: NgZone) { | ||||
|     super(optionsService, zone); | ||||
|     this.allSync = true; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @Directive({ | ||||
|   selector: '[lazyFor][lazyForOf]' | ||||
| }) | ||||
| export class LazyFor { | ||||
|   @Input() lazyForOf: any; | ||||
| 
 | ||||
|   prevIdx = null; | ||||
| 
 | ||||
|   private _viewRef; | ||||
|   constructor( | ||||
|     public _template: TemplateRef<LazyForRow>, | ||||
|     public cdr: ChangeDetectorRef, | ||||
|     public _viewContainer: ViewContainerRef, | ||||
|     public lazyTasks: LazyTasksService, | ||||
|     public scroll: ScrollService | ||||
|   ){ | ||||
|   } | ||||
| 
 | ||||
|   nextIteration(idx: number, sync: boolean):Promise<void> { | ||||
|     const view = this._viewContainer.createEmbeddedView( | ||||
|                 this._template, new LazyForRow(this.lazyForOf[idx], idx, sync), idx < this.prevIdx ? 0 : undefined); | ||||
|     this.prevIdx = idx; | ||||
|     view.context.index = idx; | ||||
|     (<any>view as ChangeDetectorRef).markForCheck(); | ||||
|     (<any>view as ChangeDetectorRef).detectChanges(); | ||||
|     if (sync) { | ||||
|       return Promise.resolve(); | ||||
|     } | ||||
|     return new Promise<void>((resolve, reject) => { | ||||
|       requestAnimationFrame(() => { | ||||
|         this.scroll.saveScroll(); | ||||
| 
 | ||||
|         view.context.show = true; | ||||
|         (<any>view as ChangeDetectorRef).markForCheck(); | ||||
|         (<any>view as ChangeDetectorRef).detectChanges(); | ||||
| 
 | ||||
|         this.scroll.restoreScroll(); | ||||
|         resolve(); | ||||
|       }); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|     this.lazyTasks.addTasks(this.lazyForOf, this.nextIteration.bind(this)) | ||||
|   } | ||||
| } | ||||
|  | @ -83,7 +83,7 @@ export class StickySidebar implements OnInit, OnDestroy { | |||
|     // FIXME use more reliable code
 | ||||
|     this.$redocEl = this.$element.offsetParent.parentNode || DOM.defaultDoc().body; | ||||
|     this.bind(); | ||||
|     setTimeout(() => this.updatePosition()); | ||||
|     requestAnimationFrame(() => this.updatePosition()); | ||||
|     //this.updatePosition()
 | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -69,6 +69,9 @@ export class Tabs implements OnInit { | |||
|     </div> | ||||
|   `,
 | ||||
|   styles: [` | ||||
|     :host { | ||||
|       display: block; | ||||
|     } | ||||
|     .tab-wrap { | ||||
|       display: none; | ||||
|     } | ||||
|  |  | |||
|  | @ -12,8 +12,8 @@ $zippy-redirect-bg-color: rgba($zippy-redirect-color, .08); | |||
| 
 | ||||
| :host { | ||||
|   // performance optimization | ||||
|   transform: translate3d(0, 0, 0); | ||||
|   backface-visibility: hidden; | ||||
|   // transform: translate3d(0, 0, 0); | ||||
|   // backface-visibility: hidden; | ||||
|   overflow: hidden; | ||||
|   display: block; | ||||
| } | ||||
|  |  | |||
|  | @ -6,9 +6,11 @@ import { Zippy } from './Zippy/zippy'; | |||
| import { CopyButton } from './CopyButton/copy-button.directive'; | ||||
| import { SelectOnClick } from './SelectOnClick/select-on-click.directive'; | ||||
| import { DynamicNg2Viewer, DynamicNg2Wrapper } from './DynamicNg2Viewer/dynamic-ng2-viewer.component'; | ||||
| import { LazyFor, LazyTasksService, LazyTasksServiceSync } from './LazyFor/lazy-for'; | ||||
| 
 | ||||
| export const REDOC_COMMON_DIRECTIVES = [ | ||||
|   DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper | ||||
|   DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper, LazyFor | ||||
| ]; | ||||
| 
 | ||||
| export { DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper } | ||||
| export { DropDown, StickySidebar, Tabs, Tab, Zippy, CopyButton, SelectOnClick, DynamicNg2Viewer, DynamicNg2Wrapper, LazyFor } | ||||
| export { LazyTasksService, LazyTasksServiceSync } | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ $base-font: Roboto; | |||
| $base-font-family: sans-serif; | ||||
| $base-font-weight: $light; | ||||
| $base-font-size: 1em; | ||||
| $base-line-height: 1.55em; | ||||
| $base-line-height: 1.5em; | ||||
| $text-color: $black; | ||||
| 
 | ||||
| // Heading Font | ||||
|  |  | |||
|  | @ -74,3 +74,7 @@ export function throttle(fn, threshhold, scope) { | |||
|     } | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export const isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 | ||||
|   || (function (p) { return p.toString() === '[object SafariRemoteNotification]'; })(!window['safari'] | ||||
|   || safari.pushNotification); | ||||
|  |  | |||
|  | @ -13,24 +13,10 @@ export class SpecManager { | |||
|   public basePath: string; | ||||
| 
 | ||||
|   public spec = new BehaviorSubject<any|null>(null); | ||||
|   private _instance: any; | ||||
|   private _url: string; | ||||
|   private parser: any; | ||||
| 
 | ||||
|   static instance() { | ||||
|     return new SpecManager(); | ||||
|   } | ||||
| 
 | ||||
|   constructor() { | ||||
|     if (SpecManager.prototype._instance) { | ||||
|       return SpecManager.prototype._instance; | ||||
|     } | ||||
| 
 | ||||
|     SpecManager.prototype._instance = this; | ||||
|   } | ||||
| 
 | ||||
|   load(urlOrObject: string|Object) { | ||||
|     this.schema = null; | ||||
|     let promise = new Promise((resolve, reject) => { | ||||
|       this.parser = new JsonSchemaRefParser(); | ||||
|       this.parser.bundle(urlOrObject, {http: {withCredentials: false}}) | ||||
|  |  | |||
							
								
								
									
										7
									
								
								manual-types/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								manual-types/index.d.ts
									
									
									
									
										vendored
									
									
								
							|  | @ -19,4 +19,11 @@ declare var AOT: any; | |||
| interface ErrorStackTraceLimit { | ||||
|   stackTraceLimit: number; | ||||
| } | ||||
| interface History { | ||||
|   scrollRestoration: "auto"|"manual"; | ||||
| } | ||||
| interface Window { | ||||
|   HTMLElement: any | ||||
| } | ||||
| declare var safari: any; | ||||
| interface ErrorConstructor extends ErrorStackTraceLimit {} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "redoc", | ||||
|   "description": "Swagger-generated API Reference Documentation", | ||||
|   "version": "1.5.2", | ||||
|   "version": "1.6.0", | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "git://github.com/Rebilly/ReDoc" | ||||
|  | @ -96,8 +96,8 @@ | |||
|     "ts-helpers": "^1.1.1", | ||||
|     "tslint": "^3.15.1", | ||||
|     "typescript": "^2.0.3", | ||||
|     "webpack": "^2.1.0-beta.25", | ||||
|     "webpack-dev-server": "^2.1.0-beta.10", | ||||
|     "webpack": "^2.1.0-beta.27", | ||||
|     "webpack-dev-server": "^2.1.0-beta.12", | ||||
|     "zone.js": "^0.6.25" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ beforeEach(function() { | |||
|       services.OptionsService, | ||||
|       services.ComponentParser, | ||||
|       services.ContentProjector, | ||||
|       { provide: sharedComponents.LazyTasksService, useClass: sharedComponents.LazyTasksServiceSync }, | ||||
|       { provide: ErrorHandler, useClass: services.CustomErrorHandler }, | ||||
|       { provide: services.COMPONENT_PARSER_ALLOWED, useValue: { 'security-definitions': components.SecurityDefinitions }} | ||||
|     ], | ||||
|  | @ -60,6 +61,14 @@ beforeEach(function() { | |||
|   }); | ||||
| }); | ||||
| 
 | ||||
| // afterEach(function() {
 | ||||
| //     TestBed.resetTestingModule();
 | ||||
| // });
 | ||||
| 
 | ||||
| // afterEach(function() {
 | ||||
| //   TestBed.resetTestEnvironment();
 | ||||
| // })
 | ||||
| 
 | ||||
| 
 | ||||
| var testContext = require.context('..', true, /\.spec\.ts/); | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,11 +9,6 @@ describe('Utils', () => { | |||
|       specMgr = new SpecManager(); | ||||
|     }); | ||||
| 
 | ||||
|     it('Should be a singleton', ()=> { | ||||
|       (new SpecManager()).should.be.equal(specMgr); | ||||
|       SpecManager.instance().should.be.equal(specMgr); | ||||
|     }); | ||||
| 
 | ||||
|     it('load should return a promise', ()=> { | ||||
|       specMgr.load('/tests/schemas/extended-petstore.yml').should.be.instanceof(Promise); | ||||
|     }); | ||||
|  | @ -35,15 +30,10 @@ describe('Utils', () => { | |||
|     }); | ||||
| 
 | ||||
|     describe('Schema manager basic functionality', ()=> { | ||||
|       beforeAll(function (done) { | ||||
|         specMgr.load('/tests/schemas/extended-petstore.yml').then(() => { | ||||
|           done(); | ||||
|         }, () => { | ||||
|           throw new Error('Error handler should not be called'); | ||||
|         }); | ||||
|       beforeEach(function (done) { | ||||
|         specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail); | ||||
|       }); | ||||
| 
 | ||||
| 
 | ||||
|       it('should contain non-empty schema', ()=> { | ||||
|         specMgr.schema.should.be.an.Object(); | ||||
|         specMgr.schema.should.be.not.empty(); | ||||
|  | @ -68,9 +58,9 @@ describe('Utils', () => { | |||
| 
 | ||||
|       it('should substitute api host when spec host is undefined', () => { | ||||
|         specMgr._schema.host = undefined; | ||||
|         specMgr._url = 'https://petstore.swagger.io/v2'; | ||||
|         specMgr._url = 'http://petstore.swagger.io/v2'; | ||||
|         specMgr.init(); | ||||
|         specMgr.apiUrl.should.be.equal('https://petstore.swagger.io/v2'); | ||||
|         specMgr.apiUrl.should.be.equal('http://petstore.swagger.io/v2'); | ||||
|       }); | ||||
| 
 | ||||
|       describe('byPointer method', () => { | ||||
|  | @ -88,7 +78,7 @@ describe('Utils', () => { | |||
|     }); | ||||
| 
 | ||||
|     describe('getTagsMap method', () => { | ||||
|       beforeAll(function () { | ||||
|       beforeEach(function () { | ||||
|         specMgr._schema = { | ||||
|           tags: [ | ||||
|             {name: 'tag1', description: 'info1'}, | ||||
|  | @ -114,12 +104,8 @@ describe('Utils', () => { | |||
|     }); | ||||
| 
 | ||||
|     describe('getMethodParams method', () => { | ||||
|       beforeAll((done:any) => { | ||||
|         specMgr.load('/tests/schemas/schema-mgr-methodparams.json').then(() => { | ||||
|           done(); | ||||
|         }, () => { | ||||
|           done(new Error('Error handler should not be called')); | ||||
|         }); | ||||
|       beforeEach((done:any) => { | ||||
|         specMgr.load('/tests/schemas/schema-mgr-methodparams.json').then(done, done.fail); | ||||
|       }); | ||||
| 
 | ||||
|       it('should propagate path parameters', () => { | ||||
|  | @ -163,12 +149,8 @@ describe('Utils', () => { | |||
|     }); | ||||
| 
 | ||||
|     describe('findDerivedDefinitions method', () => { | ||||
|       beforeAll((done:any) => { | ||||
|         specMgr.load('/tests/schemas/extended-petstore.yml').then(() => { | ||||
|           done(); | ||||
|         }, () => { | ||||
|           done(new Error('Error handler should not be called')); | ||||
|         }); | ||||
|       beforeEach((done) => { | ||||
|         specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail); | ||||
|       }); | ||||
| 
 | ||||
|       it('should find derived definitions for Pet', () => { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user