From 5f38e4bf316dab7ebfada571c20e2c2699b4daa7 Mon Sep 17 00:00:00 2001 From: YinChang Date: Sun, 22 Mar 2020 21:19:43 +0800 Subject: [PATCH 01/64] fix(ng.core) correct params of SetEnvironment Action --- npm/ng-packs/packages/core/src/lib/states/config.state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 5269946391..6843fe2dc6 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -300,7 +300,7 @@ export class ConfigState { } @Action(SetEnvironment) - setEnvironment({ patchState }: StateContext, environment: Config.Environment) { + setEnvironment({ patchState }: StateContext, { environment }:SetEnvironment) { return patchState({ environment, }); From dc93296d6325c9d74b034cbb6472cde164ee2c32 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Mon, 23 Mar 2020 09:21:43 +0300 Subject: [PATCH 02/64] docs: add service-proxies document --- docs/en/UI/Angular/Config-State.md | 4 ++ docs/en/UI/Angular/Permission-Management.md | 2 +- docs/en/UI/Angular/Rest-Service.md | 3 + docs/en/UI/Angular/Service-Proxies.md | 67 ++++++++++++++++++ .../generated-files-via-generate-proxy.png | Bin 0 -> 240566 bytes docs/en/docs-nav.json | 4 ++ 6 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 docs/en/UI/Angular/Rest-Service.md create mode 100644 docs/en/UI/Angular/Service-Proxies.md create mode 100644 docs/en/UI/Angular/images/generated-files-via-generate-proxy.png diff --git a/docs/en/UI/Angular/Config-State.md b/docs/en/UI/Angular/Config-State.md index 72c8e6c6fd..7fc202982c 100644 --- a/docs/en/UI/Angular/Config-State.md +++ b/docs/en/UI/Angular/Config-State.md @@ -288,3 +288,7 @@ Note that **you do not have to call this method at application initiation**, bec #### Environment Properties Please refer to `Config.Environment` type for all the properties you can pass to `dispatchSetEnvironment` as parameter. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L13). + +## What's Next? + +* [Component Replacement](./Component-Replacement.md) \ No newline at end of file diff --git a/docs/en/UI/Angular/Permission-Management.md b/docs/en/UI/Angular/Permission-Management.md index 6e5c5d7155..ababd25e7f 100644 --- a/docs/en/UI/Angular/Permission-Management.md +++ b/docs/en/UI/Angular/Permission-Management.md @@ -76,4 +76,4 @@ Granted Policies are stored in the `auth` property of `ConfigState`. ## What's Next? -* [Component Replacement](./Component-Replacement.md) \ No newline at end of file +* [Config State](./Config-State.md) \ No newline at end of file diff --git a/docs/en/UI/Angular/Rest-Service.md b/docs/en/UI/Angular/Rest-Service.md new file mode 100644 index 0000000000..d817cab3c6 --- /dev/null +++ b/docs/en/UI/Angular/Rest-Service.md @@ -0,0 +1,3 @@ +## Rest Service + +TODO \ No newline at end of file diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md new file mode 100644 index 0000000000..56d640ec54 --- /dev/null +++ b/docs/en/UI/Angular/Service-Proxies.md @@ -0,0 +1,67 @@ +## Service Proxies + +It is common to call a REST endpoint in the server from our Angular applications. In this case, we generally create **services** (those have methods for each service method on the server side) and **model objects** (matches to [DTOs](../../Data-Transfer-Objects) in the server side). + +In addition to manually creating such server-interacting services, we could use tools like [NSWAG](https://github.com/RicoSuter/NSwag) to generate service proxies for us. But NSWAG has the following problems we've experienced: + +* It generates a **big, single** .ts file which has some problems; + * It get **too large** when your application grows. + * It doesn't fit into the **[modular](../../Module-Development-Basics) approach** of the ABP framework. +* It creates a bit **ugly code**. We want to have a clean code (just like if we write manually). +* It can not generate the same **method signature** declared in the server side (because swagger.json doesn't exactly reflect the method signature of the backend service). We've created an endpoint that exposes server side method contacts to allow clients generate a better aligned client proxies. + +ABP CLI `generate-proxies` command automatically generates the typescript client proxies by creating folders which separated by module names in the `src/app` folder. +Run the following command in the **root folder** of the angular application: + +```bash +abp generate-proxy +``` + +It only creates proxies only for your own application's services. It doesn't create proxies for the services of the application modules you're using (by default). There are several options. See the [CLI documentation](../../CLI). + +The files generated with the `--module all` option like below: + +![generated-files-via-generate-proxy](./images/generated-files-via-generate-proxy.png) + +### Services + +Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Rest-Service.md). + + + +The `providedIn` property of the services is defined as `'root'`. Therefore no need to add a service as a provider to a module. You can use a service by injecting it into a constructor as shown below: + +```js +import { AbpApplicationConfigurationService } from '../app/shared/services'; + +//... +export class HomeComponent{ + constructor(private appConfigService: AbpApplicationConfigurationService) {} + + ngOnInit() { + this.appConfigService.get().subscribe() + } +} +``` + +The Angular compiler removes the services that have not been injected anywhere from the final output. See the [tree-shakable providers documentation](https://angular.io/guide/dependency-injection-providers#tree-shakable-providers). + +### Models + +The generated models match the DTOs in the back-end. Each model is generated as a class under the `src/app/*/shared/models` folder. + +There are a few [base classes](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/dtos.ts) in the `@abp/ng.core` package. Some models extend these classes. + +A class instance can be created as shown below: + +```js +import { IdentityRoleCreateDto } from '../identity/shared/models'; +//... +const instance = new IdentityRoleCreateDto({name: 'Role 1', isDefault: false, isPublic: true}) +``` + +Initial values ​​can optionally be passed to each class constructor. + +## What's Next? + +* [Localization](./Localization.md) \ No newline at end of file diff --git a/docs/en/UI/Angular/images/generated-files-via-generate-proxy.png b/docs/en/UI/Angular/images/generated-files-via-generate-proxy.png new file mode 100644 index 0000000000000000000000000000000000000000..77a120a35ddda488b21f575917e7e704ee58b83b GIT binary patch literal 240566 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%0A|x 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%0DB4diPpnh7Tg|E|$rrh!1Im5<36#++7o|kKq-W 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

s%n+a76Wa=Pzhl>ePyT5UAtf=yk2bVSo2A|b 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 i0fT@+z#uRK5cvP!R=^hNR7^?$0000 Date: Tue, 24 Mar 2020 09:53:44 +0300 Subject: [PATCH 03/64] docs: remove rest-service.md and updated the service proxies documentation --- docs/en/UI/Angular/Rest-Service.md | 3 --- docs/en/UI/Angular/Service-Proxies.md | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 docs/en/UI/Angular/Rest-Service.md diff --git a/docs/en/UI/Angular/Rest-Service.md b/docs/en/UI/Angular/Rest-Service.md deleted file mode 100644 index d817cab3c6..0000000000 --- a/docs/en/UI/Angular/Rest-Service.md +++ /dev/null @@ -1,3 +0,0 @@ -## Rest Service - -TODO \ No newline at end of file diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md index 56d640ec54..b1cd9ad500 100644 --- a/docs/en/UI/Angular/Service-Proxies.md +++ b/docs/en/UI/Angular/Service-Proxies.md @@ -25,9 +25,9 @@ The files generated with the `--module all` option like below: ### Services -Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Rest-Service.md). +Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./HTTP-Requests.md#restservice). - +A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](HTTP-Requests#how-to-get-a-specific-api-endpoint-from-application-config) The `providedIn` property of the services is defined as `'root'`. Therefore no need to add a service as a provider to a module. You can use a service by injecting it into a constructor as shown below: @@ -64,4 +64,4 @@ Initial values ​​can optionally be passed to each class constructor. ## What's Next? -* [Localization](./Localization.md) \ No newline at end of file +* [Http Requests](./Http-Requests.md) \ No newline at end of file From 439ca98c8206ca7babff23f748a38a8fab49252a Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 25 Mar 2020 17:53:32 +0300 Subject: [PATCH 04/64] feat(core): make TrackByService publicly available --- npm/ng-packs/packages/core/src/lib/services/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/index.ts b/npm/ng-packs/packages/core/src/lib/services/index.ts index e7f7f9b985..ad23b74fae 100644 --- a/npm/ng-packs/packages/core/src/lib/services/index.ts +++ b/npm/ng-packs/packages/core/src/lib/services/index.ts @@ -3,7 +3,8 @@ export * from './auth.service'; export * from './config-state.service'; export * from './lazy-load.service'; export * from './localization.service'; +export * from './profile-state.service'; export * from './profile.service'; export * from './rest.service'; -export * from './profile-state.service'; export * from './session-state.service'; +export * from './track-by.service'; From 75a13a0aa4acab0471e92ca95a1904a9ed4efd9e Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 25 Mar 2020 17:54:11 +0300 Subject: [PATCH 05/64] feat(core): add doubly linked list as shared utility --- .../core/src/lib/tests/linked-list.spec.ts | 720 ++++++++++++++++++ .../packages/core/src/lib/utils/index.ts | 1 + .../core/src/lib/utils/linked-list.ts | 240 ++++++ 3 files changed, 961 insertions(+) create mode 100644 npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts create mode 100644 npm/ng-packs/packages/core/src/lib/utils/linked-list.ts diff --git a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts new file mode 100644 index 0000000000..cd0a00096e --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts @@ -0,0 +1,720 @@ +import { LinkedList } from '../utils/linked-list'; + +describe('Linked List (Doubly)', () => { + let list: LinkedList; + + beforeEach(() => (list = new LinkedList())); + + describe('#length', () => { + it('should initially be 0', () => { + expect(list.length).toBe(0); + }); + }); + + describe('#head', () => { + it('should initially be undefined', () => { + expect(list.head).toBeUndefined(); + }); + }); + + describe('#tail', () => { + it('should initially be undefined', () => { + expect(list.tail).toBeUndefined(); + }); + }); + + describe('#add', () => { + describe('#head', () => { + it('should add node to the head of the list', () => { + list.addHead('a'); + + // "a" + + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + }); + + it('should create reference to previous and next nodes', () => { + list.add('a').head(); + list.add('b').head(); + list.add('c').head(); + + // "c" <-> "b" <-> "a" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('c'); + expect(list.head.next.value).toBe('b'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('a'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.next).toBeUndefined(); + }); + }); + + describe('#tail', () => { + it('should add node to the tail of the list', () => { + list.addTail('a'); + + // "a" + + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + expect(list.tail.next).toBeUndefined(); + }); + + it('should create reference to previous and next nodes', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.next).toBeUndefined(); + }); + }); + + describe('#after', () => { + it('should place a node after node with given value', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.add('x').after('b'); + + // "a" <-> "b" <-> "x" <-> "c" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('x'); + expect(list.head.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('x'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('a'); + }); + + it('should be able to receive a custom compareFn', () => { + list.add({ x: 1 }).tail(); + list.add({ x: 2 }).tail(); + list.add({ x: 3 }).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + list.add({ x: 0 }).after({ x: 1 }, (v1: X, v2: X) => v1.x === v2.x); + + // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} + + expect(list.length).toBe(4); + expect(list.head.value.x).toBe(1); + expect(list.head.next.value.x).toBe(0); + expect(list.head.next.next.value.x).toBe(2); + expect(list.head.next.next.next.value.x).toBe(3); + expect(list.tail.value.x).toBe(3); + expect(list.tail.previous.value.x).toBe(2); + expect(list.tail.previous.previous.value.x).toBe(0); + expect(list.tail.previous.previous.previous.value.x).toBe(1); + }); + }); + + describe('#before', () => { + it('should place a node before node with given value', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.add('x').before('b'); + + // "a" <-> "x" <-> "b" <-> "c" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('x'); + expect(list.head.next.next.value).toBe('b'); + expect(list.head.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.previous.value).toBe('a'); + }); + + it('should be able to receive a custom compareFn', () => { + list.add({ x: 1 }).tail(); + list.add({ x: 2 }).tail(); + list.add({ x: 3 }).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + list.add({ x: 0 }).before({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); + + // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} + + expect(list.length).toBe(4); + expect(list.head.value.x).toBe(1); + expect(list.head.next.value.x).toBe(0); + expect(list.head.next.next.value.x).toBe(2); + expect(list.head.next.next.next.value.x).toBe(3); + expect(list.tail.value.x).toBe(3); + expect(list.tail.previous.value.x).toBe(2); + expect(list.tail.previous.previous.value.x).toBe(0); + expect(list.tail.previous.previous.previous.value.x).toBe(1); + }); + }); + + describe('#byIndex', () => { + it('should place a node at given index', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.add('x').byIndex(1); + + // "a" <-> "x" <-> "b" <-> "c" + + list.add('y').byIndex(3); + + // "a" <-> "x" <-> "b" <-> "y" <-> "c" + + expect(list.length).toBe(5); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('x'); + expect(list.head.next.next.value).toBe('b'); + expect(list.head.next.next.next.value).toBe('y'); + expect(list.head.next.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('y'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.previous.previous.value).toBe('a'); + }); + }); + }); + + describe('#find', () => { + it('should return the first node found based on given predicate', () => { + list.add('a').tail(); + list.add('x').tail(); + list.add('b').tail(); + list.add('x').tail(); + list.add('c').tail(); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + const node1 = list.find(value => value === 'x'); + + expect(node1.value).toBe('x'); + expect(node1.previous.value).toBe('a'); + expect(node1.next.value).toBe('b'); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + const node2 = list.find((_, index) => index === 3); + + expect(node2.value).toBe('x'); + expect(node2.previous.value).toBe('b'); + expect(node2.next.value).toBe('c'); + }); + + it('should return undefined when list is empty', () => { + const node = list.find(value => value === 'x'); + + expect(node).toBeUndefined(); + }); + + it('should return undefined when predicate finds no match', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const node = list.find(value => value === 'x'); + + expect(node).toBeUndefined(); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + }); + + describe('#findIndex', () => { + it('should return the index of the first node found based on given predicate', () => { + list.add('a').tail(); + list.add('x').tail(); + list.add('b').tail(); + list.add('x').tail(); + list.add('c').tail(); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + const index1 = list.findIndex(value => value === 'x'); + + expect(index1).toBe(1); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + let timesFound = 0; + const index2 = list.findIndex(value => { + if (timesFound > 1) return false; + + timesFound += Number(value === 'x'); + + return timesFound > 1; + }); + + expect(index2).toBe(3); + }); + + it('should return -1 when list is empty', () => { + const index = list.findIndex(value => value === 'x'); + + expect(index).toBe(-1); + }); + + it('should return -1 when no match is found', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const index = list.findIndex(value => value === 'x'); + + expect(index).toBe(-1); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + }); + + describe('#forEach', () => { + it('should call given function for each node of the list', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const spy = jest.fn(); + list.forEach(spy); + + expect(spy.mock.calls).toEqual([ + ['a', 0, list], + ['b', 1, list], + ['c', 2, list], + ]); + }); + + it('should not call given function when list is empty', () => { + const spy = jest.fn(); + list.forEach(spy); + + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('#drop', () => { + describe('#head', () => { + it('should return undefined when there is no head', () => { + expect(list.drop().head()).toBeUndefined(); + }); + + it('should remove the node from the head of the list', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.drop().head(); + + // "b" <-> "c" + + expect(list.length).toBe(2); + expect(list.head.value).toBe('b'); + expect(list.head.next.value).toBe('c'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.next).toBeUndefined(); + }); + }); + + describe('#head', () => { + it('should return undefined when there is no tail', () => { + expect(list.drop().tail()).toBeUndefined(); + }); + + it('should remove the node from the tail of the list', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.drop().tail(); + + // "a" <-> "b" + + expect(list.length).toBe(2); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('b'); + expect(list.tail.previous.value).toBe('a'); + expect(list.tail.next).toBeUndefined(); + }); + }); + + describe('#byIndex', () => { + it('should remove the node at given index', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + list.add('d').tail(); + list.add('e').tail(); + + // "a" <-> "b" <-> "c" <-> "d" <-> "e" + + list.drop().byIndex(1); + + // "a" <-> "c" <-> "d" <-> "e" + + list.drop().byIndex(2); + + // "a" <-> "c" <-> "e" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('c'); + expect(list.head.next.next.value).toBe('e'); + expect(list.tail.value).toBe('e'); + expect(list.tail.previous.value).toBe('c'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should return undefined when list is empty', () => { + const node = list.drop().byIndex(0); + expect(node).toBeUndefined(); + }); + + it('should return undefined when given index does not exist', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const node1 = list.drop().byIndex(4); + + // "a" <-> "b" <-> "c" + + expect(node1).toBeUndefined(); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + + // "a" <-> "b" <-> "c" + + const node2 = list.drop().byIndex(-1); + + // "a" <-> "b" <-> "c" + + expect(node2).toBeUndefined(); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + }); + + describe('#byValue', () => { + it('should remove the first node with given value', () => { + list.add('a').tail(); + list.add('x').tail(); + list.add('b').tail(); + list.add('x').tail(); + list.add('c').tail(); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + list.drop().byValue('x'); + + // "a" <-> "b" <-> "x" <-> "c" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('x'); + expect(list.head.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('x'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('a'); + + // "a" <-> "b" <-> "x" <-> "c" + + list.drop().byValue('x'); + + // "a" <-> "b" <-> "c" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should be able to receive a custom compareFn', () => { + list.add({ x: 1 }).tail(); + list.add({ x: 2 }).tail(); + list.add({ x: 3 }).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + list.drop().byValue({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); + + // {"x":1} <-> {"x":3} + + expect(list.length).toBe(2); + expect(list.head.value.x).toBe(1); + expect(list.head.next.value.x).toBe(3); + expect(list.tail.value.x).toBe(3); + expect(list.tail.previous.value.x).toBe(1); + }); + + it('should return undefined when list is empty', () => { + const node = list.drop().byValue('x'); + expect(node).toBeUndefined(); + }); + + it('should return undefined when given value is not found', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const node = list.drop().byValue('x'); + + // "a" <-> "b" <-> "c" + + expect(node).toBeUndefined(); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + }); + }); + + describe('#get', () => { + it('should return node at given index', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const node = list.get(1); + + expect(node.value).toBe('b'); + expect(node.previous.value).toBe('a'); + expect(node.next.value).toBe('c'); + }); + + it('should return undefined when list is empty', () => { + const node = list.get(1); + + expect(node).toBeUndefined(); + }); + + it('should return undefined when predicate finds no match', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const node1 = list.get(4); + + expect(node1).toBeUndefined(); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + + // "a" <-> "b" <-> "c" + + const node2 = list.get(-1); + + expect(node2).toBeUndefined(); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + }); + + describe('#indexOf', () => { + it('should return the index of the first node found based on given value', () => { + list.add('a').tail(); + list.add('x').tail(); + list.add('b').tail(); + list.add('x').tail(); + list.add('c').tail(); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + const index1 = list.indexOf('x'); + + expect(index1).toBe(1); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + let timesFound = 0; + const index2 = list.indexOf('x', (v1: string, v2: string) => { + if (timesFound > 1) return false; + + timesFound += Number(v1 === v2); + + return timesFound > 1; + }); + + expect(index2).toBe(3); + }); + + it('should be able to receive a custom compareFn', () => { + list.add({ x: 1 }).tail(); + list.add({ x: 2 }).tail(); + list.add({ x: 3 }).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + const index = list.indexOf({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); + + expect(index).toBe(1); + }); + + it('should return -1 when list is empty', () => { + const index = list.indexOf('x'); + + expect(index).toBe(-1); + }); + + it('should return -1 when no match is found', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const index = list.indexOf('x'); + + expect(index).toBe(-1); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + }); + + describe('#toArray', () => { + it('should return array representation', () => { + list.addTail('a'); + list.addTail(2); + list.addTail('c'); + list.addTail({ k: 4, v: 'd' }); + + // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} + + const arr = list.toArray(); + expect(arr).toEqual(['a', 2, 'c', { k: 4, v: 'd' }]); + }); + + it('should return empty array when list is empty', () => { + const arr = list.toArray(); + expect(arr).toEqual([]); + }); + }); + + describe('#toString', () => { + it('should return string representation', () => { + list.addTail('a'); + list.addTail(2); + list.addTail('c'); + list.addTail({ k: 4, v: 'd' }); + + // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} + + const str = list.toString(); + expect(str).toBe('"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}'); + }); + + it('should return empty string when list is empty', () => { + const str = list.toString(); + expect(str).toBe(''); + }); + }); + + it('should be iterable', () => { + list.addTail('a'); + list.addTail('b'); + list.addTail('c'); + + // "a" <-> "b" <-> "c" + + const arr = []; + + for (let value of list) { + arr.push(value); + } + + expect(arr).toEqual(['a', 'b', 'c']); + }); +}); + +interface X { + [k: string]: any; +} diff --git a/npm/ng-packs/packages/core/src/lib/utils/index.ts b/npm/ng-packs/packages/core/src/lib/utils/index.ts index 0043152ada..a112d9acd8 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/index.ts @@ -1,5 +1,6 @@ export * from './common-utils'; export * from './generator-utils'; export * from './initial-utils'; +export * from './linked-list'; export * from './route-utils'; export * from './rxjs-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts new file mode 100644 index 0000000000..f5a027e882 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts @@ -0,0 +1,240 @@ +import compare from 'just-compare'; + +export class ListNode { + readonly value: T; + next: ListNode | undefined; + previous: ListNode | undefined; + + constructor(value: T) { + this.value = value; + } +} + +export class LinkedList { + private first: ListNode | undefined; + private last: ListNode | undefined; + private size = 0; + + get head(): ListNode | undefined { + return this.first; + } + get tail(): ListNode | undefined { + return this.last; + } + get length(): number { + return this.size; + } + + private linkWith(value: T, previousNode: ListNode, nextNode: ListNode): ListNode { + const node = new ListNode(value); + + if (!previousNode) return this.addHead(value); + if (!nextNode) return this.addTail(value); + + node.previous = previousNode; + previousNode.next = node; + node.next = nextNode; + nextNode.previous = node; + + this.size += 1; + + return node; + } + + add(value: T) { + return { + after: (previousValue: T, compareFn = compare) => { + return this.addAfter(value, previousValue, compareFn); + }, + before: (nextValue: T, compareFn = compare) => { + return this.addBefore(value, nextValue, compareFn); + }, + byIndex: (position: number): ListNode => { + return this.addByIndex(value, position); + }, + head: (): ListNode => { + return this.addHead(value); + }, + tail: (): ListNode => { + return this.addTail(value); + }, + }; + } + + addAfter(value: T, previousValue: T, compareFn = compare): ListNode { + const previous = this.find(currentValue => compareFn(currentValue, previousValue)); + + return previous ? this.linkWith(value, previous, previous.next) : this.addTail(value); + } + + addBefore(value: T, nextValue: T, compareFn = compare): ListNode { + const next = this.find(currentValue => compareFn(currentValue, nextValue)); + + return next ? this.linkWith(value, next.previous, next) : this.addHead(value); + } + + addByIndex(value: T, position: number): ListNode { + if (position <= 0) return this.addHead(value); + if (position >= this.size) return this.addTail(value); + + const next = this.get(position)!; + + return this.linkWith(value, next.previous, next); + } + + addHead(value: T): ListNode { + const node = new ListNode(value); + + node.next = this.first; + + if (this.first) this.first.previous = node; + else this.last = node; + + this.first = node; + this.size += 1; + + return node; + } + + addTail(value: T): ListNode { + const node = new ListNode(value); + + if (this.first) { + node.previous = this.last; + this.last!.next = node; + this.last = node; + } else { + this.first = node; + this.last = node; + } + + this.size += 1; + + return node; + } + + drop() { + return { + byIndex: (position: number) => this.dropByIndex(position), + byValue: (value: T, compareFn = compare) => this.dropByValue(value, compareFn), + head: () => this.dropHead(), + tail: () => this.dropTail(), + }; + } + + dropByIndex(position: number): ListNode | undefined { + if (position === 0) return this.dropHead(); + else if (position === this.size - 1) return this.dropTail(); + + const current = this.get(position); + + if (current) { + current.previous!.next = current.next; + current.next!.previous = current.previous; + + this.size -= 1; + + return current; + } + + return undefined; + } + + dropByValue(value: T, compareFn = compare): ListNode | undefined { + const position = this.findIndex(currentValue => compareFn(currentValue, value)); + + if (position < 0) return undefined; + + return this.dropByIndex(position); + } + + dropHead(): ListNode | undefined { + const head = this.first; + + if (head) { + this.first = head.next; + + if (this.first) this.first.previous = undefined; + else this.last = undefined; + + this.size -= 1; + + return head; + } + + return undefined; + } + + dropTail(): ListNode | undefined { + const tail = this.last; + + if (tail) { + this.last = tail.previous; + + if (this.last) this.last.next = undefined; + else this.first = undefined; + + this.size -= 1; + + return tail; + } + + return undefined; + } + + find(predicate: ListIteratorFunction): ListNode | undefined { + for (let current = this.first, position = 0; current; position += 1, current = current.next) { + if (predicate(current.value, position, this)) return current; + } + + return undefined; + } + + findIndex(predicate: ListIteratorFunction): number { + for (let current = this.first, position = 0; current; position += 1, current = current.next) { + if (predicate(current.value, position, this)) return position; + } + + return -1; + } + + forEach(callback: ListIteratorFunction) { + for (let node = this.first, position = 0; node; position += 1, node = node.next) { + callback(node.value, position, this); + } + } + + get(position: number): ListNode | undefined { + return this.find((_, index) => position === index); + } + + indexOf(value: T, compareFn = compare): number { + return this.findIndex(currentValue => compareFn(currentValue, value)); + } + + toArray(): T[] { + const array = new Array(this.size); + + this.forEach((value, index) => (array[index!] = value)); + + return array; + } + + toString(): string { + return this.toArray() + .map(value => JSON.stringify(value)) + .join(' <-> '); + } + + *[Symbol.iterator]() { + for (let node = this.first, position = 0; node; position += 1, node = node.next) { + yield node.value; + } + } +} + +export type ListIteratorFunction = ( + value: T, + index?: number, + list?: LinkedList, +) => R; From d56f2c7f9453c54069d63b601fd31f73bb97b5ed Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 25 Mar 2020 19:01:09 +0300 Subject: [PATCH 06/64] feat(core): add dropByValueAll to LinkedList --- .../core/src/lib/tests/linked-list.spec.ts | 89 +++++++++++++++++++ .../core/src/lib/utils/linked-list.ts | 13 +++ 2 files changed, 102 insertions(+) diff --git a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts index cd0a00096e..77b748cf0d 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts @@ -538,6 +538,95 @@ describe('Linked List (Doubly)', () => { expect(list.tail.previous.previous.value).toBe('a'); }); }); + + describe('#byValueAll', () => { + it('should remove all nodes with given value', () => { + list.add('a').tail(); + list.add('x').tail(); + list.add('b').tail(); + list.add('x').tail(); + list.add('c').tail(); + + // "a" <-> "x" <-> "b" <-> "x" <-> "c" + + const dropped = list.drop().byValueAll('x'); + + // "a" <-> "b" <-> "c" + + expect(dropped.length).toBe(2); + expect(dropped[0].value).toEqual('x'); + expect(dropped[0].previous.value).toEqual('a'); + expect(dropped[0].next.value).toEqual('b'); + expect(dropped[1].value).toEqual('x'); + expect(dropped[1].previous.value).toEqual('b'); + expect(dropped[1].next.value).toEqual('c'); + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should be able to receive a custom compareFn', () => { + list.add({ x: 1 }).tail(); + list.add({ x: 0 }).tail(); + list.add({ x: 2 }).tail(); + list.add({ x: 0 }).tail(); + list.add({ x: 3 }).tail(); + + // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + + const dropped = list.drop().byValueAll({ x: 0 }, (v1: X, v2: X) => v1.x === v2.x); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + expect(dropped.length).toBe(2); + expect(dropped[0].value.x).toEqual(0); + expect(dropped[0].previous.value.x).toEqual(1); + expect(dropped[0].next.value.x).toEqual(2); + expect(dropped[1].value.x).toEqual(0); + expect(dropped[1].previous.value.x).toEqual(2); + expect(dropped[1].next.value.x).toEqual(3); + + expect(list.length).toBe(3); + expect(list.head.value.x).toBe(1); + expect(list.head.next.value.x).toBe(2); + expect(list.head.next.next.value.x).toBe(3); + expect(list.tail.value.x).toBe(3); + expect(list.tail.previous.value.x).toBe(2); + expect(list.tail.previous.previous.value.x).toBe(1); + }); + + it('should return empty array when list is empty', () => { + const dropped = list.drop().byValueAll('x'); + expect(dropped).toEqual([]); + }); + + it('should return empty array when given value is not found', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + const dropped = list.drop().byValueAll('x'); + + // "a" <-> "b" <-> "c" + + expect(dropped).toEqual([]); + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + }); }); describe('#get', () => { diff --git a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts index f5a027e882..9c47a6ad9b 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts @@ -117,6 +117,7 @@ export class LinkedList { return { byIndex: (position: number) => this.dropByIndex(position), byValue: (value: T, compareFn = compare) => this.dropByValue(value, compareFn), + byValueAll: (value: T, compareFn = compare) => this.dropByValueAll(value, compareFn), head: () => this.dropHead(), tail: () => this.dropTail(), }; @@ -148,6 +149,18 @@ export class LinkedList { return this.dropByIndex(position); } + dropByValueAll(value: T, compareFn = compare): ListNode[] { + const dropped: ListNode[] = []; + + for (let current = this.first, position = 0; current; position += 1, current = current.next) { + if (compareFn(current.value, value)) { + dropped.push(this.dropByIndex(position - dropped.length)); + } + } + + return dropped; + } + dropHead(): ListNode | undefined { const head = this.first; From 6599c2a6762ea5ae6b27dfd6bc7db88c0ec3b593 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 25 Mar 2020 19:08:55 +0300 Subject: [PATCH 07/64] fix(core): avoid lint errors --- .../packages/core/src/lib/tests/linked-list.spec.ts | 2 +- .../packages/core/src/lib/utils/linked-list.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts index 77b748cf0d..d52050c0ad 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts @@ -796,7 +796,7 @@ describe('Linked List (Doubly)', () => { const arr = []; - for (let value of list) { + for (const value of list) { arr.push(value); } diff --git a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts index 9c47a6ad9b..6d2435acdb 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts @@ -1,3 +1,5 @@ +/* tslint:disable:no-non-null-assertion */ + import compare from 'just-compare'; export class ListNode { @@ -25,7 +27,11 @@ export class LinkedList { return this.size; } - private linkWith(value: T, previousNode: ListNode, nextNode: ListNode): ListNode { + private linkWith( + value: T, + previousNode: ListNode | undefined, + nextNode: ListNode | undefined, + ): ListNode { const node = new ListNode(value); if (!previousNode) return this.addHead(value); @@ -154,7 +160,7 @@ export class LinkedList { for (let current = this.first, position = 0; current; position += 1, current = current.next) { if (compareFn(current.value, value)) { - dropped.push(this.dropByIndex(position - dropped.length)); + dropped.push(this.dropByIndex(position - dropped.length)!); } } From 026fb1defba37b72f6353994f356ea8603ddf2f5 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 25 Mar 2020 19:48:28 +0300 Subject: [PATCH 08/64] feat(core): iterate over linked list nodes instead of node values --- .../core/src/lib/tests/linked-list.spec.ts | 39 ++++++++----------- .../core/src/lib/utils/linked-list.ts | 18 ++++----- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts index d52050c0ad..518956deba 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts @@ -212,7 +212,7 @@ describe('Linked List (Doubly)', () => { // "a" <-> "x" <-> "b" <-> "x" <-> "c" - const node1 = list.find(value => value === 'x'); + const node1 = list.find(node => node.previous && node.previous.value === 'a'); expect(node1.value).toBe('x'); expect(node1.previous.value).toBe('a'); @@ -220,7 +220,7 @@ describe('Linked List (Doubly)', () => { // "a" <-> "x" <-> "b" <-> "x" <-> "c" - const node2 = list.find((_, index) => index === 3); + const node2 = list.find(node => node.next && node.next.value === 'c'); expect(node2.value).toBe('x'); expect(node2.previous.value).toBe('b'); @@ -228,9 +228,9 @@ describe('Linked List (Doubly)', () => { }); it('should return undefined when list is empty', () => { - const node = list.find(value => value === 'x'); + const found = list.find(node => node.value === 'x'); - expect(node).toBeUndefined(); + expect(found).toBeUndefined(); }); it('should return undefined when predicate finds no match', () => { @@ -240,9 +240,9 @@ describe('Linked List (Doubly)', () => { // "a" <-> "b" <-> "c" - const node = list.find(value => value === 'x'); + const found = list.find(node => node.value === 'x'); - expect(node).toBeUndefined(); + expect(found).toBeUndefined(); expect(list.length).toBe(3); expect(list.head.value).toBe('a'); expect(list.head.next.value).toBe('b'); @@ -263,26 +263,19 @@ describe('Linked List (Doubly)', () => { // "a" <-> "x" <-> "b" <-> "x" <-> "c" - const index1 = list.findIndex(value => value === 'x'); + const index1 = list.findIndex(node => node.previous && node.previous.value === 'a'); expect(index1).toBe(1); // "a" <-> "x" <-> "b" <-> "x" <-> "c" - let timesFound = 0; - const index2 = list.findIndex(value => { - if (timesFound > 1) return false; - - timesFound += Number(value === 'x'); - - return timesFound > 1; - }); + const index2 = list.findIndex(node => node.next && node.next.value === 'c'); expect(index2).toBe(3); }); it('should return -1 when list is empty', () => { - const index = list.findIndex(value => value === 'x'); + const index = list.findIndex(node => node.value === 'x'); expect(index).toBe(-1); }); @@ -294,7 +287,7 @@ describe('Linked List (Doubly)', () => { // "a" <-> "b" <-> "c" - const index = list.findIndex(value => value === 'x'); + const index = list.findIndex(node => node.value === 'x'); expect(index).toBe(-1); expect(list.length).toBe(3); @@ -309,9 +302,9 @@ describe('Linked List (Doubly)', () => { describe('#forEach', () => { it('should call given function for each node of the list', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); + const a = list.add('a').tail(); + const b = list.add('b').tail(); + const c = list.add('c').tail(); // "a" <-> "b" <-> "c" @@ -319,9 +312,9 @@ describe('Linked List (Doubly)', () => { list.forEach(spy); expect(spy.mock.calls).toEqual([ - ['a', 0, list], - ['b', 1, list], - ['c', 2, list], + [a, 0, list], + [b, 1, list], + [c, 2, list], ]); }); diff --git a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts index 6d2435acdb..42cc5bc941 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts @@ -68,13 +68,13 @@ export class LinkedList { } addAfter(value: T, previousValue: T, compareFn = compare): ListNode { - const previous = this.find(currentValue => compareFn(currentValue, previousValue)); + const previous = this.find(node => compareFn(node.value, previousValue)); return previous ? this.linkWith(value, previous, previous.next) : this.addTail(value); } addBefore(value: T, nextValue: T, compareFn = compare): ListNode { - const next = this.find(currentValue => compareFn(currentValue, nextValue)); + const next = this.find(node => compareFn(node.value, nextValue)); return next ? this.linkWith(value, next.previous, next) : this.addHead(value); } @@ -148,7 +148,7 @@ export class LinkedList { } dropByValue(value: T, compareFn = compare): ListNode | undefined { - const position = this.findIndex(currentValue => compareFn(currentValue, value)); + const position = this.findIndex(node => compareFn(node.value, value)); if (position < 0) return undefined; @@ -203,7 +203,7 @@ export class LinkedList { find(predicate: ListIteratorFunction): ListNode | undefined { for (let current = this.first, position = 0; current; position += 1, current = current.next) { - if (predicate(current.value, position, this)) return current; + if (predicate(current, position, this)) return current; } return undefined; @@ -211,7 +211,7 @@ export class LinkedList { findIndex(predicate: ListIteratorFunction): number { for (let current = this.first, position = 0; current; position += 1, current = current.next) { - if (predicate(current.value, position, this)) return position; + if (predicate(current, position, this)) return position; } return -1; @@ -219,7 +219,7 @@ export class LinkedList { forEach(callback: ListIteratorFunction) { for (let node = this.first, position = 0; node; position += 1, node = node.next) { - callback(node.value, position, this); + callback(node, position, this); } } @@ -228,13 +228,13 @@ export class LinkedList { } indexOf(value: T, compareFn = compare): number { - return this.findIndex(currentValue => compareFn(currentValue, value)); + return this.findIndex(node => compareFn(node.value, value)); } toArray(): T[] { const array = new Array(this.size); - this.forEach((value, index) => (array[index!] = value)); + this.forEach((node, index) => (array[index!] = node.value)); return array; } @@ -253,7 +253,7 @@ export class LinkedList { } export type ListIteratorFunction = ( - value: T, + node: ListNode, index?: number, list?: LinkedList, ) => R; From 81a3f6b8563b365ee8644f9b3e017fd357c0840c Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Wed, 25 Mar 2020 20:59:52 +0300 Subject: [PATCH 09/64] docs(core): add how to create and use a LinkedList --- docs/en/UI/Angular/Linked-List.md | 844 ++++++++++++++++++++++++++++++ 1 file changed, 844 insertions(+) create mode 100644 docs/en/UI/Angular/Linked-List.md diff --git a/docs/en/UI/Angular/Linked-List.md b/docs/en/UI/Angular/Linked-List.md new file mode 100644 index 0000000000..c3fb307806 --- /dev/null +++ b/docs/en/UI/Angular/Linked-List.md @@ -0,0 +1,844 @@ +# Linked List (Doubly) + + + +The core module provides a useful data structure known as a [doubly linked list](https://en.wikipedia.org/wiki/Doubly_linked_list). Briefly, a doubly linked list is a series of records (a.k.a. nodes) which has information on the previous node, the next node, and its own value (or data). + + + +## Getting Started + +To create a doubly linked list, all you have to do is to import and create a new instance of it: + +```js +import { LinkedList } from '@abp/ng.core'; + +const list = new LinkedList(); +``` + + + +The constructor does not get any parameters. + + + +## Usage + +### How to Add New Nodes + +There are a few methods to create new nodes in a linked list and all of them are separately available as well as revealed from an `add` method. + + + +#### addHead(value: T): ListNode\ + +Adds a node with given value as the first node in list: + +```js +list.addHead('a'); + +// "a" + +list.addHead('b'); + +// "b" <-> "a" + +list.addHead('c'); + +// "c" <-> "b" <-> "a" +``` + + + +#### addTail(value: T): ListNode\ + +Adds a node with given value as the last node in list: + +```js +list.addTail('a'); + +// "a" + +list.addTail('b'); + +// "a" <-> "b" + +list.addTail('c'); + +// "a" <-> "b" <-> "c" +``` + + + +#### addAfter(value: T, previousValue: T, compareFn = compare): ListNode\ + +Adds a node with given value after the first node that has the previous value: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addAfter('x', 'b'); + +// "a" <-> "b" <-> "x" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addTail({ x: 1 }); +list.addTail({ x: 2 }); +list.addTail({ x: 3 }); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addAfter({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + + +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### addBefore(value: T, nextValue: T, compareFn = compare): ListNode\ + +Adds a node with given value before the first node that has the next value: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addBefore('x', 'b'); + +// "a" <-> "x" <-> "b" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addTail({ x: 1 }); +list.addTail({ x: 2 }); +list.addTail({ x: 3 }); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addBefore({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} +``` + + + +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### addByIndex(value: T, position: number): ListNode\ + +Adds a node with given value at the specified position in the list: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.addByIndex('x', 2); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +#### add(value: T).head(): ListNode\ + +Adds a node with given value as the first node in list: + +```js +list.add('a').head(); + +// "a" + +list.add('b').head(); + +// "b" <-> "a" + +list.add('c').head(); + +// "c" <-> "b" <-> "a" +``` + + + +> This is an alternative API for `addHead`. + + + +#### add(value: T).tail(): ListNode\ + +Adds a node with given value as the last node in list: + +```js +list.add('a').tail(); + +// "a" + +list.add('b').tail(); + +// "a" <-> "b" + +list.add('c').tail(); + +// "a" <-> "b" <-> "c" +``` + + + +> This is an alternative API for `addTail`. + + + +#### add(value: T).after(previousValue: T, compareFn = compare): ListNode\ + +Adds a node with given value after the first node that has the previous value: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.add('x').after('b'); + +// "a" <-> "b" <-> "x" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.add({ x: 1 }).tail(); +list.add({ x: 2 }).tail(); +list.add({ x: 3 }).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.add({ x: 0 }).after({ x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + + +> This is an alternative API for `addAfter`. +> +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### add(value: T).before(nextValue: T, compareFn = compare): ListNode\ + +Adds a node with given value before the first node that has the next value: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.add('x').before('b'); + +// "a" <-> "x" <-> "b" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.add({ x: 1 }).tail(); +list.add({ x: 2 }).tail(); +list.add({ x: 3 }).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.add({ x: 0 }).before({ x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} +``` + + + +> This is an alternative API for `addBefore`. +> +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### add(value: T).byIndex(position: number): ListNode\ + +Adds a node with given value at the specified position in the list: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "c" + +list.add('x').byIndex(2); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +> This is an alternative API for `addByIndex`. + + + +### How to Remove Nodes + +There are a few methods to remove nodes from a linked list and all of them are separately available as well as revealed from a `drop` method. + + + +#### dropHead(): ListNode\ | undefined + +Removes the first node from the list: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.dropHead(); + +// "b" <-> "c" +``` + + + +#### dropTail(): ListNode\ | undefined + +Removes the last node from the list: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.dropTail(); + +// "a" <-> "b" +``` + + + +#### dropByIndex(position: number): ListNode\ | undefined + +Removes the node with the specified position from the list: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.dropByIndex(1); + +// "a" <-> "c" +``` + + + +#### dropByValue(value: T, compareFn = compare): ListNode\ | undefined + +Removes the first node with given value from the list: + +```js +list.addTail('a'); +list.addTail('x'); +list.addTail('b'); +list.addTail('x'); +list.addTail('c'); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.dropByValue('x'); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addTail({ x: 1 }); +list.addTail({ x: 0 }); +list.addTail({ x: 2 }); +list.addTail({ x: 0 }); +list.addTail({ x: 3 }); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + + +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### dropByValueAll(value: T, compareFn = compare): ListNode\\[\] + +Removes all nodes with given value from the list: + +```js +list.addTail('a'); +list.addTail('x'); +list.addTail('b'); +list.addTail('x'); +list.addTail('c'); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.dropByValueAll('x'); + +// "a" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addTail({ x: 1 }); +list.addTail({ x: 0 }); +list.addTail({ x: 2 }); +list.addTail({ x: 0 }); +list.addTail({ x: 3 }); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":3} +``` + + + +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### drop().head(): ListNode\ | undefined + +Removes the first node in list: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "c" + +list.drop().head(); + +// "b" <-> "c" +``` + + + +> This is an alternative API for `dropHead`. + + + +#### drop().tail(): ListNode\ | undefined + +Removes the last node in list: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "c" + +list.drop().tail(); + +// "a" <-> "b" +``` + + + +> This is an alternative API for `dropTail`. + + + +#### drop().byIndex(position: number): ListNode\ | undefined + +Removes the node with the specified position from the list: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "c" + +list.drop().byIndex(1); + +// "a" <-> "c" +``` + + + +> This is an alternative API for `dropByIndex`. + + + +#### drop().byValue(value: T, compareFn = compare): ListNode\ | undefined + +Removes the first node with given value from the list: + +```js +list.add('a').tail(); +list.add('x').tail(); +list.add('b').tail(); +list.add('x').tail(); +list.add('c').tail(); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.drop().byValue('x'); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.add({ x: 1 }).tail(); +list.add({ x: 0 }).tail(); +list.add({ x: 2 }).tail(); +list.add({ x: 0 }).tail(); +list.add({ x: 3 }).tail(); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list.drop().byValue({ x: 0 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + + +> This is an alternative API for `dropByValue`. +> +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### drop().byValueAll(value: T, compareFn = compare): ListNode\\[\] + +Removes all nodes with given value from the list: + +```js +list.add('a').tail(); +list.add('x').tail(); +list.add('b').tail(); +list.add('x').tail(); +list.add('c').tail(); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.drop().byValueAll('x'); + +// "a" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.add({ x: 1 }).tail(); +list.add({ x: 0 }).tail(); +list.add({ x: 2 }).tail(); +list.add({ x: 0 }).tail(); +list.add({ x: 3 }).tail(); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list.drop().byValueAll({ x: 0 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":3} +``` + + + +> This is an alternative API for `dropByValueAll`. +> +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +### How to Find Nodes + +There are a few methods to find specific nodes in a linked list. + + + +#### find(predicate: ListIteratorFunction\): ListNode\ | undefined + +Finds the first node from the list that matches the given predicate: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "b" <-> "c" + +const found = list.find(node => node.value === 'b'); + +/* +found.value === "b" +found.previous.value === "a" +found.next.value === "b" +*/ +``` + + + +#### findIndex(predicate: ListIteratorFunction\): number + +Finds the position of the first node from the list that matches the given predicate: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "b" <-> "c" + +const i0 = list.findIndex(node => node.next && node.next.value === 'b'); +const i1 = list.findIndex(node => node.value === 'b'); +const i2 = list.findIndex(node => node.previous && node.previous.value === 'b'); +const i3 = list.findIndex(node => node.value === 'x'); + +/* +i0 === 0 +i1 === 1 +i2 === 2 +i3 === -1 +*/ +``` + + + +#### get(position: number): ListNode\ | undefined + +Finds and returns the node with specific position in the list: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +const found = list.get(1); + +/* +found.value === "b" +found.previous.value === "a" +found.next.value === "c" +*/ +``` + + + +#### indexOf(value: T, compareFn = compare): number + +Finds the position of the first node from the list that has the given value: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "b" <-> "c" + +const i0 = list.indexOf('a'); +const i1 = list.indexOf('b'); +const i2 = list.indexOf('c'); +const i3 = list.indexOf('x'); + +/* +i0 === 0 +i1 === 1 +i2 === 3 +i3 === -1 +*/ +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addTail({ x: 1 }); +list.addTail({ x: 0 }); +list.addTail({ x: 2 }); +list.addTail({ x: 0 }); +list.addTail({ x: 3 }); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +const i0 = indexOf({ x: 1 }, (v1, v2) => v1.x === v2.x); +const i1 = indexOf({ x: 2 }, (v1, v2) => v1.x === v2.x); +const i2 = indexOf({ x: 3 }, (v1, v2) => v1.x === v2.x); +const i3 = indexOf({ x: 0 }, (v1, v2) => v1.x === v2.x); +const i4 = indexOf({ x: 4 }, (v1, v2) => v1.x === v2.x); + +/* +i0 === 0 +i1 === 2 +i2 === 4 +i3 === 1 +i4 === -1 +*/ +``` + + + +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +### How to Check All Nodes + +There are a few ways to iterate over or display a linked list. + + + +#### forEach(callback: ListIteratorFunction\): void + +Runs a callback function on all nodes in a linked list from head to tail: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.forEach((node, index) => console.log(node.value + index)); + +// 'a0' +// 'b1' +// 'c2' +``` + + + +#### \*\[Symbol.iterator\]\(\) + +A linked list is iterable. In other words, you may use methods like `for...of` on it. + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +for(const node of list) { + console.log(node.value); +} + +// 'a' +// 'b' +// 'c' +``` + + + +#### toArray(): T[] + +Converts a linked list to an array: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +const arr = list.toArray(); + +/* +arr === ['a', 'b', 'c'] +*/ +``` + + + +#### toString(): string + +Converts a linked list to a string representation of nodes and their relations: + +```js +list.addTail('a'); +list.addTail(2); +list.addTail('c'); +list.addTail({ k: 4, v: 'd' }); + +// "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} + +const str = list.toString(); + +/* +str === '"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}' +*/ +``` + + + From 6b07a3a8375da22ceb03c889a65c415762ee5882 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 26 Mar 2020 11:52:43 +0300 Subject: [PATCH 10/64] refactor(core): add fallback api to getApiUrl selector --- npm/ng-packs/packages/core/src/lib/states/config.state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 8ce206dd76..91bcc5d914 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -81,7 +81,7 @@ export class ConfigState { static getApiUrl(key?: string) { const selector = createSelector([ConfigState], (state: Config.State): string => { - return state.environment.apis[key || 'default'].url; + return (state.environment.apis[key || 'default'] || state.environment.apis.default).url; }); return selector; From 191d2baa52be49af60b5515618225cd49a9bbfc3 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 26 Mar 2020 11:53:39 +0300 Subject: [PATCH 11/64] feat: add apiName variables to some services --- .../src/lib/services/account.service.ts | 9 +++-- .../application-configuration.service.ts | 12 +++++-- .../core/src/lib/services/profile.service.ts | 18 +++++++--- .../services/feature-management.service.ts | 8 +++-- .../src/lib/services/identity.service.ts | 34 ++++++++++++------- .../services/permission-management.service.ts | 15 ++++++-- .../lib/services/tenant-management.service.ts | 28 ++++++++++----- 7 files changed, 91 insertions(+), 33 deletions(-) diff --git a/npm/ng-packs/packages/account/src/lib/services/account.service.ts b/npm/ng-packs/packages/account/src/lib/services/account.service.ts index 6c2571a518..ae9fc19d21 100644 --- a/npm/ng-packs/packages/account/src/lib/services/account.service.ts +++ b/npm/ng-packs/packages/account/src/lib/services/account.service.ts @@ -7,6 +7,8 @@ import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models'; providedIn: 'root', }) export class AccountService { + apiName = 'AbpAccount'; + constructor(private rest: RestService) {} findTenant(tenantName: string): Observable { @@ -15,7 +17,7 @@ export class AccountService { url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } register(body: RegisterRequest): Observable { @@ -25,6 +27,9 @@ export class AccountService { body, }; - return this.rest.request(request, { skipHandleError: true }); + return this.rest.request(request, { + skipHandleError: true, + apiName: this.apiName, + }); } } diff --git a/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts b/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts index 5081f94068..39b893501c 100644 --- a/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/application-configuration.service.ts @@ -3,12 +3,18 @@ import { Observable } from 'rxjs'; import { Rest } from '../models/rest'; import { ApplicationConfiguration } from '../models/application-configuration'; import { RestService } from './rest.service'; +import { Store } from '@ngxs/store'; +import { ConfigState } from '../states/config.state'; @Injectable({ providedIn: 'root', }) export class ApplicationConfigurationService { - constructor(private rest: RestService) {} + get apiName(): string { + return this.store.selectSnapshot(ConfigState.getDeep('environment.application.name')); + } + + constructor(private rest: RestService, private store: Store) {} getConfiguration(): Observable { const request: Rest.Request = { @@ -16,6 +22,8 @@ export class ApplicationConfigurationService { url: '/api/abp/application-configuration', }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } } diff --git a/npm/ng-packs/packages/core/src/lib/services/profile.service.ts b/npm/ng-packs/packages/core/src/lib/services/profile.service.ts index 38d5fb3a7b..d80caf7e80 100644 --- a/npm/ng-packs/packages/core/src/lib/services/profile.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/profile.service.ts @@ -7,6 +7,8 @@ import { Profile, Rest } from '../models'; providedIn: 'root', }) export class ProfileService { + apiName = 'AbpIdentity'; + constructor(private rest: RestService) {} get(): Observable { @@ -15,7 +17,7 @@ export class ProfileService { url: '/api/identity/my-profile', }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } update(body: Profile.Response): Observable { @@ -25,16 +27,24 @@ export class ProfileService { body, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } - changePassword(body: Profile.ChangePasswordRequest, skipHandleError: boolean = false): Observable { + changePassword( + body: Profile.ChangePasswordRequest, + skipHandleError: boolean = false, + ): Observable { const request: Rest.Request = { method: 'POST', url: '/api/identity/my-profile/change-password', body, }; - return this.rest.request(request, { skipHandleError }); + return this.rest.request(request, { + skipHandleError, + apiName: this.apiName, + }); } } diff --git a/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts index 5f6c0eed6e..1020e60d4e 100644 --- a/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts +++ b/npm/ng-packs/packages/feature-management/src/lib/services/feature-management.service.ts @@ -8,6 +8,8 @@ import { FeatureManagement } from '../models'; providedIn: 'root', }) export class FeatureManagementService { + apiName = 'FeatureManagement'; + constructor(private rest: RestService, private store: Store) {} getFeatures(params: FeatureManagement.Provider): Observable { @@ -16,7 +18,9 @@ export class FeatureManagementService { url: '/api/abp/features', params, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } updateFeatures({ @@ -30,6 +34,6 @@ export class FeatureManagementService { body: { features }, params: { providerKey, providerName }, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } } diff --git a/npm/ng-packs/packages/identity/src/lib/services/identity.service.ts b/npm/ng-packs/packages/identity/src/lib/services/identity.service.ts index ae9ca3a921..51e88557fe 100644 --- a/npm/ng-packs/packages/identity/src/lib/services/identity.service.ts +++ b/npm/ng-packs/packages/identity/src/lib/services/identity.service.ts @@ -7,6 +7,8 @@ import { Identity } from '../models/identity'; providedIn: 'root', }) export class IdentityService { + apiName = 'AbpIdentity'; + constructor(private rest: RestService) {} getRoles(params = {} as ABP.PageQueryParams): Observable { @@ -16,7 +18,7 @@ export class IdentityService { params, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } getAllRoles(): Observable { @@ -25,7 +27,7 @@ export class IdentityService { url: '/api/identity/roles/all', }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } getRoleById(id: string): Observable { @@ -34,7 +36,7 @@ export class IdentityService { url: `/api/identity/roles/${id}`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } deleteRole(id: string): Observable { @@ -43,7 +45,7 @@ export class IdentityService { url: `/api/identity/roles/${id}`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } createRole(body: Identity.RoleSaveRequest): Observable { @@ -53,7 +55,9 @@ export class IdentityService { body, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } updateRole(body: Identity.RoleItem): Observable { @@ -66,7 +70,9 @@ export class IdentityService { body, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } getUsers(params = {} as ABP.PageQueryParams): Observable { @@ -76,7 +82,7 @@ export class IdentityService { params, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } getUserById(id: string): Observable { @@ -85,7 +91,7 @@ export class IdentityService { url: `/api/identity/users/${id}`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } getUserRoles(id: string): Observable { @@ -94,7 +100,7 @@ export class IdentityService { url: `/api/identity/users/${id}/roles`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } deleteUser(id: string): Observable { @@ -103,7 +109,7 @@ export class IdentityService { url: `/api/identity/users/${id}`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } createUser(body: Identity.UserSaveRequest): Observable { @@ -113,7 +119,9 @@ export class IdentityService { body, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } updateUser(body: Identity.UserItem): Observable { @@ -126,6 +134,8 @@ export class IdentityService { body, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } } diff --git a/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts b/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts index e7fdd98ce3..fa6433e952 100644 --- a/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts +++ b/npm/ng-packs/packages/permission-management/src/lib/services/permission-management.service.ts @@ -7,16 +7,23 @@ import { PermissionManagement } from '../models/permission-management'; providedIn: 'root', }) export class PermissionManagementService { + apiName = 'AbpPermissionManagement'; + constructor(private rest: RestService) {} - getPermissions(params: PermissionManagement.GrantedProvider): Observable { + getPermissions( + params: PermissionManagement.GrantedProvider, + ): Observable { const request: Rest.Request = { method: 'GET', url: '/api/abp/permissions', params, }; - return this.rest.request(request); + return this.rest.request( + request, + { apiName: this.apiName }, + ); } updatePermissions({ @@ -31,6 +38,8 @@ export class PermissionManagementService { params: { providerKey, providerName }, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } } diff --git a/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts b/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts index 27ee26b7ed..8678da1332 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts @@ -7,6 +7,8 @@ import { TenantManagement } from '../models/tenant-management'; providedIn: 'root', }) export class TenantManagementService { + apiName = 'AbpTenantManagement'; + constructor(private rest: RestService) {} getTenant(params = {} as ABP.PageQueryParams): Observable { @@ -16,7 +18,7 @@ export class TenantManagementService { params, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } getTenantById(id: string): Observable { @@ -25,7 +27,7 @@ export class TenantManagementService { url: `/api/multi-tenancy/tenants/${id}`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } deleteTenant(id: string): Observable { @@ -34,7 +36,7 @@ export class TenantManagementService { url: `/api/multi-tenancy/tenants/${id}`, }; - return this.rest.request(request); + return this.rest.request(request, { apiName: this.apiName }); } createTenant(body: TenantManagement.AddRequest): Observable { @@ -44,7 +46,9 @@ export class TenantManagementService { body, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } updateTenant(body: TenantManagement.UpdateRequest): Observable { @@ -57,7 +61,9 @@ export class TenantManagementService { body, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } getDefaultConnectionString(id: string): Observable { @@ -68,7 +74,9 @@ export class TenantManagementService { responseType: Rest.ResponseType.Text, url, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } updateDefaultConnectionString( @@ -81,7 +89,9 @@ export class TenantManagementService { url, params: { defaultConnectionString: payload.defaultConnectionString }, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } deleteDefaultConnectionString(id: string): Observable { @@ -91,6 +101,8 @@ export class TenantManagementService { method: 'DELETE', url, }; - return this.rest.request(request); + return this.rest.request(request, { + apiName: this.apiName, + }); } } From 498e96703fb62bfc8e90abc0b137793aa9b82d19 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Thu, 26 Mar 2020 15:06:55 +0300 Subject: [PATCH 12/64] docs: add Linked List to docs-nav.json --- docs/en/UI/Angular/Track-By-Service.md | 3 +++ docs/en/docs-nav.json | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs/en/UI/Angular/Track-By-Service.md b/docs/en/UI/Angular/Track-By-Service.md index 4506f77e47..71a98fda9d 100644 --- a/docs/en/UI/Angular/Track-By-Service.md +++ b/docs/en/UI/Angular/Track-By-Service.md @@ -112,3 +112,6 @@ class DemoComponent { } ``` +## What's Next? + +* [Linked List (Doubly)](./Linked-List.md) \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 425b8c526a..3845c979c5 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -339,6 +339,10 @@ { "text": "TrackByService", "path": "UI/Angular/Track-By-Service.md" + }, + { + "text": "Linked List (Doubly)", + "path": "UI/Angular/Linked-List.md" } ] } From 943b4767469798973a5b646bbccd2beaa2deeff5 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 26 Mar 2020 23:18:57 +0800 Subject: [PATCH 13/64] Added Entity Extensions section to documentss --- .../Entity-Framework-Core-Integration.md | 7 +- ...-Application-Modules-Extending-Entities.md | 30 ++++- docs/zh-Hans/Entities.md | 9 +- .../Entity-Framework-Core-Migrations.md | 99 ++++++---------- docs/zh-Hans/Entity-Framework-Core.md | 107 +++++++++++++++++- 5 files changed, 178 insertions(+), 74 deletions(-) diff --git a/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md b/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md index 4d98c6cbc5..c74945ec01 100644 --- a/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md +++ b/docs/zh-Hans/Best-Practices/Entity-Framework-Core-Integration.md @@ -89,20 +89,23 @@ public static class IdentityDbContextModelBuilderExtensions builder.Entity(b => { - b.ToTable(options.TablePrefix + "Users", options.Schema); + b.ToTable(options.TablePrefix + "Users", options.Schema); + b.ConfigureByConvention(); //code omitted for brevity }); builder.Entity(b => { b.ToTable(options.TablePrefix + "UserClaims", options.Schema); + b.ConfigureByConvention(); //code omitted for brevity - }); + }); //code omitted for brevity } } ```` +* **推荐** 为每个Enttiy映射调用 `b.ConfigureByConvention();`(如上所示). * **推荐** 通过继承 `ModelBuilderConfigurationOptions` 来创建 **configuration Options** 类. 例如: ````C# diff --git a/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md b/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md index 745861e2c3..1ff03ae104 100644 --- a/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md +++ b/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md @@ -25,7 +25,35 @@ return user.GetProperty("Title"); 参阅[实体文档](Entities.md)了解更多关于额外系统. -> 可以基于额外的属性执行**业务逻辑**. 你可以**override**服务方法获取或设置值. 重写服务在下面进行讨论. +> 可以基于额外的属性执行**业务逻辑**. 你可以[重写服务方法](Customizing-Application-Modules-Overriding-Services.md). 然后获取或设置如上所示的值. + +## 实体扩展 (EF Core) + +如上所述,实体所有的额外属性都作为单个JSON对象存储在数据库表中. 它不适用复杂的场景,特别是在你需要的时候. + +* 使用额外属性创建**索引**和**外键**. +* 使用额外属性编写**SQL**或**LINQ**(例如根据属性值搜索). +* 创建你**自己的实体**映射到相同的表,但在实体中定义一个额外属性做为 **常规属性**(参阅 [EF Core迁移文档](Entity-Framework-Core-Migrations.md)了解更多). + +为了解决上面的问题,用于EF Core的ABP框架实体扩展系统允许你使用上面定义相同的额外属性API,但将所需的属性存储在单独的数据库表字段中. + +假设你想要添加 `SocialSecurityNumber` 到[身份模块](Modules/Identity.md)的 `IdentityUser` 实体. 你可以使用 `EntityExtensionManager` 静态类: + +````csharp +EntityExtensionManager.AddProperty( + "SocialSecurityNumber", + b => { b.HasMaxLength(32); } +); +```` + +* 你提供了 `IdentityUser` 作为实体名(泛型参数), `string` 做为新属性的类型, `SocialSecurityNumber` 做为属性名(也是数据库表的字段名). +* 你还需要提供一个使用[EF Core Fluent API](https://docs.microsoft.com/en-us/ef/core/modeling/entity-properties)定义数据库映射属性的操作. + +> 必须在使用相关的 `DbContext` 之前执行此代码. 应用程序启动模板定义了一个名为 `YourProjectNameEntityExtensions` 的静态类. 你可以在此类中定义扩展确保在正确的时间执行它. 否则你需要自己处理. + +定义实体扩展后你需要使用EF Core的[Add-Migration](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell#add-migration)和[Update-Database](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell#update-database)命令来创建code first迁移类并更新数据库. + +然后你可以使用上一部分中定义的相同额外属性系统来操纵实体上的属性. ## 创建新实体映射到同一个数据库表/Collection diff --git a/docs/zh-Hans/Entities.md b/docs/zh-Hans/Entities.md index e5cd3cf839..32dc4ceb53 100644 --- a/docs/zh-Hans/Entities.md +++ b/docs/zh-Hans/Entities.md @@ -365,14 +365,17 @@ public static class IdentityUserExtensions 存储字典的方式取决于你使用的数据库提供程序. -* 对于 [Entity Framework Core](Entity-Framework-Core.md), 它以 `JSON` 字符串形式存储在 `ExtraProperties` 字段中. 序列化到 `JSON` 和反序列化到 `JSON` 由ABP使用EF Core的[值转换](https://docs.microsoft.com/zh-cn/ef/core/modeling/value-conversions)系统自动完成. +* 对于 [Entity Framework Core](Entity-Framework-Core.md),这是两种类型的配置; + * 默认它以 `JSON` 字符串形式存储在 `ExtraProperties` 字段中. 序列化到 `JSON` 和反序列化到 `JSON` 由ABP使用EF Core的[值转换](https://docs.microsoft.com/zh-cn/ef/core/modeling/value-conversions)系统自动完成. + * 如果需要,你可以使用 `EntityExtensionManager` 为所需的额外属性定义一个单独的数据库字段. 那些使用 `EntityExtensionManager` 配置的属性继续使用单个 `JSON` 字段. 当你使用预构建的[应用模块](Modules/Index.md)并且想要[扩展模块的实体](Customizing-Application-Modules-Extending-Entities.md). 参阅[EF Core迁移文档](Entity-Framework-Core.md)了解如何使用 `EntityExtensionManager`. * 对于 [MongoDB](MongoDB.md), 它以 **常规字段** 存储, 因为 MongoDB 天生支持这种 [额外](https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/#supporting-extra-elements) 系统. ### 讨论额外的属性 -如果你使用**可重复使用的模块**,其中定义了一个实体,你想使用简单的方式get/set此实体相关的一些数据,那么额外的属性系统是非常有用的. 通常 **不需要** 为自己的实体使用这个系统,是因为它有以下缺点: +如果你使用**可重复使用的模块**,其中定义了一个实体,你想使用简单的方式get/set此实体相关的一些数据,那么额外的属性系统是非常有用的. +你通常 **不需要** 为自己的实体使用这个系统,是因为它有以下缺点: -* 它不是**完全类型安全的**. +* 它不是**完全类型安全的**,因为它使用字符串用作属性名称. * 这些属性**不容易[自动映射](Object-To-Object-Mapping.md)到其他对象**. * 它**不会**为EF Core在数据库表中**创建字段**,因此在数据库中针对这个字段创建索引或搜索/排序并不容易. diff --git a/docs/zh-Hans/Entity-Framework-Core-Migrations.md b/docs/zh-Hans/Entity-Framework-Core-Migrations.md index bcb0ac864e..223e0a56f8 100644 --- a/docs/zh-Hans/Entity-Framework-Core-Migrations.md +++ b/docs/zh-Hans/Entity-Framework-Core-Migrations.md @@ -95,7 +95,7 @@ Volo.Abp.IdentityServer.AbpIdentityServerDbProperties.DbTablePrefix = "Ids"; 这个项目有应用程序的 `DbContext`类(本例中的 `BookStoreDbContex` ). -**每个模块都使用自己的 `DbContext` 类**来访问数据库。同样你的应用程序有它自己的 `DbContext`. 通常在应用程序中使用这个 `DbContet`(如果你遵循最佳实践,应该在自定义[仓储](Repositories.md)中使用). 它几乎是一个空的 `DbContext`,因为你的应用程序在一开始没有任何实体,除了预定义的 `AppUser` 实体: +**每个模块都使用自己的 `DbContext` 类**来访问数据库。同样你的应用程序有它自己的 `DbContext`. 通常在应用程序中使用这个 `DbContet`(如果你遵循最佳实践,应该在[仓储](Repositories.md)中使用). 它几乎是一个空的 `DbContext`,因为你的应用程序在一开始没有任何实体,除了预定义的 `AppUser` 实体: ````csharp [ConnectionStringName("Default")] @@ -119,15 +119,15 @@ public class BookStoreDbContext : AbpDbContext builder.Entity(b => { - //Sharing the same table "AbpUsers" with the IdentityUser - b.ToTable("AbpUsers"); + //Sharing the same Users table with the IdentityUser + b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); - //Configure base properties b.ConfigureByConvention(); b.ConfigureAbpUser(); - //Moved customization of the "AbpUsers" table to an extension method - b.ConfigureCustomUserProperties(); + /* Configure mappings for your additional properties + * Also see the MyProjectNameEntityExtensions class + */ }); /* Configure your own tables/entities inside the ConfigureBookStore method */ @@ -190,12 +190,6 @@ public class BookStoreMigrationsDbContext : AbpDbContext(b => - { - b.ConfigureCustomUserProperties(); - }); - /* Configure your own tables/entities inside the ConfigureBookStore method */ builder.ConfigureBookStore(); } @@ -276,7 +270,7 @@ public class BackgroundJobsDbContext 您可能想在应用程序中**重用依赖模块的表**. 在这种情况下你有两个选择: -1. 你可以**直接使用模块定义的实体**. +1. 你可以**直接使用模块定义的实体**(你仍然可以在某种程度上[扩展实体](Customizing-Application-Modules-Extending-Entities.md)). 2. 你可以**创建一个新的实体**映射到同一个数据库表。 ###### 使用由模块定义的实体 @@ -379,10 +373,8 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity(b => { b.ToTable("AbpRoles"); - b.ConfigureByConvention(); - - b.ConfigureCustomRoleProperties(); + b.Property(x => x.Title).HasMaxLength(128); }); ... @@ -399,68 +391,42 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity(b => { b.ToTable("AbpRoles"); - b.ConfigureByConvention(); - - b.ConfigureCustomRoleProperties(); + b.Property(x => x.Title).HasMaxLength(128); }); ```` * 它映射到 `AbpRoles` 表,与 `IdentityRole` 实体共享. * `ConfigureByConvention()` 配置了标准/基本属性(像`TenantId`),建议总是调用它. -`ConfigureCustomRoleProperties()` 还不存在. 在 `BookStoreDbContextModelCreatingExtensions` 类中定义它 (在 `.EntityFrameworkCore` 项目的 `DbContext` 附近): +你已经为你的 `DbContext` 配置自定义属性,该属性在应用程序运行时使用. +与其直接更改 `MigrationsDbContext`,我们应该使用ABP框架的实体扩展系统,找到 在解决方案的 `.EntityFrameworkCore` 项目中找到 `YourProjectNameEntityExtensions` 类(本示例中是 `BookStoreEntityExtensions`)并且进行以下更改: ````csharp -public static void ConfigureCustomRoleProperties(this EntityTypeBuilder b) - where TRole : class, IEntity +public static class MyProjectNameEntityExtensions { - b.Property(nameof(AppRole.Title)).HasMaxLength(128); -} -```` - -* 这个方法只定义实体的**自定义属性**. -* 遗憾的是,我们不能在这里充分的利用**类型安全**(通过引用`AppRole`实体). 我们能做的最好就是使用 `Title` 名称做为类型安全。 - -你已经为运行应用程序使用的 `DbContext` 配置了自定义属性. 我们还需要配置 `MigrationsDbContext`. - -打开`MigrationsDbContext`(本例是 `BookStoreMigrationsDbContext`)进行以下更改: - -````csharp -protected override void OnModelCreating(ModelBuilder builder) -{ - base.OnModelCreating(builder); - - /* Include modules to your migration db context */ - - ... - - /* Configure customizations for entities from the modules included */ + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); - //CONFIGURE THE CUSTOM ROLE PROPERTIES - builder.Entity(b => + public static void Configure() { - b.ConfigureCustomRoleProperties(); - }); - - ... - - /* Configure your own tables/entities inside the ConfigureBookStore method */ - - builder.ConfigureBookStore(); + OneTimeRunner.Run(() => + { + EntityExtensionManager.AddProperty( + "Title", + b => { b.HasMaxLength(128); } + ); + }); + } } ```` -只增加下面几行: +> 我们建议使用 `nameof(AppRole.Title)` 而不是硬编码 "Title" 字符串 -````csharp -builder.Entity(b => -{ - b.ConfigureCustomRoleProperties(); -}); -```` +`EntityExtensionManager` 用于添加属性到现有的实体. 由于 `EntityExtensionManager` 是静态的,因此应调用一次. `OneTimeRunner` 是ABP框架定义简单的工具类. -通过这种方式,我们重用了用于为角色配置自定义属性映射的扩展方法. 但是对 `IdentityRole` 实体进行了相同的自定义. +参阅[EF Core集成文档](Entity-Framework-Core.md)了解更多关于实体扩展系统. + +我们在两个类中都重复了类似的数据库映射代码,例如 `HasMaxLength(128)`. 现在你可以在包管理控制台(记得选择 `.EntityFrameworkCore.DbMigrations` 做为PMC的默认项目并将 `.Web` 项目设置为启动项目)使用标准的 `Add-Migration` 命令添加一个新的EF Core数据库迁移. @@ -540,7 +506,7 @@ public class AppRoleAppService : ApplicationService, IAppRoleAppService ###### 使用ExtraProperties -所有从 `AggregateRoot` 派生的实体都可以在 `ExtraProperties` 属性中存储键值对, 它是 `Dictionary` 类型在数据库中被序列化为JSON. 所以你可以在字典中添加值用于查询,无需更改实体. +所有从 `AggregateRoot` 派生的实体都可以在 `ExtraProperties` 属性(因为它们都实现了 `IHasExtraProperties` 接口)中存储键值对, 它是 `Dictionary` 类型在数据库中被序列化为JSON. 所以你可以在字典中添加值用于查询,无需更改实体. 例如你可以将查询属性 `Title` 存储在 `IdentityRole` 中,而不是创建一个新的实体. 例: @@ -558,16 +524,13 @@ public class IdentityRoleExtendingService : ITransientDependency public async Task GetTitleAsync(Guid id) { var role = await _identityRoleRepository.GetAsync(id); - return role.GetProperty("Title"); } public async Task SetTitleAsync(Guid id, string newTitle) { var role = await _identityRoleRepository.GetAsync(id); - role.SetProperty("Title", newTitle); - await _identityRoleRepository.UpdateAsync(role); } } @@ -580,6 +543,12 @@ public class IdentityRoleExtendingService : ITransientDependency * 所有的额外属性都存储在数据库中的一个**JSON对象**,它们不是作为表的字段存储,与简单的表字段相比创建索引和针对此属性使用SQL查询将更加困难. * 属性名称是字符串,他们**不是类型安全的**. 建议这些类型的属性定义常量,以防止拼写错误. +###### 使用实体扩展系统 + +实体扩展系统解决了额外属性主要的问题: 它可以将额外属性做为**标准表字段**存储到数据库. + +你需要做的就是如上所诉使用 `EntityExtensionManager` 定义额外属性, 然后你就可以使得 `GetProperty` 和 `SetProperty` 方法对实体的属性进行get/set,但是这时它存储在数据库表的单独字段中. + ###### 创建新表 你可以创建**自己的表**来存储属性,而不是创建新实体并映射到同一表. 你通常复制原始实体的一些值. 例如可以将 `Name` 字段添加到你自己的表中,它是原表中 `Name` 字段的副本. diff --git a/docs/zh-Hans/Entity-Framework-Core.md b/docs/zh-Hans/Entity-Framework-Core.md index 0bec4d1ab9..23e3f30cb9 100644 --- a/docs/zh-Hans/Entity-Framework-Core.md +++ b/docs/zh-Hans/Entity-Framework-Core.md @@ -58,6 +58,53 @@ namespace MyCompany.MyProject } ```` +### 关于EF Core Fluent Mapping + +[应用程序启动模板](Startup-Templates/Application.md)已配置使用[EF Core fluent configuration API](https://docs.microsoft.com/en-us/ef/core/modeling/)映射你的实体到数据库表. + +你依然为你的实体属性使用**data annotation attributes**(像`[Required]`),而ABP文档通常遵循**fluent mapping API** approach方法. 如何使用取决与你. + +ABP框架有一些**实体基类**和**约定**(参阅[实体文档](Entities.md))提供了一些有用的扩展方法来配置从基本实体类继承的属性. + +#### ConfigureByConvention 方法 + +`ConfigureByConvention()` 是主要的扩展方法,它对你的实体**配置所有的基本属性**和约定. 所以在你的流利映射代码中为你所有的实体调用这个方法是 **最佳实践**, + +**示例**: 假设你有一个直接继承 `AggregateRoot` 基类的 `Book` 实体: + +````csharp +public class Book : AuditedAggregateRoot +{ + public string Name { get; set; } +} +```` + +你可以在你的 `DbContext` 重写 `OnModelCreating` 方法并且做以下配置: + +````csharp +protected override void OnModelCreating(ModelBuilder builder) +{ + //Always call the base method + base.OnModelCreating(builder); + + builder.Entity(b => + { + b.ToTable("Books"); + + //Configure the base properties + b.ConfigureByConvention(); + + //Configure other properties (if you are using the fluent API) + b.Property(x => x.Name).IsRequired().HasMaxLength(128); + }); +} +```` + +* 这里调用了 `b.ConfigureByConvention()` 它对于**配置基本属性**非常重要. +* 你可以在这里配置 `Name` 属性或者使用**data annotation attributes**(参阅[EF Core 文档](https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-properties)). + +> 尽管有许多扩展方法可以配置基本属性,但如果需要 `ConfigureByConvention()` 内部会调用它们. 因此仅调用它就足够了. + ### 配置连接字符串选择 如果你的应用程序有多个数据库,你可以使用 `connectionStringName]` Attribute为你的DbContext配置连接字符串名称. @@ -225,7 +272,7 @@ public override async Task DeleteAsync( } ```` -### 访问 EF Core API +## 访问 EF Core API 大多数情况下应该隐藏仓储后面的EF Core API(这也是仓储的设计目地). 但是如果想要通过仓储访问DbContext实现,则可以使用`GetDbContext()`或`GetDbSet()`扩展方法. 例: @@ -251,9 +298,59 @@ public class BookService > 要点: 你必须在使用`DbContext`的项目里引用`Volo.Abp.EntityFrameworkCore`包. 这会破坏封装,但在这种情况下,这就是你需要的. -### 高级主题 +## Extra Properties & Entity Extension Manager + +额外属性系统允许你为实现了 `IHasExtraProperties` 的实体set/get动态属性. 当你想将自定义属性添加到[应用程序模块](Modules/Index.md)中定义的实体时,它特别有用. + +默认,实体的所有额外属性存储在数据库的一个 `JSON` 对象中. 实体扩展系统允许你存储额外属性在数据库的单独字段中. -#### 设置默认仓储类 +有关额外属性和实体扩展系统的更多信息,请参阅下列文档: + +* [自定义应用模块: 扩展实体](Customizing-Application-Modules-Extending-Entities.md) +* [实体](Entities.md) + +本节只解释了 `EntityExtensionManager` 及其用法. + +### AddProperty 方法 + +`EntityExtensionManager` 的 `AddProperty` 方法允许你实体定义附加的属性. + +**示例**: 浅咖 `Title` 属性 (数据库字段)到 `IdentityRole` 实体: + +````csharp +EntityExtensionManager.AddProperty( + "Title", + b => { b.HasMaxLength(128); } +); +```` + +如果相关模块已实现此功能(通过使用下面说明的 `ConfigureExtensions`)则将新属性添加到模型中. 然后你需要运行标准的 `Add-Migration` 和 `Update-Database` 命令更新数据库以添加新字段. + +>`AddProperty` 方法必须在使用相关的 `DbContext` 之前调用,它是一个静态方法. 最好的方法是尽早的应用程序中使用它. 应用程序启动模板含有 `YourProjectNameEntityExtensions` 类,可以在放心的在此类中使用此方法. + +### ConfigureExtensions + +如果你正在开发一个可重用使用的模块,并允许应用程序开发人员将属性添加到你的实体,你可以在实体映射使用 `ConfigureExtensions` 扩展方法: + +````csharp +builder.Entity(b => +{ + b.ConfigureExtensions(); + //... +}); +```` + +如果你调用 `ConfigureByConvention()` 扩展方法(在此示例中 `b.ConfigureByConvention`),ABP框架内部会调用 `ConfigureExtensions` 方法. 使用 `ConfigureByConvention` 方法是**最佳实践**,因为它还按照约定配置基本属性的数据库映射. + +参阅上面提到的 "*ConfigureByConvention 方法*" 了解更多信息. + +### GetPropertyNames + +`EntityExtensionManager.GetPropertyNames` 静态方法可以用作为此实体定义的扩展属性的名称. 应用程序代码通常不需要,但是ABP框架在内部使用它. + +## 高级主题 + +### 设置默认仓储类 默认的通用仓储的默认实现是`EfCoreRepository`类,你可以创建自己的实现,并将其做为默认实现 @@ -343,3 +440,7 @@ context.Services.AddAbpDbContext(options => ```` 在这个例子中,`OtherDbContext`实现了`IBookStoreDbContext`. 此功能允许你在开发时使用多个DbContext(每个模块一个),但在运行时可以使用单个DbContext(实现所有DbContext的所有接口). + +## 另请参阅 + +* [实体](Entities.md) \ No newline at end of file From d0956a0a241e01520f01115b464620877ae834cc Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Thu, 26 Mar 2020 18:25:16 +0300 Subject: [PATCH 14/64] Update en.json --- .../AbpIoLocalization/Admin/Localization/Resources/en.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index cb79af1665..2a27cce735 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -86,6 +86,11 @@ "AreYouSureYouWantToDeleteAllComputers": "Are you sure you want to delete all computers?", "DeleteAll": "Delete all", "DoYouWantToCreateNewUser": "Do you want to create new user?", - "MasterModules": "Master Modules" + "MasterModules": "Master Modules", + "OrganizationName": "Organization name", + "OrganizationNamePlaceholder": "Organization name...", + "UsernameOrEmail": "Username or email", + "UsernameOrEmailPlaceholder": "Username or email...", + "Member": "Member" } } \ No newline at end of file From 4d93244f06b3f54c3c700b7439ce1ddadf3b4dc9 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Thu, 26 Mar 2020 19:06:27 +0300 Subject: [PATCH 15/64] feat(core): add methods to LinkedList for adding and dropping multiple nodes --- .../core/src/lib/tests/linked-list.spec.ts | 870 ++++++++++++++++-- .../core/src/lib/utils/linked-list.ts | 257 ++++-- 2 files changed, 997 insertions(+), 130 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts index 518956deba..3ad9eeda63 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts @@ -1,4 +1,4 @@ -import { LinkedList } from '../utils/linked-list'; +import { LinkedList, ListNode } from '../utils/linked-list'; describe('Linked List (Doubly)', () => { let list: LinkedList; @@ -44,9 +44,11 @@ describe('Linked List (Doubly)', () => { expect(list.length).toBe(3); expect(list.head.value).toBe('c'); expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('a'); expect(list.head.previous).toBeUndefined(); expect(list.tail.value).toBe('a'); expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('c'); expect(list.tail.next).toBeUndefined(); }); }); @@ -72,34 +74,49 @@ describe('Linked List (Doubly)', () => { expect(list.length).toBe(3); expect(list.head.value).toBe('a'); expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); expect(list.head.previous).toBeUndefined(); expect(list.tail.value).toBe('c'); expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); expect(list.tail.next).toBeUndefined(); }); }); describe('#after', () => { - it('should place a node after node with given value', () => { + it('should add a node after node with given value', () => { list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - // "a" <-> "b" <-> "c" + // "a" - list.add('x').after('b'); + list.add('b').after('a'); + list.add('c').after('b'); - // "a" <-> "b" <-> "x" <-> "c" + // "a" <-> "b" <-> "c" - expect(list.length).toBe(4); + expect(list.length).toBe(3); expect(list.head.value).toBe('a'); expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('x'); - expect(list.head.next.next.next.value).toBe('c'); + expect(list.head.next.next.value).toBe('c'); expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('x'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('a'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should add a node to tail if given value is not found', () => { + list.add('a').tail(); + + // "a" + + list.add('b').after('x'); + + // "a" <-> "b" + + expect(list.length).toBe(2); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.tail.value).toBe('b'); + expect(list.tail.previous.value).toBe('a'); }); it('should be able to receive a custom compareFn', () => { @@ -126,79 +143,516 @@ describe('Linked List (Doubly)', () => { }); describe('#before', () => { - it('should place a node before node with given value', () => { + it('should add a node before node with given value', () => { + list.add('c').tail(); + + // "c" + + list.add('b').before('c'); + list.add('a').before('b'); + + // "a" <-> "b" <-> "c" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should add a node to head if given value is not found', () => { + list.add('b').tail(); + + // "a" + + list.add('a').before('x'); + + // "a" <-> "b" + + expect(list.length).toBe(2); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.tail.value).toBe('b'); + expect(list.tail.previous.value).toBe('a'); + }); + + it('should be able to receive a custom compareFn', () => { + list.add({ x: 1 }).tail(); + list.add({ x: 2 }).tail(); + list.add({ x: 3 }).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + list.add({ x: 0 }).before({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); + + // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} + + expect(list.length).toBe(4); + expect(list.head.value.x).toBe(1); + expect(list.head.next.value.x).toBe(0); + expect(list.head.next.next.value.x).toBe(2); + expect(list.head.next.next.next.value.x).toBe(3); + expect(list.tail.value.x).toBe(3); + expect(list.tail.previous.value.x).toBe(2); + expect(list.tail.previous.previous.value.x).toBe(0); + expect(list.tail.previous.previous.previous.value.x).toBe(1); + }); + }); + + describe('#byIndex', () => { + it('should add a node at given index', () => { list.add('a').tail(); list.add('b').tail(); list.add('c').tail(); // "a" <-> "b" <-> "c" - list.add('x').before('b'); + list.add('x').byIndex(1); // "a" <-> "x" <-> "b" <-> "c" - expect(list.length).toBe(4); + list.add('y').byIndex(3); + + // "a" <-> "x" <-> "b" <-> "y" <-> "c" + + expect(list.length).toBe(5); expect(list.head.value).toBe('a'); expect(list.head.next.value).toBe('x'); expect(list.head.next.next.value).toBe('b'); + expect(list.head.next.next.next.value).toBe('y'); + expect(list.head.next.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('y'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.previous.previous.value).toBe('a'); + }); + + it('should add a node to head if given index is zero', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.add('x').byIndex(0); + + // "x" <-> "a" <-> "b" <-> "c" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('x'); + expect(list.head.next.value).toBe('a'); + expect(list.head.next.next.value).toBe('b'); expect(list.head.next.next.next.value).toBe('c'); expect(list.tail.value).toBe('c'); expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('x'); - expect(list.tail.previous.previous.previous.value).toBe('a'); + expect(list.tail.previous.previous.value).toBe('a'); + expect(list.tail.previous.previous.previous.value).toBe('x'); + }); + + it('should add a node to tail if given index more than size', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.add('x').byIndex(4); + + // "a" <-> "b" <-> "c" <-> "x" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.head.next.next.next.value).toBe('x'); + expect(list.tail.value).toBe('x'); + expect(list.tail.previous.value).toBe('c'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('a'); + }); + + it('should be able to add a node at given index counting from right to left', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.add('x').byIndex(-1); + + // "a" <-> "b" <-> "x" <-> "c" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('x'); + expect(list.head.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('x'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('a'); + }); + + it('should add a node to head if given index is less than minus size', () => { + list.add('a').tail(); + list.add('b').tail(); + list.add('c').tail(); + + // "a" <-> "b" <-> "c" + + list.add('x').byIndex(-4); + + // "x" <-> "a" <-> "b" <-> "c" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('x'); + expect(list.head.next.value).toBe('a'); + expect(list.head.next.next.value).toBe('b'); + expect(list.head.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + expect(list.tail.previous.previous.previous.value).toBe('x'); + }); + }); + }); + + describe('#addMany', () => { + describe('#head', () => { + it('should add multiple nodes to the head of the list', () => { + list.add('x').head(); + + // "x" + + list.addMany(['a', 'b', 'c']).head(); + + // "a" <-> "b" <-> "c" <-> "x" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.head.next.next.next.value).toBe('x'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('x'); + expect(list.tail.previous.value).toBe('c'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('a'); + expect(list.tail.next).toBeUndefined(); + }); + }); + + describe('#tail', () => { + it('should add multiple nodes to the tail of the list', () => { + list.add('x').tail(); + + // "x" + + list.addMany(['a', 'b', 'c']).tail(); + + // "x" <-> "a" <-> "b" <-> "c" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('x'); + expect(list.head.next.value).toBe('a'); + expect(list.head.next.next.value).toBe('b'); + expect(list.head.next.next.next.value).toBe('c'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + expect(list.tail.previous.previous.previous.value).toBe('x'); + expect(list.tail.next).toBeUndefined(); + }); + }); + + describe('#after', () => { + it('should add multiple nodes after node with given value', () => { + list.add('a').tail(); + + // "a" + + list.addMany(['b', 'c']).after('a'); + + // "a" <-> "b" <-> "c" + + list.addMany(['x', 'y', 'z']).after('b'); + + // "a" <-> "b" <-> "x" <-> "y" <-> "z" <-> "c" + + expect(list.length).toBe(6); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('x'); + expect(list.head.next.next.next.value).toBe('y'); + expect(list.head.next.next.next.next.value).toBe('z'); + expect(list.head.next.next.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('z'); + expect(list.tail.previous.previous.value).toBe('y'); + expect(list.tail.previous.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.previous.previous.value).toBe('a'); + }); + + it('should add multiple nodes to tail if given value is not found', () => { + list.add('a').tail(); + + // "a" + + list.addMany(['b', 'c']).after('x'); + + // "a" <-> "b" <-> "c" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should be able to receive a custom compareFn', () => { + list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + list.addMany([{ x: 4 }, { x: 5 }]).after({ x: 1 }, (v1: X, v2: X) => v1.x === v2.x); + + // {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} + + expect(list.length).toBe(5); + expect(list.head.value.x).toBe(1); + expect(list.head.next.value.x).toBe(4); + expect(list.head.next.next.value.x).toBe(5); + expect(list.head.next.next.next.value.x).toBe(2); + expect(list.head.next.next.next.next.value.x).toBe(3); + expect(list.tail.value.x).toBe(3); + expect(list.tail.previous.value.x).toBe(2); + expect(list.tail.previous.previous.value.x).toBe(5); + expect(list.tail.previous.previous.previous.value.x).toBe(4); + expect(list.tail.previous.previous.previous.previous.value.x).toBe(1); + }); + + it('should not change the list when empty array given as value', () => { + list.add('a').tail(); + + // "a" + + list.addMany([]).after('a'); + + // "a" + + expect(list.length).toBe(1); + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + }); + }); + + describe('#before', () => { + it('should add multiple nodes before node with given value', () => { + list.add('c').tail(); + + // "c" + + list.addMany(['a', 'b']).before('c'); + + // "a" <-> "b" <-> "c" + + list.addMany(['x', 'y', 'z']).before('b'); + + // "a" <-> "x" <-> "y" <-> "z" <-> "b" <-> "c" + + expect(list.length).toBe(6); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('x'); + expect(list.head.next.next.value).toBe('y'); + expect(list.head.next.next.next.value).toBe('z'); + expect(list.head.next.next.next.next.value).toBe('b'); + expect(list.head.next.next.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('z'); + expect(list.tail.previous.previous.previous.value).toBe('y'); + expect(list.tail.previous.previous.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.previous.previous.previous.value).toBe('a'); + }); + + it('should add multiple nodes to head if given value is not found', () => { + list.add('c').tail(); + + // "c" + + list.addMany(['a', 'b']).before('x'); + + // "a" <-> "b" <-> "c" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should be able to receive a custom compareFn', () => { + list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + list.addMany([{ x: 4 }, { x: 5 }]).before({ x: 3 }, (v1: X, v2: X) => v1.x === v2.x); + + // {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} + + expect(list.length).toBe(5); + expect(list.head.value.x).toBe(1); + expect(list.head.next.value.x).toBe(2); + expect(list.head.next.next.value.x).toBe(4); + expect(list.head.next.next.next.value.x).toBe(5); + expect(list.head.next.next.next.next.value.x).toBe(3); + expect(list.tail.value.x).toBe(3); + expect(list.tail.previous.value.x).toBe(5); + expect(list.tail.previous.previous.value.x).toBe(4); + expect(list.tail.previous.previous.previous.value.x).toBe(2); + expect(list.tail.previous.previous.previous.previous.value.x).toBe(1); + }); + + it('should not change the list when empty array given as value', () => { + list.add('a').tail(); + + // "a" + + list.addMany([]).before('a'); + + // "a" + + expect(list.length).toBe(1); + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + }); + }); + + describe('#byIndex', () => { + it('should add multiple nodes starting from given index', () => { + list.addMany(['a', 'b', 'c']).tail(); + + // "a" <-> "b" <-> "c" + + list.addMany(['x', 'y']).byIndex(1); + + // "a" <-> "x" <-> "y" <-> "b" <-> "c" + + list.addMany(['z']).byIndex(4); + + // "a" <-> "x" <-> "y" <-> "b" <-> "z" <-> "c" + + expect(list.length).toBe(6); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('x'); + expect(list.head.next.next.value).toBe('y'); + expect(list.head.next.next.next.value).toBe('b'); + expect(list.head.next.next.next.next.value).toBe('z'); + expect(list.head.next.next.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('z'); + expect(list.tail.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.value).toBe('y'); + expect(list.tail.previous.previous.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.previous.previous.previous.value).toBe('a'); + }); + + it('should add multiple nodes to head if given index is zero', () => { + list.addMany(['a', 'b', 'c']).tail(); + + // "a" <-> "b" <-> "c" + + list.addMany(['x', 'y']).byIndex(0); + + // "x" <-> "y" <-> "a" <-> "b" <-> "c" + + expect(list.length).toBe(5); + expect(list.head.value).toBe('x'); + expect(list.head.next.value).toBe('y'); + expect(list.head.next.next.value).toBe('a'); + expect(list.head.next.next.next.value).toBe('b'); + expect(list.head.next.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + expect(list.tail.previous.previous.previous.value).toBe('y'); + expect(list.tail.previous.previous.previous.previous.value).toBe('x'); }); - it('should be able to receive a custom compareFn', () => { - list.add({ x: 1 }).tail(); - list.add({ x: 2 }).tail(); - list.add({ x: 3 }).tail(); + it('should add multiple nodes to tail if given index more than size', () => { + list.addMany(['a', 'b', 'c']).tail(); - // {"x":1} <-> {"x":2} <-> {"x":3} + // "a" <-> "b" <-> "c" - list.add({ x: 0 }).before({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); + list.addMany(['x', 'y']).byIndex(4); - // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} + // "a" <-> "b" <-> "c" <-> "x" <-> "y" - expect(list.length).toBe(4); - expect(list.head.value.x).toBe(1); - expect(list.head.next.value.x).toBe(0); - expect(list.head.next.next.value.x).toBe(2); - expect(list.head.next.next.next.value.x).toBe(3); - expect(list.tail.value.x).toBe(3); - expect(list.tail.previous.value.x).toBe(2); - expect(list.tail.previous.previous.value.x).toBe(0); - expect(list.tail.previous.previous.previous.value.x).toBe(1); + expect(list.length).toBe(5); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.head.next.next.next.value).toBe('x'); + expect(list.head.next.next.next.next.value).toBe('y'); + expect(list.tail.value).toBe('y'); + expect(list.tail.previous.value).toBe('x'); + expect(list.tail.previous.previous.value).toBe('c'); + expect(list.tail.previous.previous.previous.value).toBe('b'); + expect(list.tail.previous.previous.previous.previous.value).toBe('a'); }); - }); - describe('#byIndex', () => { - it('should place a node at given index', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); + it('should be able to add multiple nodes at given index counting from right to left', () => { + list.addMany(['a', 'b', 'c']).tail(); // "a" <-> "b" <-> "c" - list.add('x').byIndex(1); - - // "a" <-> "x" <-> "b" <-> "c" - - list.add('y').byIndex(3); + list.addMany(['x', 'y']).byIndex(-1); - // "a" <-> "x" <-> "b" <-> "y" <-> "c" + // "a" <-> "b" <-> "x" <-> "y" <-> "c" expect(list.length).toBe(5); expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('x'); - expect(list.head.next.next.value).toBe('b'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('x'); expect(list.head.next.next.next.value).toBe('y'); expect(list.head.next.next.next.next.value).toBe('c'); expect(list.tail.value).toBe('c'); expect(list.tail.previous.value).toBe('y'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.value).toBe('x'); + expect(list.tail.previous.previous.previous.value).toBe('b'); expect(list.tail.previous.previous.previous.previous.value).toBe('a'); }); + + it('should add a node to head if given index is less than minus size', () => { + list.addMany(['a', 'b', 'c']).tail(); + + // "a" <-> "b" <-> "c" + + list.addMany(['x', 'y']).byIndex(-4); + + // "x" <-> "y" <-> "a" <-> "b" <-> "c" + + expect(list.length).toBe(5); + expect(list.head.value).toBe('x'); + expect(list.head.next.value).toBe('y'); + expect(list.head.next.next.value).toBe('a'); + expect(list.head.next.next.next.value).toBe('b'); + expect(list.head.next.next.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + expect(list.tail.previous.previous.previous.value).toBe('y'); + expect(list.tail.previous.previous.previous.previous.value).toBe('x'); + }); }); }); @@ -212,19 +666,19 @@ describe('Linked List (Doubly)', () => { // "a" <-> "x" <-> "b" <-> "x" <-> "c" - const node1 = list.find(node => node.previous && node.previous.value === 'a'); + const found1 = list.find(node => node.previous && node.previous.value === 'a'); - expect(node1.value).toBe('x'); - expect(node1.previous.value).toBe('a'); - expect(node1.next.value).toBe('b'); + expect(found1.value).toBe('x'); + expect(found1.previous.value).toBe('a'); + expect(found1.next.value).toBe('b'); // "a" <-> "x" <-> "b" <-> "x" <-> "c" - const node2 = list.find(node => node.next && node.next.value === 'c'); + const found2 = list.find(node => node.next && node.next.value === 'c'); - expect(node2.value).toBe('x'); - expect(node2.previous.value).toBe('b'); - expect(node2.next.value).toBe('c'); + expect(found2.value).toBe('x'); + expect(found2.previous.value).toBe('b'); + expect(found2.next.value).toBe('c'); }); it('should return undefined when list is empty', () => { @@ -329,7 +783,8 @@ describe('Linked List (Doubly)', () => { describe('#drop', () => { describe('#head', () => { it('should return undefined when there is no head', () => { - expect(list.drop().head()).toBeUndefined(); + const dropped = list.drop().head(); + expect(dropped).toBeUndefined(); }); it('should remove the node from the head of the list', () => { @@ -350,12 +805,22 @@ describe('Linked List (Doubly)', () => { expect(list.tail.value).toBe('c'); expect(list.tail.previous.value).toBe('b'); expect(list.tail.next).toBeUndefined(); + + // "b" <-> "c" + + list.drop().head(); + list.drop().head(); + + expect(list.length).toBe(0); + expect(list.head).toBeUndefined(); + expect(list.tail).toBeUndefined(); }); }); - describe('#head', () => { + describe('#tail', () => { it('should return undefined when there is no tail', () => { - expect(list.drop().tail()).toBeUndefined(); + const dropped = list.drop().tail(); + expect(dropped).toBeUndefined(); }); it('should remove the node from the tail of the list', () => { @@ -376,6 +841,15 @@ describe('Linked List (Doubly)', () => { expect(list.tail.value).toBe('b'); expect(list.tail.previous.value).toBe('a'); expect(list.tail.next).toBeUndefined(); + + // "a" <-> "b" + + list.drop().tail(); + list.drop().tail(); + + expect(list.length).toBe(0); + expect(list.head).toBeUndefined(); + expect(list.tail).toBeUndefined(); }); }); @@ -393,7 +867,7 @@ describe('Linked List (Doubly)', () => { // "a" <-> "c" <-> "d" <-> "e" - list.drop().byIndex(2); + list.drop().byIndex(-2); // "a" <-> "c" <-> "e" @@ -404,11 +878,21 @@ describe('Linked List (Doubly)', () => { expect(list.tail.value).toBe('e'); expect(list.tail.previous.value).toBe('c'); expect(list.tail.previous.previous.value).toBe('a'); + + // "a" <-> "c" <-> "e" + + list.drop().byIndex(2); + list.drop().byIndex(1); + list.drop().byIndex(0); + + expect(list.length).toBe(0); + expect(list.head).toBeUndefined(); + expect(list.tail).toBeUndefined(); }); it('should return undefined when list is empty', () => { - const node = list.drop().byIndex(0); - expect(node).toBeUndefined(); + const dropped = list.drop().byIndex(0); + expect(dropped).toBeUndefined(); }); it('should return undefined when given index does not exist', () => { @@ -418,11 +902,11 @@ describe('Linked List (Doubly)', () => { // "a" <-> "b" <-> "c" - const node1 = list.drop().byIndex(4); + const dropped1 = list.drop().byIndex(4); // "a" <-> "b" <-> "c" - expect(node1).toBeUndefined(); + expect(dropped1).toBeUndefined(); expect(list.length).toBe(3); expect(list.head.value).toBe('a'); expect(list.head.next.value).toBe('b'); @@ -433,11 +917,11 @@ describe('Linked List (Doubly)', () => { // "a" <-> "b" <-> "c" - const node2 = list.drop().byIndex(-1); + const dropped2 = list.drop().byIndex(-4); // "a" <-> "b" <-> "c" - expect(node2).toBeUndefined(); + expect(dropped2).toBeUndefined(); expect(list.length).toBe(3); expect(list.head.value).toBe('a'); expect(list.head.next.value).toBe('b'); @@ -506,8 +990,8 @@ describe('Linked List (Doubly)', () => { }); it('should return undefined when list is empty', () => { - const node = list.drop().byValue('x'); - expect(node).toBeUndefined(); + const dropped = list.drop().byValue('x'); + expect(dropped).toBeUndefined(); }); it('should return undefined when given value is not found', () => { @@ -622,6 +1106,214 @@ describe('Linked List (Doubly)', () => { }); }); + describe('#dropMany', () => { + describe('#head', () => { + it('should return empty array when there is no head', () => { + const dropped = list.dropMany(1).head(); + expect(dropped).toEqual([]); + }); + + it('should remove multiple nodes from the head of the list', () => { + list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); + + // "a" <-> "b" <-> "c" <-> "d" <-> "e" + + list.dropMany(3).head(); + + // "d" <-> "e" + + expect(list.length).toBe(2); + expect(list.head.value).toBe('d'); + expect(list.head.next.value).toBe('e'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('e'); + expect(list.tail.previous.value).toBe('d'); + expect(list.tail.next).toBeUndefined(); + }); + + it('should not change the list when count is less than or equal to zero', () => { + list.add('a').tail(); + + // "a" + + list.dropMany(0).head(); + + // "a" + + list.dropMany(-1).head(); + + // "a" + + expect(list.length).toBe(1); + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + }); + }); + + describe('#tail', () => { + it('should return empty array when there is no tail', () => { + const dropped = list.dropMany(1).tail(); + expect(dropped).toEqual([]); + }); + + it('should remove multiple nodes from the tail of the list', () => { + list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); + + // "a" <-> "b" <-> "c" <-> "d" <-> "e" + + list.dropMany(3).tail(); + + // "a" <-> "b" + + expect(list.length).toBe(2); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.previous).toBeUndefined(); + expect(list.tail.value).toBe('b'); + expect(list.tail.previous.value).toBe('a'); + expect(list.tail.next).toBeUndefined(); + }); + + it('should not change the list when count is less than or equal to zero', () => { + list.add('a').tail(); + + // "a" + + list.dropMany(0).tail(); + + // "a" + + list.dropMany(-1).tail(); + + // "a" + + expect(list.length).toBe(1); + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + }); + }); + + describe('#byIndex', () => { + it('should remove multiple nodes starting from given index', () => { + list.addMany(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']).tail(); + + // "a" <-> "b" <-> "c" <-> "d" <-> "e" <-> "f" <-> "g" <-> "h" + + list.dropMany(2).byIndex(1); + + // "a" <-> "d" <-> "e" <-> "f" <-> "g" <-> "h" + + list.dropMany(2).byIndex(-3); + + // "a" <-> "d" <-> "e" <-> "h" + + expect(list.length).toBe(4); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('d'); + expect(list.head.next.next.value).toBe('e'); + expect(list.head.next.next.next.value).toBe('h'); + expect(list.tail.value).toBe('h'); + expect(list.tail.previous.value).toBe('e'); + expect(list.tail.previous.previous.value).toBe('d'); + expect(list.tail.previous.previous.previous.value).toBe('a'); + + list.dropMany(4).byIndex(0); + + expect(list.length).toBe(0); + expect(list.head).toBeUndefined(); + expect(list.tail).toBeUndefined(); + }); + + it('should return empty array when list is empty', () => { + const dropped = list.dropMany(1).byIndex(0); + expect(dropped).toEqual([]); + }); + + it('should return empty array when given index does not exist', () => { + list.addMany(['a', 'b', 'c']).tail(); + + // "a" <-> "b" <-> "c" + + const dropped = list.dropMany(3).byIndex(4); + + // "a" <-> "b" <-> "c" + + expect(dropped).toEqual([]); + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + }); + + it('should remove from start when given index is less than minus size', () => { + list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); + + // "a" <-> "b" <-> "c" <-> "d" <-> "e" + + list.dropMany(2).byIndex(-9); + + // "c" <-> "d" <-> "e" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('c'); + expect(list.head.next.value).toBe('d'); + expect(list.head.next.next.value).toBe('e'); + expect(list.tail.value).toBe('e'); + expect(list.tail.previous.value).toBe('d'); + expect(list.tail.previous.previous.value).toBe('c'); + }); + + it('should remove from end when given index + count is larger than or equal to size', () => { + list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); + + // "a" <-> "b" <-> "c" <-> "d" <-> "e" + + list.dropMany(2).byIndex(3); + + // "a" <-> "b" <-> "c" + + expect(list.length).toBe(3); + expect(list.head.value).toBe('a'); + expect(list.head.next.value).toBe('b'); + expect(list.head.next.next.value).toBe('c'); + expect(list.tail.value).toBe('c'); + expect(list.tail.previous.value).toBe('b'); + expect(list.tail.previous.previous.value).toBe('a'); + + // "a" <-> "b" <-> "c" + + list.dropMany(9).byIndex(1); + + // "a" + + expect(list.length).toBe(1); + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + }); + + it('should not change the list when count is less than or equal to zero', () => { + list.add('a').tail(); + + // "a" + + list.dropMany(0).byIndex(0); + + // "a" + + list.dropMany(-1).byIndex(0); + + // "a" + + expect(list.length).toBe(1); + expect(list.head.value).toBe('a'); + expect(list.tail.value).toBe('a'); + }); + }); + }); + describe('#get', () => { it('should return node at given index', () => { list.add('a').tail(); @@ -761,6 +1453,27 @@ describe('Linked List (Doubly)', () => { }); }); + describe('#toNodeArray', () => { + it('should return array of nodes', () => { + list.addTail('a'); + list.addTail(2); + list.addTail('c'); + list.addTail({ k: 4, v: 'd' }); + + // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} + + const arr = list.toNodeArray(); + + expect(arr.every(node => node instanceof ListNode)).toBe(true); + expect(arr.map(node => node.value)).toEqual(['a', 2, 'c', { k: 4, v: 'd' }]); + }); + + it('should return empty array when list is empty', () => { + const arr = list.toNodeArray(); + expect(arr).toEqual([]); + }); + }); + describe('#toString', () => { it('should return string representation', () => { list.addTail('a'); @@ -778,6 +1491,23 @@ describe('Linked List (Doubly)', () => { const str = list.toString(); expect(str).toBe(''); }); + + it('should be coercible', () => { + list.addMany(['a', 'b', 'c']).tail(); + + // "a" <-> "b" <-> "c" + + expect('' + list).toBe('"a" <-> "b" <-> "c"'); + }); + + it('should be able to receive a custom mapperFn', () => { + list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); + + // {"x":1} <-> {"x":2} <-> {"x":3} + + const str = list.toString(value => value.x); + expect(str).toBe('1 <-> 2 <-> 3'); + }); }); it('should be iterable', () => { diff --git a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts index 42cc5bc941..9bd3e21941 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts @@ -27,65 +27,107 @@ export class LinkedList { return this.size; } - private linkWith( + private attach( value: T, previousNode: ListNode | undefined, nextNode: ListNode | undefined, ): ListNode { - const node = new ListNode(value); - if (!previousNode) return this.addHead(value); + if (!nextNode) return this.addTail(value); + const node = new ListNode(value); node.previous = previousNode; previousNode.next = node; node.next = nextNode; nextNode.previous = node; - this.size += 1; + this.size++; + + return node; + } + + private attachMany( + values: T[], + previousNode: ListNode | undefined, + nextNode: ListNode | undefined, + ): ListNode[] { + if (!values.length) return []; + + if (!previousNode) return this.addManyHead(values); + + if (!nextNode) return this.addManyTail(values); + + const list = new LinkedList(); + list.addManyTail(values); + list.first!.previous = previousNode; + previousNode.next = list.first; + list.last!.next = nextNode; + nextNode.previous = list.last; + + this.size += values.length; + + return list.toNodeArray(); + } + + private detach(node: ListNode) { + if (!node.previous) return this.dropHead(); + + if (!node.next) return this.dropTail(); + + node.previous.next = node.next; + node.next.previous = node.previous; + + this.size--; return node; } add(value: T) { return { - after: (previousValue: T, compareFn = compare) => { - return this.addAfter(value, previousValue, compareFn); - }, - before: (nextValue: T, compareFn = compare) => { - return this.addBefore(value, nextValue, compareFn); - }, - byIndex: (position: number): ListNode => { - return this.addByIndex(value, position); - }, - head: (): ListNode => { - return this.addHead(value); - }, - tail: (): ListNode => { - return this.addTail(value); - }, + after: (previousValue: T, compareFn: ListComparisonFn = compare) => + this.addAfter(value, previousValue, compareFn), + before: (nextValue: T, compareFn: ListComparisonFn = compare) => + this.addBefore(value, nextValue, compareFn), + byIndex: (position: number) => this.addByIndex(value, position), + head: () => this.addHead(value), + tail: () => this.addTail(value), + }; + } + + addMany(values: T[]) { + return { + after: (previousValue: T, compareFn: ListComparisonFn = compare) => + this.addManyAfter(values, previousValue, compareFn), + before: (nextValue: T, compareFn: ListComparisonFn = compare) => + this.addManyBefore(values, nextValue, compareFn), + byIndex: (position: number) => this.addManyByIndex(values, position), + head: () => this.addManyHead(values), + tail: () => this.addManyTail(values), }; } - addAfter(value: T, previousValue: T, compareFn = compare): ListNode { + addAfter(value: T, previousValue: T, compareFn: ListComparisonFn = compare): ListNode { const previous = this.find(node => compareFn(node.value, previousValue)); - return previous ? this.linkWith(value, previous, previous.next) : this.addTail(value); + return previous ? this.attach(value, previous, previous.next) : this.addTail(value); } - addBefore(value: T, nextValue: T, compareFn = compare): ListNode { + addBefore(value: T, nextValue: T, compareFn: ListComparisonFn = compare): ListNode { const next = this.find(node => compareFn(node.value, nextValue)); - return next ? this.linkWith(value, next.previous, next) : this.addHead(value); + return next ? this.attach(value, next.previous, next) : this.addHead(value); } addByIndex(value: T, position: number): ListNode { + if (position < 0) position += this.size; + else if (position >= this.size) return this.addTail(value); + if (position <= 0) return this.addHead(value); - if (position >= this.size) return this.addTail(value); const next = this.get(position)!; - return this.linkWith(value, next.previous, next); + return this.attach(value, next.previous, next); } addHead(value: T): ListNode { @@ -97,7 +139,7 @@ export class LinkedList { else this.last = node; this.first = node; - this.size += 1; + this.size++; return node; } @@ -114,51 +156,92 @@ export class LinkedList { this.last = node; } - this.size += 1; + this.size++; return node; } + addManyAfter( + values: T[], + previousValue: T, + compareFn: ListComparisonFn = compare, + ): ListNode[] { + const previous = this.find(node => compareFn(node.value, previousValue)); + + return previous ? this.attachMany(values, previous, previous.next) : this.addManyTail(values); + } + + addManyBefore( + values: T[], + nextValue: T, + compareFn: ListComparisonFn = compare, + ): ListNode[] { + const next = this.find(node => compareFn(node.value, nextValue)); + + return next ? this.attachMany(values, next.previous, next) : this.addManyHead(values); + } + + addManyByIndex(values: T[], position: number): ListNode[] { + if (position < 0) position += this.size; + + if (position <= 0) return this.addManyHead(values); + + if (position >= this.size) return this.addManyTail(values); + + const next = this.get(position)!; + + return this.attachMany(values, next.previous, next); + } + + addManyHead(values: T[]): ListNode[] { + return values.reduceRight[]>((nodes, value) => { + nodes.unshift(this.addHead(value)); + return nodes; + }, []); + } + + addManyTail(values: T[]): ListNode[] { + return values.map(value => this.addTail(value)); + } + drop() { return { byIndex: (position: number) => this.dropByIndex(position), - byValue: (value: T, compareFn = compare) => this.dropByValue(value, compareFn), - byValueAll: (value: T, compareFn = compare) => this.dropByValueAll(value, compareFn), + byValue: (value: T, compareFn: ListComparisonFn = compare) => + this.dropByValue(value, compareFn), + byValueAll: (value: T, compareFn: ListComparisonFn = compare) => + this.dropByValueAll(value, compareFn), head: () => this.dropHead(), tail: () => this.dropTail(), }; } + dropMany(count: number) { + return { + byIndex: (position: number) => this.dropManyByIndex(count, position), + head: () => this.dropManyHead(count), + tail: () => this.dropManyTail(count), + }; + } + dropByIndex(position: number): ListNode | undefined { - if (position === 0) return this.dropHead(); - else if (position === this.size - 1) return this.dropTail(); + if (position < 0) position += this.size; const current = this.get(position); - if (current) { - current.previous!.next = current.next; - current.next!.previous = current.previous; - - this.size -= 1; - - return current; - } - - return undefined; + return current ? this.detach(current) : undefined; } - dropByValue(value: T, compareFn = compare): ListNode | undefined { + dropByValue(value: T, compareFn: ListComparisonFn = compare): ListNode | undefined { const position = this.findIndex(node => compareFn(node.value, value)); - if (position < 0) return undefined; - - return this.dropByIndex(position); + return position < 0 ? undefined : this.dropByIndex(position); } - dropByValueAll(value: T, compareFn = compare): ListNode[] { + dropByValueAll(value: T, compareFn: ListComparisonFn = compare): ListNode[] { const dropped: ListNode[] = []; - for (let current = this.first, position = 0; current; position += 1, current = current.next) { + for (let current = this.first, position = 0; current; position++, current = current.next) { if (compareFn(current.value, value)) { dropped.push(this.dropByIndex(position - dropped.length)!); } @@ -176,7 +259,7 @@ export class LinkedList { if (this.first) this.first.previous = undefined; else this.last = undefined; - this.size -= 1; + this.size--; return head; } @@ -193,7 +276,7 @@ export class LinkedList { if (this.last) this.last.next = undefined; else this.first = undefined; - this.size -= 1; + this.size--; return tail; } @@ -201,24 +284,66 @@ export class LinkedList { return undefined; } - find(predicate: ListIteratorFunction): ListNode | undefined { - for (let current = this.first, position = 0; current; position += 1, current = current.next) { + dropManyByIndex(count: number, position: number): ListNode[] { + if (count <= 0) return []; + + if (position < 0) position = Math.max(position + this.size, 0); + else if (position >= this.size) return []; + + count = Math.min(count, this.size - position); + + const dropped: ListNode[] = []; + + while (count--) { + const current = this.get(position); + dropped.push(this.detach(current!)!); + } + + return dropped; + } + + dropManyHead(count: Exclude): ListNode[] { + if (count <= 0) return []; + + count = Math.min(count, this.size); + + const dropped: ListNode[] = []; + + while (count--) dropped.unshift(this.dropHead()!); + + return dropped; + } + + dropManyTail(count: Exclude): ListNode[] { + if (count <= 0) return []; + + count = Math.min(count, this.size); + + const dropped: ListNode[] = []; + + while (count--) dropped.push(this.dropTail()!); + + return dropped; + } + + find(predicate: ListIteratorFn): ListNode | undefined { + for (let current = this.first, position = 0; current; position++, current = current.next) { if (predicate(current, position, this)) return current; } return undefined; } - findIndex(predicate: ListIteratorFunction): number { - for (let current = this.first, position = 0; current; position += 1, current = current.next) { + findIndex(predicate: ListIteratorFn): number { + for (let current = this.first, position = 0; current; position++, current = current.next) { if (predicate(current, position, this)) return position; } return -1; } - forEach(callback: ListIteratorFunction) { - for (let node = this.first, position = 0; node; position += 1, node = node.next) { + forEach(callback: ListIteratorFn) { + for (let node = this.first, position = 0; node; position++, node = node.next) { callback(node, position, this); } } @@ -227,7 +352,7 @@ export class LinkedList { return this.find((_, index) => position === index); } - indexOf(value: T, compareFn = compare): number { + indexOf(value: T, compareFn: ListComparisonFn = compare): number { return this.findIndex(node => compareFn(node.value, value)); } @@ -239,20 +364,32 @@ export class LinkedList { return array; } - toString(): string { + toNodeArray(): ListNode[] { + const array = new Array(this.size); + + this.forEach((node, index) => (array[index!] = node)); + + return array; + } + + toString(mapperFn: ListMapperFn = JSON.stringify): string { return this.toArray() - .map(value => JSON.stringify(value)) + .map(value => mapperFn(value)) .join(' <-> '); } *[Symbol.iterator]() { - for (let node = this.first, position = 0; node; position += 1, node = node.next) { + for (let node = this.first, position = 0; node; position++, node = node.next) { yield node.value; } } } -export type ListIteratorFunction = ( +export type ListMapperFn = (value: T) => any; + +export type ListComparisonFn = (value1: T, value2: T) => boolean; + +export type ListIteratorFn = ( node: ListNode, index?: number, list?: LinkedList, From 7a22f1a4f24bf1f098723c3c896d177e0d4e4cf9 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Thu, 26 Mar 2020 20:08:54 +0300 Subject: [PATCH 16/64] docs(core): add how to add and drop multiple values in LinkedList --- docs/en/UI/Angular/Linked-List.md | 628 +++++++++++++++++++++++++----- 1 file changed, 534 insertions(+), 94 deletions(-) diff --git a/docs/en/UI/Angular/Linked-List.md b/docs/en/UI/Angular/Linked-List.md index c3fb307806..388c5963cd 100644 --- a/docs/en/UI/Angular/Linked-List.md +++ b/docs/en/UI/Angular/Linked-List.md @@ -26,7 +26,7 @@ The constructor does not get any parameters. ### How to Add New Nodes -There are a few methods to create new nodes in a linked list and all of them are separately available as well as revealed from an `add` method. +There are several methods to create new nodes in a linked list and all of them are separately available as well as revealed by `add` and `addMany` methods. @@ -50,6 +50,22 @@ list.addHead('c'); +#### addManyHead(values: T\[\]): ListNode\\[\] + +Adds multiple nodes with given values as the first nodes in list: + +```js +list.addManyHead(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyHead(['x', 'y', 'z']); + +// "x" <-> "y" <-> "z" <-> "a" <-> "b" <-> "c" +``` + + + #### addTail(value: T): ListNode\ Adds a node with given value as the last node in list: @@ -70,6 +86,22 @@ list.addTail('c'); +#### addManyTail(values: T\[\]): ListNode\\[\] + +Adds multiple nodes with given values as the last nodes in list: + +```js +list.addManyTail(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyTail(['x', 'y', 'z']); + +// "a" <-> "b" <-> "c" <-> "x" <-> "y" <-> "z" +``` + + + #### addAfter(value: T, previousValue: T, compareFn = compare): ListNode\ Adds a node with given value after the first node that has the previous value: @@ -109,6 +141,40 @@ list.addAfter({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x); +#### addManyAfter(values: T\[\], previousValue: T, compareFn = compare): ListNode\\[\] + +Adds multiple nodes with given values after the first node that has the previous value: + +```js +list.addManyTail(['a', 'b', 'b', 'c']); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addManyAfter(['x', 'y'], 'b'); + +// "a" <-> "b" <-> "x" <-> "y" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addManyAfter([{ x: 4 }, { x: 5 }], { x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} +``` + + + +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + #### addBefore(value: T, nextValue: T, compareFn = compare): ListNode\ Adds a node with given value before the first node that has the next value: @@ -148,6 +214,40 @@ list.addBefore({ x: 0 }, { x: 2 }, (v1, v2) => v1.x === v2.x); +#### addManyBefore(values: T\[\], nextValue: T, compareFn = compare): ListNode\\[\] + +Adds multiple nodes with given values before the first node that has the next value: + +```js +list.addManyTail(['a', 'b', 'b', 'c']); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addManyBefore(['x', 'y'], 'b'); + +// "a" <-> "x" <-> "y" <-> "b" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addManyBefore([{ x: 4 }, { x: 5 }], { x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} +``` + + + +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + #### addByIndex(value: T, position: number): ListNode\ Adds a node with given value at the specified position in the list: @@ -166,6 +266,52 @@ list.addByIndex('x', 2); +It works with negative index too: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.addByIndex('x', -1); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +#### addManyByIndex(values: T\[\], position: number): ListNode\\[\] + +Adds multiple nodes with given values at the specified position in the list: + +```js +list.addManyTail(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyByIndex(['x', 'y'], 2); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + + + +It works with negative index too: + +```js +list.addManyTail(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyByIndex(['x', 'y'], -1); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + + + #### add(value: T).head(): ListNode\ Adds a node with given value as the first node in list: @@ -314,10 +460,172 @@ list.add('x').byIndex(2); +It works with negative index too: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "c" + +list.add('x').byIndex(-1); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + > This is an alternative API for `addByIndex`. +#### addMany(values: T\[\]).head(): ListNode\\[\] + +Adds multiple nodes with given values as the first nodes in list: + +```js +list.addMany(['a', 'b', 'c']).head(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y', 'z']).head(); + +// "x" <-> "y" <-> "z" <-> "a" <-> "b" <-> "c" +``` + + + +> This is an alternative API for `addManyHead`. + + + +#### addMany(values: T\[\]).tail(): ListNode\\[\] + +Adds multiple nodes with given values as the last nodes in list: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y', 'z']).tail(); + +// "a" <-> "b" <-> "c" <-> "x" <-> "y" <-> "z" +``` + + + +> This is an alternative API for `addManyTail`. + + + +#### addMany(values: T\[\]).after(previousValue: T, compareFn = compare): ListNode\\[\] + +Adds multiple nodes with given values after the first node that has the previous value: + +```js +list.addMany(['a', 'b', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addMany(['x', 'y']).after('b'); + +// "a" <-> "b" <-> "x" <-> "y" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addMany([{ x: 4 }, { x: 5 }]).after({ x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} +``` + + + +> This is an alternative API for `addManyAfter`. +> +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### addMany(values: T\[\]).before(nextValue: T, compareFn = compare): ListNode\\[\] + +Adds multiple nodes with given values before the first node that has the next value: + +```js +list.addMany(['a', 'b', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addMany(['x', 'y']).before('b'); + +// "a" <-> "x" <-> "y" <-> "b" <-> "b" <-> "c" +``` + + + +You may pass a custom compare function to detect the searched value: + +```js +list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addMany([{ x: 4 }, { x: 5 }]).before({ x: 2 }, (v1, v2) => v1.x === v2.x); + +// {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} +``` + + + +> This is an alternative API for `addManyBefore`. +> +> The default compare function checks deep equality, so you will rarely need to pass that parameter. + + + +#### addMany(values: T\[\]).byIndex(position: number): ListNode\\[\] + +Adds multiple nodes with given values at the specified position in the list: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y']).byIndex(2); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + + + +It works with negative index too: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y']).byIndex(-1); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + + + +> This is an alternative API for `addManyByIndex`. + + + ### How to Remove Nodes There are a few methods to remove nodes from a linked list and all of them are separately available as well as revealed from a `drop` method. @@ -329,9 +637,7 @@ There are a few methods to remove nodes from a linked list and all of them are s Removes the first node from the list: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('c'); +list.addMany(['a', 'b', 'c']).tail(); // "a" <-> "b" <-> "c" @@ -342,14 +648,28 @@ list.dropHead(); +#### dropManyHead(count: number): ListNode\\[\] + +Removes the first nodes from the list based on given count: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropManyHead(2); + +// "c" +``` + + + #### dropTail(): ListNode\ | undefined Removes the last node from the list: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('c'); +list.addMany(['a', 'b', 'c']).tail(); // "a" <-> "b" <-> "c" @@ -360,14 +680,28 @@ list.dropTail(); +#### dropManyTail(count: number): ListNode\\[\] + +Removes the last nodes from the list based on given count: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropManyTail(2); + +// "a" +``` + + + #### dropByIndex(position: number): ListNode\ | undefined Removes the node with the specified position from the list: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('c'); +list.addMany(['a', 'b', 'c']).tail(); // "a" <-> "b" <-> "c" @@ -378,16 +712,56 @@ list.dropByIndex(1); +It works with negative index too: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropByIndex(-2); + +// "a" <-> "c" +``` + + + +#### dropManyByIndex(count: number, position: number): ListNode\\[\] + +Removes the nodes starting from the specified position from the list based on given count: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropManyByIndex(2, 1); + +// "a" <-> "d" +``` + + + +It works with negative index too: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropManyByIndex(2, -2); + +// "a" <-> "d" +``` + + + #### dropByValue(value: T, compareFn = compare): ListNode\ | undefined Removes the first node with given value from the list: ```js -list.addTail('a'); -list.addTail('x'); -list.addTail('b'); -list.addTail('x'); -list.addTail('c'); +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); // "a" <-> "x" <-> "b" <-> "x" <-> "c" @@ -401,11 +775,7 @@ list.dropByValue('x'); You may pass a custom compare function to detect the searched value: ```js -list.addTail({ x: 1 }); -list.addTail({ x: 0 }); -list.addTail({ x: 2 }); -list.addTail({ x: 0 }); -list.addTail({ x: 3 }); +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} @@ -425,11 +795,7 @@ list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x); Removes all nodes with given value from the list: ```js -list.addTail('a'); -list.addTail('x'); -list.addTail('b'); -list.addTail('x'); -list.addTail('c'); +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); // "a" <-> "x" <-> "b" <-> "x" <-> "c" @@ -443,11 +809,7 @@ list.dropByValueAll('x'); You may pass a custom compare function to detect the searched value: ```js -list.addTail({ x: 1 }); -list.addTail({ x: 0 }); -list.addTail({ x: 2 }); -list.addTail({ x: 0 }); -list.addTail({ x: 3 }); +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} @@ -467,9 +829,7 @@ list.dropByValue({ x: 0 }, (v1, v2) => v1.x === v2.x); Removes the first node in list: ```js -list.add('a').tail(); -list.add('b').tail(); -list.add('c').tail(); +list.addMany(['a', 'b', 'c']).tail(); // "a" <-> "b" <-> "c" @@ -489,9 +849,7 @@ list.drop().head(); Removes the last node in list: ```js -list.add('a').tail(); -list.add('b').tail(); -list.add('c').tail(); +list.addMany(['a', 'b', 'c']).tail(); // "a" <-> "b" <-> "c" @@ -511,9 +869,7 @@ list.drop().tail(); Removes the node with the specified position from the list: ```js -list.add('a').tail(); -list.add('b').tail(); -list.add('c').tail(); +list.addMany(['a', 'b', 'c']).tail(); // "a" <-> "b" <-> "c" @@ -524,6 +880,20 @@ list.drop().byIndex(1); +It works with negative index too: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.drop().byIndex(-2); + +// "a" <-> "c" +``` + + + > This is an alternative API for `dropByIndex`. @@ -533,11 +903,7 @@ list.drop().byIndex(1); Removes the first node with given value from the list: ```js -list.add('a').tail(); -list.add('x').tail(); -list.add('b').tail(); -list.add('x').tail(); -list.add('c').tail(); +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); // "a" <-> "x" <-> "b" <-> "x" <-> "c" @@ -551,11 +917,7 @@ list.drop().byValue('x'); You may pass a custom compare function to detect the searched value: ```js -list.add({ x: 1 }).tail(); -list.add({ x: 0 }).tail(); -list.add({ x: 2 }).tail(); -list.add({ x: 0 }).tail(); -list.add({ x: 3 }).tail(); +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} @@ -577,11 +939,7 @@ list.drop().byValue({ x: 0 }, (v1, v2) => v1.x === v2.x); Removes all nodes with given value from the list: ```js -list.add('a').tail(); -list.add('x').tail(); -list.add('b').tail(); -list.add('x').tail(); -list.add('c').tail(); +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); // "a" <-> "x" <-> "b" <-> "x" <-> "c" @@ -595,11 +953,7 @@ list.drop().byValueAll('x'); You may pass a custom compare function to detect the searched value: ```js -list.add({ x: 1 }).tail(); -list.add({ x: 0 }).tail(); -list.add({ x: 2 }).tail(); -list.add({ x: 0 }).tail(); -list.add({ x: 3 }).tail(); +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} @@ -616,6 +970,80 @@ list.drop().byValueAll({ x: 0 }, (v1, v2) => v1.x === v2.x); +#### dropMany(count: number).head(): ListNode\\[\] + +Removes the first nodes from the list based on given count: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropMany(2).head(); + +// "c" +``` + + + +> This is an alternative API for `dropManyHead`. + + + +#### dropMany(count: number).tail(): ListNode\\[\] + +Removes the last nodes from the list based on given count: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropMany(2).tail(); + +// "a" +``` + + + +> This is an alternative API for `dropManyTail`. + + + +#### dropMany(count: number).byIndex(position: number): ListNode\\[\] + +Removes the nodes starting from the specified position from the list based on given count: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropMany(2).byIndex(1); + +// "a" <-> "d" +``` + + + +It works with negative index too: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropMany(2).byIndex(-2); + +// "a" <-> "d" +``` + + + +> This is an alternative API for `dropManyByIndex`. + + + ### How to Find Nodes There are a few methods to find specific nodes in a linked list. @@ -627,10 +1055,7 @@ There are a few methods to find specific nodes in a linked list. Finds the first node from the list that matches the given predicate: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('b'); -list.addTail('c'); +list.addTailMany(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -650,10 +1075,7 @@ found.next.value === "b" Finds the position of the first node from the list that matches the given predicate: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('b'); -list.addTail('c'); +list.addTailMany(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -677,9 +1099,7 @@ i3 === -1 Finds and returns the node with specific position in the list: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('c'); +list.addTailMany(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -699,10 +1119,7 @@ found.next.value === "c" Finds the position of the first node from the list that has the given value: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('b'); -list.addTail('c'); +list.addTailMany(['a', 'b', 'b', 'c']); // "a" <-> "b" <-> "b" <-> "c" @@ -724,11 +1141,7 @@ i3 === -1 You may pass a custom compare function to detect the searched value: ```js -list.addTail({ x: 1 }); -list.addTail({ x: 0 }); -list.addTail({ x: 2 }); -list.addTail({ x: 0 }); -list.addTail({ x: 3 }); +list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} @@ -764,9 +1177,7 @@ There are a few ways to iterate over or display a linked list. Runs a callback function on all nodes in a linked list from head to tail: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('c'); +list.addTailMany(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -784,9 +1195,7 @@ list.forEach((node, index) => console.log(node.value + index)); A linked list is iterable. In other words, you may use methods like `for...of` on it. ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('c'); +list.addTailMany(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -801,14 +1210,12 @@ for(const node of list) { -#### toArray(): T[] +#### toArray(): T\[\] -Converts a linked list to an array: +Converts a linked list to an array of values: ```js -list.addTail('a'); -list.addTail('b'); -list.addTail('c'); +list.addTailMany(['a', 'b', 'c']); // "a" <-> "b" <-> "c" @@ -821,15 +1228,32 @@ arr === ['a', 'b', 'c'] +#### toNodeArray(): T\[\] + +Converts a linked list to an array of nodes: + +```js +list.addTailMany(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +const arr = list.toNodeArray(); + +/* +arr[0].value === 'a' +arr[1].value === 'a' +arr[2].value === 'a' +*/ +``` + + + #### toString(): string Converts a linked list to a string representation of nodes and their relations: ```js -list.addTail('a'); -list.addTail(2); -list.addTail('c'); -list.addTail({ k: 4, v: 'd' }); +list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} @@ -842,3 +1266,19 @@ str === '"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}' +You may pass a custom mapper function to map values before stringifying them: + +```js +list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }]).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} <-> {"x":4} <-> {"x":5} + +const str = list.toString(value => value.x); + +/* +str === '1 <-> 2 <-> 3 <-> 4 <-> 5' +*/ +``` + + + From 73911d7854647d096a5f9a2d1b708038c96ca7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 26 Mar 2020 22:15:25 +0300 Subject: [PATCH 17/64] Added Getting-Started-With-Startup-Templates.md updated links. --- docs/en/Getting-Started-With-Startup-Templates.md | 6 ++++++ docs/en/Startup-Templates/Application.md | 15 +++++++++------ docs/en/docs-nav.json | 1 + 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 docs/en/Getting-Started-With-Startup-Templates.md diff --git a/docs/en/Getting-Started-With-Startup-Templates.md b/docs/en/Getting-Started-With-Startup-Templates.md new file mode 100644 index 0000000000..19442ec81e --- /dev/null +++ b/docs/en/Getting-Started-With-Startup-Templates.md @@ -0,0 +1,6 @@ +# Getting Started with the Startup Templates + +See the following tutorials to learn how to get started with the ABP Framework using the pre-built application startup templates: + +* [Getting Started With the ASP.NET Core MVC / Razor Pages UI](Getting-Started-AspNetCore-MVC-Template.md) +* [Getting Started with the Angular UI](Getting-Started-Angular-Template.md) \ No newline at end of file diff --git a/docs/en/Startup-Templates/Application.md b/docs/en/Startup-Templates/Application.md index 3a5c6c3bb2..8298c01f1b 100644 --- a/docs/en/Startup-Templates/Application.md +++ b/docs/en/Startup-Templates/Application.md @@ -2,11 +2,12 @@ ## Introduction -This template provides a layered application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. This document explains the solution structure and projects in details. If you want to start quickly, follow the guides below: +This template provides a layered application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. -* See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template (uses MVC as the UI framework and Entity Framework Core as the database provider). -* See the [ASP.NET Core MVC Application Development Tutorial](../Tutorials/Part-1.md?UI=MVC) to learn how to develop applications using this template (uses MVC as the UI framework and Entity Framework Core as the database provider). -* See the [Angular Application Development Tutorial](../Tutorials/Part-1.md?UI=NG) to learn how to develop applications using this template (uses Angular as the UI framework and MongoDB as the database provider). +This document explains **the solution structure** and projects in details. If you want to start quickly, follow the guides below: + +* [The getting started document](../Getting-Started-With-Startup-Templates.md) explains how to create a new application in a few minutes. +* [The application development tutorial](../Tutorials/Part-1) explains step by step application development. ## How to Start With? @@ -123,6 +124,8 @@ Notice that the migration `DbContext` is only used for database migrations and * * Depends on the `.EntityFrameworkCore` project since it re-uses the configuration defined for the `DbContext` of the application. > This project is available only if you are using EF Core as the database provider. +> +> See the [Entity Framework Core Migrations Guide](../Entity-Framework-Core-Migrations.md) to understand this project in details. #### .DbMigrator Project @@ -269,5 +272,5 @@ The files under the `angular/src/environments` folder has the essential configur ## What's Next? -- See [Getting Started With the ASP.NET Core MVC Template](../Getting-Started-AspNetCore-MVC-Template.md) to create a new solution and run it for this template. -- See the [ASP.NET Core MVC Tutorial](../Tutorials/Part-1.md) to learn how to develop applications using this template. +- [The getting started document](../Getting-Started-With-Startup-Templates.md) explains how to create a new application in a few minutes. +- [The application development tutorial](../Tutorials/Part-1) explains step by step application development. diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index c273e59346..71641bf2a8 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -5,6 +5,7 @@ "items": [ { "text": "From Startup Templates", + "path": "Getting-Started-With-Startup-Templates.md" "items": [ { "text": "Application with MVC (Razor Pages) UI", From d971f7e8c94af38aae1dbdbbc031af69a265442c Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Fri, 27 Mar 2020 08:53:43 +0800 Subject: [PATCH 18/64] Fix typo --- docs/zh-Hans/Entity-Framework-Core-Migrations.md | 2 +- docs/zh-Hans/Entity-Framework-Core.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/zh-Hans/Entity-Framework-Core-Migrations.md b/docs/zh-Hans/Entity-Framework-Core-Migrations.md index 223e0a56f8..0b6b075fb6 100644 --- a/docs/zh-Hans/Entity-Framework-Core-Migrations.md +++ b/docs/zh-Hans/Entity-Framework-Core-Migrations.md @@ -400,7 +400,7 @@ builder.Entity(b => * `ConfigureByConvention()` 配置了标准/基本属性(像`TenantId`),建议总是调用它. 你已经为你的 `DbContext` 配置自定义属性,该属性在应用程序运行时使用. -与其直接更改 `MigrationsDbContext`,我们应该使用ABP框架的实体扩展系统,找到 在解决方案的 `.EntityFrameworkCore` 项目中找到 `YourProjectNameEntityExtensions` 类(本示例中是 `BookStoreEntityExtensions`)并且进行以下更改: +与其直接更改 `MigrationsDbContext`,我们应该使用ABP框架的实体扩展系统,在解决方案的 `.EntityFrameworkCore` 项目中找到 `YourProjectNameEntityExtensions` 类(本示例中是 `BookStoreEntityExtensions`)并且进行以下更改: ````csharp public static class MyProjectNameEntityExtensions diff --git a/docs/zh-Hans/Entity-Framework-Core.md b/docs/zh-Hans/Entity-Framework-Core.md index 23e3f30cb9..8638b89b63 100644 --- a/docs/zh-Hans/Entity-Framework-Core.md +++ b/docs/zh-Hans/Entity-Framework-Core.md @@ -315,7 +315,7 @@ public class BookService `EntityExtensionManager` 的 `AddProperty` 方法允许你实体定义附加的属性. -**示例**: 浅咖 `Title` 属性 (数据库字段)到 `IdentityRole` 实体: +**示例**: 添加 `Title` 属性 (数据库字段)到 `IdentityRole` 实体: ````csharp EntityExtensionManager.AddProperty( From c4c4cfadccc27dadac817b9d0e96a0df4f6c0cc6 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 07:29:52 +0300 Subject: [PATCH 19/64] fix: fix lint error --- npm/ng-packs/packages/core/src/lib/states/config.state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index f8f4d069f6..74e9ee8cd0 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -301,7 +301,7 @@ export class ConfigState { } @Action(SetEnvironment) - setEnvironment({ patchState }: StateContext, { environment }:SetEnvironment) { + setEnvironment({ patchState }: StateContext, { environment }: SetEnvironment) { return patchState({ environment, }); From 0a17d721b96d39e8f37819afa4a343459cf1c37d Mon Sep 17 00:00:00 2001 From: Mehmet Erim <34455572+mehmet-erim@users.noreply.github.com> Date: Fri, 27 Mar 2020 07:53:39 +0300 Subject: [PATCH 20/64] Update Service-Proxies.md --- docs/en/UI/Angular/Service-Proxies.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md index b1cd9ad500..97b43b7513 100644 --- a/docs/en/UI/Angular/Service-Proxies.md +++ b/docs/en/UI/Angular/Service-Proxies.md @@ -25,9 +25,9 @@ The files generated with the `--module all` option like below: ### Services -Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./HTTP-Requests.md#restservice). +Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Http-Requests.md#restservice). -A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](HTTP-Requests#how-to-get-a-specific-api-endpoint-from-application-config) +A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests.md#how-to-get-a-specific-api-endpoint-from-application-config) The `providedIn` property of the services is defined as `'root'`. Therefore no need to add a service as a provider to a module. You can use a service by injecting it into a constructor as shown below: @@ -64,4 +64,4 @@ Initial values ​​can optionally be passed to each class constructor. ## What's Next? -* [Http Requests](./Http-Requests.md) \ No newline at end of file +* [HTTP Requests](./Http-Requests.md) From aab32ad8c5dc6c968f42f4e6ee57953f68eec5a8 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 09:10:45 +0300 Subject: [PATCH 21/64] chore: set dev-app as default project --- npm/ng-packs/angular.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index d0289505fe..af185688b2 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -526,5 +526,5 @@ } } }, - "defaultProject": "core" + "defaultProject": "dev-app" } From 83645730b9bc38eae238861c9432acb05518189f Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 09:16:45 +0300 Subject: [PATCH 22/64] refactor: deprecate the requirements obj --- .../apps/dev-app/src/app/app.module.ts | 3 -- .../packages/core/src/lib/models/common.ts | 6 ++- templates/app/angular/src/app/app.module.ts | 6 +-- .../module/angular/src/app/app.module.ts | 46 +++++++++---------- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/npm/ng-packs/apps/dev-app/src/app/app.module.ts b/npm/ng-packs/apps/dev-app/src/app/app.module.ts index f1324c233a..64f8150f01 100644 --- a/npm/ng-packs/apps/dev-app/src/app/app.module.ts +++ b/npm/ng-packs/apps/dev-app/src/app/app.module.ts @@ -21,9 +21,6 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; imports: [ CoreModule.forRoot({ environment, - requirements: { - layouts: LAYOUTS, - }, }), ThemeSharedModule.forRoot(), AccountConfigModule.forRoot({ redirectUrl: '/' }), diff --git a/npm/ng-packs/packages/core/src/lib/models/common.ts b/npm/ng-packs/packages/core/src/lib/models/common.ts index 8b4fb76cb0..79792a1b61 100644 --- a/npm/ng-packs/packages/core/src/lib/models/common.ts +++ b/npm/ng-packs/packages/core/src/lib/models/common.ts @@ -6,7 +6,11 @@ import { Subject } from 'rxjs'; export namespace ABP { export interface Root { environment: Partial; - requirements: Config.Requirements; + /** + * + * @deprecated To be deleted in v3.0 + */ + requirements?: Config.Requirements; } export type PagedResponse = { diff --git a/templates/app/angular/src/app/app.module.ts b/templates/app/angular/src/app/app.module.ts index 5df354fb38..928dff5172 100644 --- a/templates/app/angular/src/app/app.module.ts +++ b/templates/app/angular/src/app/app.module.ts @@ -3,7 +3,6 @@ import { CoreModule } from '@abp/ng.core'; import { IdentityConfigModule } from '@abp/ng.identity.config'; import { SettingManagementConfigModule } from '@abp/ng.setting-management.config'; import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config'; -import { LAYOUTS } from '@abp/ng.theme.basic'; import { ThemeSharedModule } from '@abp/ng.theme.shared'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; @@ -20,10 +19,7 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; @NgModule({ imports: [ CoreModule.forRoot({ - environment, - requirements: { - layouts: LAYOUTS - } + environment }), ThemeSharedModule.forRoot(), AccountConfigModule.forRoot({ redirectUrl: '/' }), diff --git a/templates/module/angular/src/app/app.module.ts b/templates/module/angular/src/app/app.module.ts index c0fef36c9a..2705a3318b 100644 --- a/templates/module/angular/src/app/app.module.ts +++ b/templates/module/angular/src/app/app.module.ts @@ -1,21 +1,20 @@ -import { AccountConfigModule } from '@abp/ng.account.config'; -import { CoreModule } from '@abp/ng.core'; -import { IdentityConfigModule } from '@abp/ng.identity.config'; -import { SettingManagementConfigModule } from '@abp/ng.setting-management.config'; -import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config'; -import { LAYOUTS } from '@abp/ng.theme.basic'; -import { ThemeSharedModule } from '@abp/ng.theme.shared'; -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin'; -import { NgxsModule } from '@ngxs/store'; -import { OAuthModule } from 'angular-oauth2-oidc'; -import { environment } from '../environments/environment'; -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { SharedModule } from './shared/shared.module'; -import { MyProjectNameConfigModule } from '../../projects/my-project-name-config/src/public-api'; +import { AccountConfigModule } from "@abp/ng.account.config"; +import { CoreModule } from "@abp/ng.core"; +import { IdentityConfigModule } from "@abp/ng.identity.config"; +import { SettingManagementConfigModule } from "@abp/ng.setting-management.config"; +import { TenantManagementConfigModule } from "@abp/ng.tenant-management.config"; +import { ThemeSharedModule } from "@abp/ng.theme.shared"; +import { NgModule } from "@angular/core"; +import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { NgxsLoggerPluginModule } from "@ngxs/logger-plugin"; +import { NgxsModule } from "@ngxs/store"; +import { OAuthModule } from "angular-oauth2-oidc"; +import { environment } from "../environments/environment"; +import { AppRoutingModule } from "./app-routing.module"; +import { AppComponent } from "./app.component"; +import { SharedModule } from "./shared/shared.module"; +import { MyProjectNameConfigModule } from "../../projects/my-project-name-config/src/public-api"; const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; @@ -24,14 +23,11 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; imports: [ ThemeSharedModule.forRoot(), CoreModule.forRoot({ - environment, - requirements: { - layouts: LAYOUTS, - }, + environment }), OAuthModule.forRoot(), NgxsModule.forRoot([]), - AccountConfigModule.forRoot({ redirectUrl: '/' }), + AccountConfigModule.forRoot({ redirectUrl: "/" }), IdentityConfigModule, TenantManagementConfigModule, SettingManagementConfigModule, @@ -41,8 +37,8 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; AppRoutingModule, SharedModule, - ...(environment.production ? [] : LOGGERS), + ...(environment.production ? [] : LOGGERS) ], - bootstrap: [AppComponent], + bootstrap: [AppComponent] }) export class AppModule {} From 97c3a6fce04210fdadfb526ce83c4e7ab7e1a59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0smail=20=C3=87A=C4=9EDA=C5=9E?= Date: Fri, 27 Mar 2020 10:02:49 +0300 Subject: [PATCH 23/64] added missing comma to docs-nav.json --- docs/en/docs-nav.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 71641bf2a8..d46e16bfe5 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -5,7 +5,7 @@ "items": [ { "text": "From Startup Templates", - "path": "Getting-Started-With-Startup-Templates.md" + "path": "Getting-Started-With-Startup-Templates.md", "items": [ { "text": "Application with MVC (Razor Pages) UI", From 4ad19ebf84a7af0d68dcd9968bf13e510c6b77ee Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 10:21:56 +0300 Subject: [PATCH 24/64] feat(theme-basic): add layouts to replaceable components state --- .../src/lib/services/initial.service.ts | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts b/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts index 33c7c18770..21fb1756bc 100644 --- a/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts +++ b/npm/ng-packs/packages/theme-basic/src/lib/services/initial.service.ts @@ -1,12 +1,29 @@ +import { LazyLoadService, AddReplaceableComponent } from '@abp/ng.core'; import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { LazyLoadService } from '@abp/ng.core'; +import { Store } from '@ngxs/store'; import styles from '../constants/styles'; +import { ApplicationLayoutComponent } from '../components/application-layout/application-layout.component'; +import { AccountLayoutComponent } from '../components/account-layout/account-layout.component'; +import { EmptyLayoutComponent } from '../components/empty-layout/empty-layout.component'; @Injectable({ providedIn: 'root' }) export class InitialService { - constructor(private lazyLoadService: LazyLoadService) { + constructor(private lazyLoadService: LazyLoadService, private store: Store) { this.appendStyle().subscribe(); + this.store.dispatch([ + new AddReplaceableComponent({ + key: 'Theme.ApplicationLayoutComponent', + component: ApplicationLayoutComponent, + }), + new AddReplaceableComponent({ + key: 'Theme.AccountLayoutComponent', + component: AccountLayoutComponent, + }), + new AddReplaceableComponent({ + key: 'Theme.EmptyLayoutComponent', + component: EmptyLayoutComponent, + }), + ]); } appendStyle() { From 857ee6b9ef82376c42229c613764feaee7896a95 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 10:22:45 +0300 Subject: [PATCH 25/64] feat(core): get layouts from store in dynamic-layout.component --- .../components/dynamic-layout.component.ts | 49 +++++---- .../tests/dynamic-layout.component.spec.ts | 102 +++++++++++------- 2 files changed, 93 insertions(+), 58 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 7fc1826efe..fc3926a1ea 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,13 +1,15 @@ import { Component, Input, OnDestroy, Type, Injector } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router, UrlSegment } from '@angular/router'; import { Select, Store } from '@ngxs/store'; -import { Observable } from 'rxjs'; +import { Observable, combineLatest } from 'rxjs'; import snq from 'snq'; import { eLayoutType } from '../enums/common'; import { Config } from '../models/config'; import { ABP } from '../models/common'; import { ConfigState } from '../states/config.state'; import { takeUntilDestroy } from '../utils/rxjs-utils'; +import { ReplaceableComponentsState } from '../states/replaceable-components.state'; +import { ReplaceableComponents } from '../models/replaceable-components'; @Component({ selector: 'abp-dynamic-layout', @@ -20,24 +22,24 @@ import { takeUntilDestroy } from '../utils/rxjs-utils'; `, }) export class DynamicLayoutComponent implements OnDestroy { - @Select(ConfigState.getOne('requirements')) requirements$: Observable; + @Select(ReplaceableComponentsState.getComponent('Theme.ApplicationLayoutComponent')) + applicationLayout$: Observable; + + @Select(ReplaceableComponentsState.getComponent('Theme.AccountLayoutComponent')) + accountLayout$: Observable; + + @Select(ReplaceableComponentsState.getComponent('Theme.EmptyLayoutComponent')) + emptyLayout$: Observable; layout: Type; + layouts = {} as { [key in eLayoutType]: Type }; + + expectedLayout: eLayoutType = eLayoutType.empty; + constructor(private router: Router, private route: ActivatedRoute, private store: Store) { - const { - requirements: { layouts }, - routes, - } = this.store.selectSnapshot(ConfigState.getAll); - - if ((this.route.snapshot.data || {}).layout) { - this.layout = layouts - .filter(l => !!l) - .find( - (l: any) => - snq(() => l.type.toLowerCase().indexOf(this.route.snapshot.data.layout), -1) > -1, - ); - } + this.listenToLayouts(); + const { routes } = this.store.selectSnapshot(ConfigState.getAll); router.events.pipe(takeUntilDestroy(this)).subscribe(event => { if (event instanceof NavigationEnd) { @@ -45,16 +47,25 @@ export class DynamicLayoutComponent implements OnDestroy { { path: router.url.replace('/', '') }, ] as any); - const layout = (this.route.snapshot.data || {}).layout || findLayout(segments, routes); + this.expectedLayout = + (this.route.snapshot.data || {}).layout || findLayout(segments, routes); - this.layout = layouts - .filter(l => !!l) - .find((l: any) => snq(() => l.type.toLowerCase().indexOf(layout), -1) > -1); + this.layout = this.layouts[this.expectedLayout]; } }); } ngOnDestroy() {} + + listenToLayouts() { + combineLatest(this.applicationLayout$, this.accountLayout$, this.emptyLayout$) + .pipe(takeUntilDestroy(this)) + .subscribe(([application, account, empty]) => { + this.layouts.application = application.component; + this.layouts.account = account.component; + this.layouts.empty = empty.component; + }); + } } function findLayout(segments: UrlSegment[], routes: ABP.FullRoute[]): eLayoutType { diff --git a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts index 54a602b84a..0a123b6a4f 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts @@ -1,36 +1,36 @@ import { Component, NgModule } from '@angular/core'; import { ActivatedRoute, RouterModule } from '@angular/router'; -import { createRoutingFactory, SpectatorRouting, SpyObject } from '@ngneat/spectator/jest'; -import { Store } from '@ngxs/store'; +import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest'; +import { NgxsModule, Store } from '@ngxs/store'; +import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; import { eLayoutType } from '../enums'; import { ABP } from '../models'; -import { DynamicLayoutComponent, RouterOutletComponent } from '../components'; +import { ConfigState, ReplaceableComponentsState } from '../states'; +import { ApplicationConfigurationService } from '../services'; @Component({ selector: 'abp-layout-application', template: '', }) -class DummyApplicationLayoutComponent { - static type = eLayoutType.application; -} +class DummyApplicationLayoutComponent {} @Component({ selector: 'abp-layout-account', template: '', }) -class DummyAccountLayoutComponent { - static type = eLayoutType.account; -} +class DummyAccountLayoutComponent {} @Component({ selector: 'abp-layout-empty', template: '', }) -class DummyEmptyLayoutComponent { - static type = eLayoutType.empty; -} +class DummyEmptyLayoutComponent {} -const LAYOUTS = [DummyApplicationLayoutComponent, DummyAccountLayoutComponent, DummyEmptyLayoutComponent]; +const LAYOUTS = [ + DummyApplicationLayoutComponent, + DummyAccountLayoutComponent, + DummyEmptyLayoutComponent, +]; @NgModule({ imports: [RouterModule], @@ -47,13 +47,57 @@ class DummyComponent { constructor(public route: ActivatedRoute) {} } +const storeData = { + ConfigState: { + routes: [ + { + path: '', + wrapper: true, + children: [ + { + path: 'parentWithLayout', + layout: eLayoutType.application, + children: [ + { path: 'childWithoutLayout' }, + { path: 'childWithLayout', layout: eLayoutType.account }, + ], + }, + ], + }, + { path: 'withData', layout: eLayoutType.application }, + , + ] as ABP.FullRoute[], + environment: { application: {} }, + }, + ReplaceableComponentsState: { + replaceableComponents: [ + { + key: 'Theme.ApplicationLayoutComponent', + component: DummyApplicationLayoutComponent, + }, + { + key: 'Theme.AccountLayoutComponent', + component: DummyAccountLayoutComponent, + }, + { + key: 'Theme.EmptyLayoutComponent', + component: DummyEmptyLayoutComponent, + }, + ], + }, +}; + describe('DynamicLayoutComponent', () => { const createComponent = createRoutingFactory({ component: RouterOutletComponent, stubsEnabled: false, - mocks: [Store], declarations: [DummyComponent, DynamicLayoutComponent], - imports: [RouterModule, DummyLayoutModule], + mocks: [ApplicationConfigurationService], + imports: [ + RouterModule, + DummyLayoutModule, + NgxsModule.forRoot([ConfigState, ReplaceableComponentsState]), + ], routes: [ { path: '', component: RouterOutletComponent }, { @@ -100,33 +144,13 @@ describe('DynamicLayoutComponent', () => { }); let spectator: SpectatorRouting; - let store: SpyObject; - const mockStoreData = { - requirements: { layouts: LAYOUTS }, - routes: [ - { - path: '', - wrapper: true, - children: [ - { - path: 'parentWithLayout', - layout: eLayoutType.application, - children: [{ path: 'childWithoutLayout' }, { path: 'childWithLayout', layout: eLayoutType.account }], - }, - ], - }, - { path: 'withData', layout: eLayoutType.application }, - , - ] as ABP.FullRoute[], - environment: { application: {} }, - }; - let storeSpy: jest.SpyInstance; + let store: Store; beforeEach(async () => { spectator = createComponent(); store = spectator.get(Store); - storeSpy = jest.spyOn(store, 'selectSnapshot'); - storeSpy.mockReturnValue(mockStoreData); + + store.reset(storeData); }); it('should handle application layout from parent abp route and display it', async () => { @@ -159,7 +183,7 @@ describe('DynamicLayoutComponent', () => { }); it('should not display any layout when layouts are empty', async () => { - storeSpy.mockReturnValue({ ...mockStoreData, requirements: { layouts: [] } }); + store.reset({ ...storeData, ReplaceableComponentsState: {} }); spectator.detectChanges(); From 84890bb91958cc4666a23ef559c62757cb8c9442 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Fri, 27 Mar 2020 11:58:12 +0300 Subject: [PATCH 26/64] #closes 3381 --- .../Controllers/HomeController.cs | 7 + .../docs/app/VoloDocs.Web/Pages/Index.cshtml | 5 - .../app/VoloDocs.Web/Pages/Index.cshtml.cs | 29 - .../docs/app/VoloDocs.Web/package-lock.json | 2796 +++++++++++------ modules/docs/app/VoloDocs.Web/package.json | 2 +- .../Documents/DocumentAdminAppService.cs | 12 +- .../Volo/Docs/Documents/DocumentAppService.cs | 20 +- .../Volo/Docs/DocsDomainConsts.cs | 7 + .../Documents/FileSystemDocumentSource.cs | 15 +- .../GitHub/Documents/GithubDocumentSource.cs | 15 +- .../Extensions/NewtonsoftJsonExtensions.cs | 22 + .../ScribanDocumentSectionRenderer.cs | 109 +- .../src/Volo.Docs.Web/Volo.Docs.Web.csproj | 1 + 13 files changed, 2068 insertions(+), 972 deletions(-) delete mode 100644 modules/docs/app/VoloDocs.Web/Pages/Index.cshtml delete mode 100644 modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs create mode 100644 modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainConsts.cs create mode 100644 modules/docs/src/Volo.Docs.Domain/Volo/Extensions/NewtonsoftJsonExtensions.cs diff --git a/modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs b/modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs index 5594306229..17c6550502 100644 --- a/modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs +++ b/modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs @@ -6,7 +6,14 @@ namespace VoloDocs.Web.Controllers { public void Index() { + //if (!_urlUiOptions.RoutePrefix.IsNullOrWhiteSpace()) + //{ + // return Redirect("." + _urlUiOptions.RoutePrefix); + //} + //return Page(); + + //return Redirect("/en/abp/latest"); } } } diff --git a/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml b/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml deleted file mode 100644 index 0d75012326..0000000000 --- a/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -@page -@model VoloDocs.Web.Pages.IndexModel -@{ -} - diff --git a/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs b/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs deleted file mode 100644 index b127db4b3f..0000000000 --- a/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Options; -using Volo.Docs; - -namespace VoloDocs.Web.Pages -{ - public class IndexModel : PageModel - { - private readonly DocsUiOptions _urlUiOptions; - - public IndexModel(IOptions urlOptions) - { - _urlUiOptions = urlOptions.Value; - } - - public IActionResult OnGet() - { - //TODO: Create HomeController & Index instead of Page. Otherwise, we have an empty Index.cshtml file. - if (!_urlUiOptions.RoutePrefix.IsNullOrWhiteSpace()) - { - return Redirect("." + _urlUiOptions.RoutePrefix); - } - - return Page(); - } - } -} \ No newline at end of file diff --git a/modules/docs/app/VoloDocs.Web/package-lock.json b/modules/docs/app/VoloDocs.Web/package-lock.json index a666d3cc7c..e764349469 100644 --- a/modules/docs/app/VoloDocs.Web/package-lock.json +++ b/modules/docs/app/VoloDocs.Web/package-lock.json @@ -5,247 +5,256 @@ "requires": true, "dependencies": { "@abp/anchor-js": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@abp/anchor-js/-/anchor-js-0.5.1.tgz", - "integrity": "sha512-9N/iPP9tDdq9lKRNFQuqRL+QYSv5fq789KgHHGG1/MqExJ6KTPcqUDHvQ53kz96pUgJA+Fyn3dpwgPqLVYI3Yg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/anchor-js/-/anchor-js-1.1.1.tgz", + "integrity": "sha512-hHyYYJ09hhT5xeQJUsBN43yT+y49FKcigq4Wrx8448TrW7r2NJD5i3Xy3BGEstNSlfcQPTsmciQnbkcU2rX1HQ==", "requires": { - "@abp/core": "^0.4.9", - "anchor-js": "^4.1.1" + "@abp/core": "^1.1.1", + "anchor-js": "^4.2.2" } }, "@abp/aspnetcore.mvc.ui": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-0.4.9.tgz", - "integrity": "sha512-AveMEi6WRQmD1tM9yVNLAe6ffcbtoL3ZGQKTPA95Q+dy5xBXVg4Y8jdhxawNPK64KYArhfrVkQDfa4mq3pW5Qw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-1.1.1.tgz", + "integrity": "sha512-wZbptVCSxZzEjkJx+/sWrH9Pikp9nOy7V8Htz+L+S7/qAzfXu5PRVV8ahddfAcDHRk30buRhdbJlCVdt6hkZ6g==", "requires": { - "ansi-colors": "^1.1.0", + "ansi-colors": "^4.1.1", "extend-object": "^1.0.0", - "gulp": "^3.9.1", - "merge-stream": "^1.0.1", + "gulp": "^4.0.2", + "merge-stream": "^2.0.0", "path": "^0.12.7", - "rimraf": "^2.6.2" + "rimraf": "^3.0.0" } }, "@abp/aspnetcore.mvc.ui.theme.basic": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-0.4.9.tgz", - "integrity": "sha512-g3Zby8x8+vWw3oTJsSPasjm4tv7BG4270PEf7jK9w925+lIyKYHn6UxOcFbSU2pi9mJTnaF+Fk/rScIcdxwZZw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-1.1.1.tgz", + "integrity": "sha512-ooXtCM3TWN69RU7xs6avnzOQBXzsiHY5BEIogzSBialZC4uG5H56qrIr4MbsFNae+PQM23Mw2tnJ/Z7dutURCQ==", "requires": { - "@abp/aspnetcore.mvc.ui.theme.shared": "^0.4.9" + "@abp/aspnetcore.mvc.ui.theme.shared": "^1.1.1" } }, "@abp/aspnetcore.mvc.ui.theme.shared": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-0.4.9.tgz", - "integrity": "sha512-Fe4UfQ215Pz2V6D5HUYhDJFeJWjKLcv2gsZfOheWLlOBgYh274pdnUIwceoZ4Btpa9aPECLG+LAH4dTbs8vjRg==", - "requires": { - "@abp/aspnetcore.mvc.ui": "^0.4.9", - "@abp/bootstrap": "^0.4.9", - "@abp/datatables.net-bs4": "^0.4.9", - "@abp/font-awesome": "^0.4.9", - "@abp/jquery-form": "^0.4.9", - "@abp/jquery-validation-unobtrusive": "^0.4.9", - "@abp/lodash": "^0.4.9", - "@abp/select2": "^0.4.9", - "@abp/sweetalert": "^0.4.9", - "@abp/timeago": "^0.4.9", - "@abp/toastr": "^0.4.9" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-1.1.1.tgz", + "integrity": "sha512-LilSyefzT1+rcTU7vbWxcO8TwBgGZIx6QbMUrDicSTH6LLJ9S5+yNaGNJbbZKDG6qx0BEoC1u8dE8KCUshwxoQ==", + "requires": { + "@abp/aspnetcore.mvc.ui": "^1.1.1", + "@abp/bootstrap": "^1.1.1", + "@abp/bootstrap-datepicker": "^1.1.1", + "@abp/datatables.net-bs4": "^1.1.1", + "@abp/font-awesome": "^1.1.1", + "@abp/jquery-form": "^1.1.1", + "@abp/jquery-validation-unobtrusive": "^1.1.1", + "@abp/lodash": "^1.1.1", + "@abp/luxon": "^1.1.1", + "@abp/malihu-custom-scrollbar-plugin": "^1.1.1", + "@abp/select2": "^1.1.1", + "@abp/sweetalert": "^1.1.1", + "@abp/timeago": "^1.1.1", + "@abp/toastr": "^1.1.1" } }, "@abp/bootstrap": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/bootstrap/-/bootstrap-0.4.9.tgz", - "integrity": "sha512-A7lDHo43KnqjINo0xWECx/WzNQe3vqvE3GPv6uXq3KDi+gStQLAFBq3bVcVW/kSxiquDQ/tuCLRpE0qlhoobag==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/bootstrap/-/bootstrap-1.1.1.tgz", + "integrity": "sha512-OIaGJaizhI8UNfy4bnw2xT2Z0QG7BJJrjxOPGepfd4jn/AUi/vFdOpJFWvu2P9PwSzRmn/LuSlr2WONDOdPVWQ==", + "requires": { + "@abp/core": "^1.1.1", + "bootstrap": "^4.3.1" + } + }, + "@abp/bootstrap-datepicker": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/bootstrap-datepicker/-/bootstrap-datepicker-1.1.1.tgz", + "integrity": "sha512-RIQLSrKBu/cTAU2lFenSAoKcMp7wgF4e3nP4/iOu5ZtCgti5vUjMEcqEvBxtlwKTHMXsTG4GtNKqjTwjXMjONQ==", "requires": { - "@abp/core": "^0.4.9", - "bootstrap": "^4.1.1" + "bootstrap-datepicker": "^1.9.0" } }, "@abp/clipboard": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@abp/clipboard/-/clipboard-0.5.1.tgz", - "integrity": "sha512-efOPloVL0moRqGpAMA7DU5o+vzzu7ipGOFcG9nOfDD6uGY9AsOztFc71GwIFrtwADvJSiJHD6OfEIfR++dCT0w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/clipboard/-/clipboard-1.1.1.tgz", + "integrity": "sha512-O6b7VCAh2mxjkPhgUZYQBhXf0u+dWRECBOYN4KxYiKfk+2xL8X+34Ls7hVLbyuD+4xCcNirMZIG5CnS8auHC/A==", "requires": { - "@abp/core": "^0.4.9", + "@abp/core": "^1.1.1", "clipboard": "^2.0.4" } }, "@abp/core": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/core/-/core-0.4.9.tgz", - "integrity": "sha512-JbibIPDz0w/C9YhexNagMD763qbZCclcHLVlTpKEZUcxzp6rZXwPR6ekh/PnvzD0R7wmHOh+bHl0hs5JSdy3oA==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/core/-/core-1.1.1.tgz", + "integrity": "sha512-OvUG7xRvk8nSqwC1s45YPnTuhC2OWe1AVa1nnC6FVHMH/g1Je7UJwnbu47K7uNS+lDRJUIktNbufYKiwutEjRg==" }, "@abp/datatables.net": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/datatables.net/-/datatables.net-0.4.9.tgz", - "integrity": "sha512-URimiGMFBusEMo9aAadLFCossbYyrN2R6+6YrQ+heqxdheCPmm2wupsXSFoUBHBhD0tzF4rY+OL//oOg3Wtg4g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/datatables.net/-/datatables.net-1.1.1.tgz", + "integrity": "sha512-y76IDBlgc0n1YgQqJ+9cfzXpLwr2arhdIfSmqX+qRXz6GfVNY7e3JijkFSgDKUzKGYo1HZMzgJmDmeNIRwMZsQ==", "requires": { - "@abp/core": "^0.4.9", - "datatables.net": "^1.10.16" + "@abp/core": "^1.1.1", + "datatables.net": "^1.10.20" } }, "@abp/datatables.net-bs4": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/datatables.net-bs4/-/datatables.net-bs4-0.4.9.tgz", - "integrity": "sha512-Dsz0fy2haBz3dl2MLXWjW2bhDBzp1cpWXSXj8TEEzYazjhyoqBy6AiFlEnRCqO49KZD6Ps1cONSRxx4c4On9Lg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/datatables.net-bs4/-/datatables.net-bs4-1.1.1.tgz", + "integrity": "sha512-t40xQIGBMLPZiSbcZHW3AwE8uk+xcl7OitBT1jym0XPKVtgJsHez3ynDE5v/PjHe+ColCG8lTRjRnNoXo5dzDw==", "requires": { - "@abp/datatables.net": "^0.4.9", - "datatables.net-bs4": "^1.10.16" + "@abp/datatables.net": "^1.1.1", + "datatables.net-bs4": "^1.10.20" } }, "@abp/docs": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@abp/docs/-/docs-0.5.1.tgz", - "integrity": "sha512-3pGYnxZxm2kYPfu2EwDF0QinsNTym2cWlYAo7mVUBuReTPMhwHYtgrE7AX3nCVv1iAo6ltW+mebonGt1s5yXzA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/docs/-/docs-1.1.1.tgz", + "integrity": "sha512-z2Dk7EOhBdFo6BYbgccD91PIg37L2ehkgRT4RWf61dRyjPAQgPwwwyVSqApFmKveWPCzreHoTPKbnrU7EzAssw==", "requires": { - "@abp/anchor-js": "^0.5.1", - "@abp/clipboard": "^0.5.1", - "@abp/malihu-custom-scrollbar-plugin": "^0.5.1", - "@abp/popper.js": "^0.5.1", - "@abp/prismjs": "^0.5.1" + "@abp/anchor-js": "^1.1.1", + "@abp/clipboard": "^1.1.1", + "@abp/malihu-custom-scrollbar-plugin": "^1.1.1", + "@abp/popper.js": "^1.1.1", + "@abp/prismjs": "^1.1.1" } }, "@abp/font-awesome": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/font-awesome/-/font-awesome-0.4.9.tgz", - "integrity": "sha512-H4H/PJeSypoq++jP7eqS3y5jpbPIglUByPymHnho7U84gO06tZBUIoK00zyLACUVLzNztWANhmVqyA6OaHQ6xQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/font-awesome/-/font-awesome-1.1.1.tgz", + "integrity": "sha512-6RHbixi7IVWAb3JCHrUmEYD3HmAH4R75Nuo54LvFzATrh4G6gdBONIeDuTo79OTxe4Zhp+WLxeA49Y21kt77mg==", "requires": { - "@abp/core": "^0.4.9", - "font-awesome": "^4.7.0" + "@abp/core": "^1.1.1", + "@fortawesome/fontawesome-free": "^5.11.2" } }, "@abp/jquery": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/jquery/-/jquery-0.4.9.tgz", - "integrity": "sha512-Lte2rfDhcVXRA18mJMyPs0oFoiX/7x10F9cwMyX4+8e9QulA0dg+MjJZ4HIbxRoKvaYPz0AdE5VZ511folFeTg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/jquery/-/jquery-1.1.1.tgz", + "integrity": "sha512-kj4BTtXF0VbCzCqRXnRVEbGndR3F8NlbBhVQN6BQktOuZta3fvx7f2+pSok8vQv0ddmqUFY7FTT2Ei3l4363LQ==", "requires": { - "@abp/core": "^0.4.9", - "jquery": "^3.3.1" + "@abp/core": "^1.1.1", + "jquery": "^3.4.1" } }, "@abp/jquery-form": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/jquery-form/-/jquery-form-0.4.9.tgz", - "integrity": "sha512-T1LzHNO+L/ATSfsaSu9H5rq9oUGjjBrwcJBVQhWRuGbB/Nsv1t4CdCPZHqbYBsn3ji/6T9C80FMdL+s4iOfy8A==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/jquery-form/-/jquery-form-1.1.1.tgz", + "integrity": "sha512-AIIdN36f8xwr4LgiNnBHohJ5tlxh/r+DuDtXcScpZN6GWBE+XgUotN0pZIIva82IxCyUNdDudzgluX9IjI+00w==", "requires": { - "@abp/jquery": "^0.4.9", + "@abp/jquery": "^1.1.1", "jquery-form": "^4.2.2" } }, "@abp/jquery-validation": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/jquery-validation/-/jquery-validation-0.4.9.tgz", - "integrity": "sha512-8C58G9BTYszW6E0EO6NYqXLEbABXt/yGlOqAFCkPiLVfUcoC6uARqkgIgNVnE99ZUeSmluNsYeCczP0Qb0yNzg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/jquery-validation/-/jquery-validation-1.1.1.tgz", + "integrity": "sha512-YvAjIW8epp+ddu01BTUkZWPfEADAvNPJeUrrZ6OpcPWM15Tf+ddr4ATgJ1LCg0Bh5F09iQC855osow3lt8sc7g==", "requires": { - "@abp/jquery": "^0.4.9", - "jquery-validation": "^1.17.0" + "@abp/jquery": "^1.1.1", + "jquery-validation": "^1.19.1" } }, "@abp/jquery-validation-unobtrusive": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-0.4.9.tgz", - "integrity": "sha512-GR62QEPexX4uLcqF3y72V39nlx/70C7ubeaIxS1ZEq+0wGklwAiyq1p19qpB5Cuj2GJL+vFrIig5w+B79P2iAw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-1.1.1.tgz", + "integrity": "sha512-q1b0KG8l3DXUiW8JXdq9l1jR/CwgzrZdxwdKGLB2J/oxHlywQIb7yrjR6WGCshjPpcx2SkOL0j/ZXMIMh533hQ==", "requires": { - "@abp/jquery-validation": "^0.4.9", - "jquery-validation-unobtrusive": "^3.2.9" + "@abp/jquery-validation": "^1.1.1", + "jquery-validation-unobtrusive": "^3.2.11" } }, "@abp/lodash": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/lodash/-/lodash-0.4.9.tgz", - "integrity": "sha512-/itYbXQL145WigKIGpnyWgARml7QGie7xqhC1WZG5iUkdqLx+4YBXesdzGFykthDewiQNz8mB2gPk64d376AAw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/lodash/-/lodash-1.1.1.tgz", + "integrity": "sha512-nH7bRS28Tf4hEXcpKHd1IM+MzYTqX8t3htGmsLX4UESQd52eODYOIldtX6gm3OW1O6ECwW6si/o0M2pTEpQqvg==", "requires": { - "@abp/core": "^0.4.9", - "lodash": "^4.17.10" - }, - "dependencies": { - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - } + "@abp/core": "^1.1.1", + "lodash": "^4.17.15" + } + }, + "@abp/luxon": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/luxon/-/luxon-1.1.1.tgz", + "integrity": "sha512-WNu8JRSb5FDXfcDwjMYyeYeUN48uuDc/I2cdo3xd1rcY+lbmbzxoG9IYOlE8cRHdgX3z82qsZXFs2lcAy0Le2g==", + "requires": { + "luxon": "^1.21.3" } }, "@abp/malihu-custom-scrollbar-plugin": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-0.5.1.tgz", - "integrity": "sha512-i9XorWsWcqb3tbbiwvE4OQCfXXJO38HlkQgvlwzxshB8xDcS9v/JL5fAAvM/Cr66fB2zybsrCseq9QcBXL+yCQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/malihu-custom-scrollbar-plugin/-/malihu-custom-scrollbar-plugin-1.1.1.tgz", + "integrity": "sha512-n4b4QK/L1Czdx0oOpUR/bWjK9VENexfUSV/aMjwzHhDmEFABAmEfhIpudCYDwewGswrd7C9agmBvakv2rwPQeA==", "requires": { - "@abp/core": "^0.4.9", + "@abp/core": "^1.1.1", "malihu-custom-scrollbar-plugin": "^3.1.5" } }, "@abp/popper.js": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@abp/popper.js/-/popper.js-0.5.1.tgz", - "integrity": "sha512-qS97hQQtG78mVPRvmZZtBb7Vu905NpThl9PN3jPWWDiVbOV2/d+BXCTRhTRbfdup3C5cmGdntjaGxfcxlxHt1Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/popper.js/-/popper.js-1.1.1.tgz", + "integrity": "sha512-heR73cqmMsVPNgsPxBYbkvc842R3hEEuDAj4oaXZwVTeWXayU6TdDcGdIrfwMZwW2eWivYNnO0bMOVmuhZKTTQ==", "requires": { - "@abp/core": "^0.4.9", - "popper.js": "^1.14.6" + "@abp/core": "^1.1.1", + "popper.js": "^1.16.0" } }, "@abp/prismjs": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@abp/prismjs/-/prismjs-0.5.1.tgz", - "integrity": "sha512-DxoidAVV8LmtgFonbzGJEfszQDUaHEQc+/bcJtyQCuv5l+uflVnpAH+6W8IAErt5N96a0RJbeV9ZBXjIhqOzpA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/prismjs/-/prismjs-1.1.1.tgz", + "integrity": "sha512-kZh2imqVTMDWmE2v+S4wMsigu/hSyVaz3VvZzGctFNctzC17LeD6z6ymfrtQ5BPJbCxGSpHwOt4/Y8bycPTEuQ==", "requires": { - "@abp/core": "^0.4.9", - "prismjs": "^1.15.0" + "@abp/core": "^1.1.1", + "prismjs": "^1.17.1" } }, "@abp/select2": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/select2/-/select2-0.4.9.tgz", - "integrity": "sha512-AaJHWy9fkP/LqVkruZGfvzgdZXyT96/FpnflUpoNPXjuvVRptkJpd/BQiWjpyjS+qzuZ+m3X9dms0d0lAyFKYA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/select2/-/select2-1.1.1.tgz", + "integrity": "sha512-t0qcJhD+uo2+XWr4nmMQLAx7MRGQUBdZ81YmGty045ReoSaEKQf4haLkzBcMzpBRusiyMQO/PbxjtwMw/xJQTQ==", "requires": { - "@abp/core": "^0.4.9", - "select2": "^4.0.5" + "@abp/core": "^1.1.1", + "select2": "^4.0.12" } }, "@abp/sweetalert": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/sweetalert/-/sweetalert-0.4.9.tgz", - "integrity": "sha512-FaxtELo7Um/uasasbOGi1j1XuBtZ819mUo7+6pMSWj2CSAkg2A/RyT9NhYcX8oyGpv188W5CjZfJ1DM0bRH+pQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/sweetalert/-/sweetalert-1.1.1.tgz", + "integrity": "sha512-V6K/qg7J/bdFmom2kaXYeiLvcmLHFl+MacPX4yYAK2biZdb2pWOkUdmcAzZdOT+UruKfLRhvraVC2uXDySi9NA==", "requires": { - "@abp/core": "^0.4.9", - "sweetalert": "^2.1.0" + "@abp/core": "^1.1.1", + "sweetalert": "^2.1.2" } }, "@abp/timeago": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/timeago/-/timeago-0.4.9.tgz", - "integrity": "sha512-RsAyJCl+rEWQL0q0Nx/ijy+iOBYm5IC5Re6y4SQ1jYF/B4n88GM2PFy6kLrE+HHGl5Z5hkWr5OGXmFem50Y3Wg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/timeago/-/timeago-1.1.1.tgz", + "integrity": "sha512-QYYih/4n6XhCqkRw7fBfyg58T5CHqJHyz7SAfq86RiKAJ4jVtjdSVxj3XKxz8eCb56wZGsO1xXXStI3vdLwwNw==", "requires": { - "@abp/jquery": "^0.4.9", - "timeago": "^1.6.3" + "@abp/jquery": "^1.1.1", + "timeago": "^1.6.7" } }, "@abp/toastr": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@abp/toastr/-/toastr-0.4.9.tgz", - "integrity": "sha512-KjnETa1Og5EIMDw/pIxPoKLTABBcsxBN8BqwIG33ya2+BIRfAI7b0lEMoKK+qmwVwI5dkXZlKFGIUmPtb+zVCw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@abp/toastr/-/toastr-1.1.1.tgz", + "integrity": "sha512-GDewBppm+0FO6kTTy0huczoH9P5q6lFicHFAoEawAMkuWJFW/Ihv/YnEvKGDQwGftuVSWexfqBMN/RZ5YSOiGQ==", "requires": { - "@abp/jquery": "^0.4.9", + "@abp/jquery": "^1.1.1", "toastr": "^2.1.4" } }, - "almond": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/almond/-/almond-0.3.3.tgz", - "integrity": "sha1-oOfJWsdiTWQXtElLHmi/9pMWiiA=" + "@fortawesome/fontawesome-free": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz", + "integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==" }, "anchor-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/anchor-js/-/anchor-js-4.1.1.tgz", - "integrity": "sha512-c2Wl9F1X0C4jkYKLla1SNE2uI6xJrSKsRC7HCCg4yLNQ5sL5D+tDEWrjRaoTuTlMTqBCnF6kOuR3dx59Erxpvw==" + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/anchor-js/-/anchor-js-4.2.2.tgz", + "integrity": "sha512-Rg1tGaG4K3avYqDh7rOYCE/odWxpUiHStnlKL/bGOt9cl6NjR06zhPGVQcCAjE5PT48oQeHVgqNmLzxh0Kuk4A==" }, "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "requires": { - "ansi-wrap": "^0.1.0" - } + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" }, "ansi-gray": { "version": "0.1.1", @@ -260,16 +269,28 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, "ansi-wrap": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "requires": { + "buffer-equal": "^1.0.0" + } + }, "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", @@ -280,35 +301,89 @@ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "requires": { + "make-iterator": "^1.0.0" + } + }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "requires": { + "make-iterator": "^1.0.0" + } + }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" }, - "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" - }, "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } }, "array-unique": { "version": "0.3.2", @@ -320,11 +395,51 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, + "async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "requires": { + "async-done": "^1.2.2" + } + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -380,15 +495,32 @@ } } }, - "beeper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=" + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } }, "bootstrap": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz", - "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w==" + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz", + "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==" + }, + "bootstrap-datepicker": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.9.0.tgz", + "integrity": "sha512-9rYYbaVOheGYxjOr/+bJCmRPihfy+LkLSg4fIFMT9Od8WwWB/MB50w0JO1eBgKUMbb7PFHQD5uAfI3ArAxZRXA==", + "requires": { + "jquery": ">=1.7.1 <4.0.0" + } }, "brace-expansion": { "version": "1.1.11", @@ -426,6 +558,16 @@ } } }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -442,16 +584,35 @@ "unset-value": "^1.0.0" } }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + } } }, "class-utils": { @@ -476,24 +637,64 @@ } }, "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", + "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", "tiny-emitter": "^2.0.0" } }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" }, "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } }, "collection-visit": { "version": "1.0.0", @@ -510,47 +711,79 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "datatables.net": { - "version": "1.10.19", - "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.19.tgz", - "integrity": "sha512-+ljXcI6Pj3PTGy5pesp3E5Dr3x3AV45EZe0o1r0gKENN2gafBKXodVnk2ypKwl2tTmivjxbkiqoWnipTefyBTA==", + "version": "1.10.20", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.20.tgz", + "integrity": "sha512-4E4S7tTU607N3h0fZPkGmAtr9mwy462u+VJ6gxYZ8MxcRIjZqHy3Dv1GNry7i3zQCktTdWbULVKBbkAJkuHEnQ==", "requires": { "jquery": ">=1.7" } }, "datatables.net-bs4": { - "version": "1.10.19", - "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.19.tgz", - "integrity": "sha512-pgeP17w4aPR7HIxIwuJghfqXULjdg1K6xMUUKDyCERJRSNNK4MRToFfELtIsluLNN555YBK4Kx8nihX5/ZT1Fw==", + "version": "1.10.20", + "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.20.tgz", + "integrity": "sha512-kQmMUMsHMOlAW96ztdoFqjSbLnlGZQ63iIM82kHbmldsfYdzuyhbb4hTx6YNBi481WCO3iPSvI6YodNec46ZAw==", "requires": { - "datatables.net": "1.10.19", + "datatables.net": "1.10.20", "jquery": ">=1.7" } }, - "dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -559,17 +792,42 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "requires": { - "clone": "^1.0.2" + "object-keys": "^1.0.12" } }, "define-property": { @@ -614,30 +872,65 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, - "deprecated": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", - "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=" - }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" }, - "duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "requires": { - "readable-stream": "~1.1.9" + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" } }, "end-of-stream": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", - "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "requires": { - "once": "~1.3.0" + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, "es6-object-assign": { @@ -645,10 +938,25 @@ "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } }, "expand-brackets": { "version": "2.1.4", @@ -690,6 +998,21 @@ "homedir-polyfill": "^1.0.1" } }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -779,15 +1102,22 @@ } }, "fancy-log": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", - "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "requires": { "ansi-gray": "^0.1.1", "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", "time-stamp": "^1.0.0" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -809,26 +1139,30 @@ } } }, - "find-index": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", - "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=" + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } }, "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", "requires": { "detect-file": "^1.0.0", - "is-glob": "^3.1.0", + "is-glob": "^4.0.0", "micromatch": "^3.0.4", "resolve-dir": "^1.0.1" } }, "fined": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", - "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "requires": { "expand-tilde": "^2.0.2", "is-plain-object": "^2.0.3", @@ -837,20 +1171,19 @@ "parse-filepath": "^1.0.1" } }, - "first-chunk-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", - "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=" - }, "flagged-respawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", - "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" }, - "font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } }, "for-in": { "version": "1.0.2", @@ -873,84 +1206,579 @@ "map-cache": "^0.2.2" } }, - "fs.realpath": { + "fs-mkdirp-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gaze": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", - "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", "requires": { - "globule": "~0.1.0" + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "glob": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", - "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "fsevents": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", + "optional": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" - } - }, - "glob-stream": { - "version": "3.1.18", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", - "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", - "requires": { - "glob": "^4.3.1", - "glob2base": "^0.0.12", - "minimatch": "^2.0.1", - "ordered-read-streams": "^0.1.0", - "through2": "^0.6.1", - "unique-stream": "^1.0.0" + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" }, "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } - } + }, + "chownr": { + "version": "1.1.4", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.3", + "bundled": true, + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.3.3", + "bundled": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.3", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "optional": true + } } }, - "glob-watcher": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", - "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", "requires": { - "gaze": "^0.5.1" + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" } }, - "glob2base": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "glob-watcher": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", "requires": { - "find-index": "^0.1.1" + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" } }, "global-modules": { @@ -975,51 +1803,10 @@ "which": "^1.2.14" } }, - "globule": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", - "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", - "requires": { - "glob": "~3.1.21", - "lodash": "~1.0.1", - "minimatch": "~0.2.11" - }, - "dependencies": { - "glob": { - "version": "3.1.21", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", - "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", - "requires": { - "graceful-fs": "~1.2.0", - "inherits": "1", - "minimatch": "~0.2.11" - } - }, - "graceful-fs": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", - "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=" - }, - "inherits": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", - "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=" - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" - } - } - } - }, "glogg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", - "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "requires": { "sparkles": "^1.0.0" } @@ -1033,56 +1820,54 @@ } }, "graceful-fs": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", - "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", - "requires": { - "natives": "^1.1.0" - } + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "gulp": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", - "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", - "requires": { - "archy": "^1.0.0", - "chalk": "^1.0.0", - "deprecated": "^0.0.1", - "gulp-util": "^3.0.0", - "interpret": "^1.0.0", - "liftoff": "^2.1.0", - "minimist": "^1.1.0", - "orchestrator": "^0.3.0", - "pretty-hrtime": "^1.0.0", - "semver": "^4.1.0", - "tildify": "^1.0.0", - "v8flags": "^2.0.2", - "vinyl-fs": "^0.3.0" - } - }, - "gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", - "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", - "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "requires": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "gulp-cli": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz", + "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.1.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.0.1", + "yargs": "^7.1.0" + } + } } }, "gulplog": { @@ -1093,21 +1878,10 @@ "glogg": "^1.0.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-gulplog": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", - "requires": { - "sparkles": "^1.0.0" - } + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "has-value": { "version": "1.0.0", @@ -1139,13 +1913,18 @@ } }, "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "requires": { "parse-passwd": "^1.0.0" } }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1156,9 +1935,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", @@ -1166,9 +1945,14 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "is-absolute": { "version": "1.0.0", @@ -1197,6 +1981,19 @@ } } }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -1247,14 +2044,27 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "^2.1.1" } }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -1302,15 +2112,20 @@ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=" + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -1323,9 +2138,9 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" }, "jquery-form": { "version": "4.2.2", @@ -1341,154 +2156,103 @@ "integrity": "sha1-BvAzXxbjU6aV5yBr9QUDy1I6buU=" }, "jquery-validation": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/jquery-validation/-/jquery-validation-1.18.0.tgz", - "integrity": "sha512-+MK0pvoegfLrJnwtCU6tx305Vgp9HWevpmdVwDf5TthmINkn0wqqLD0bpa75EERlHsBBjMmza//ppx5ZQPnW3Q==", - "requires": { - "jquery": "^1.7 || ^2.0 || ^3.1" - } + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jquery-validation/-/jquery-validation-1.19.1.tgz", + "integrity": "sha512-QNnrZBqSltWUEJx+shOY5WtfrIb0gWmDjFfQP8rZKqMMSfpRSwEkSqhfHPvDfkObD8Hnv5KHSYI8yg73sVFdqA==" }, "jquery-validation-unobtrusive": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.2.10.tgz", - "integrity": "sha512-z9ZBP/HslaGNKzFSpfLNJoFm2iqPJfE6CKM0H5e9LmKnYTFxErvCFQZomOLiTmLmZi8Wi/otW38cEXExVDha0w==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.2.11.tgz", + "integrity": "sha512-3FQPllaWdD+Aq55zJLGSW39+eXPDz1HhwAvrSwYi8zHQ8DVcu5IJ1HVeTiCl0BnCnrIBvfFU3zEB/DrGdcoRIQ==", "requires": { "jquery": ">=1.8", "jquery-validation": ">=1.16" } }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=" + }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, - "liftoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", - "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "requires": { - "extend": "^3.0.0", - "findup-sync": "^2.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" } }, - "lodash": { - "version": "1.0.2", - "resolved": "http://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", - "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=" - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" - }, - "lodash._basetostring": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", - "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=" - }, - "lodash._basevalues": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", - "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=" - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" - }, - "lodash._reescape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", - "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=" - }, - "lodash._reevaluate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", - "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" - }, - "lodash._root": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" - }, - "lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "requires": { - "lodash._root": "^3.0.0" + "readable-stream": "^2.0.5" } }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "invert-kv": "^1.0.0" } }, - "lodash.restparam": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "requires": { + "flush-write-stream": "^1.0.2" + } }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" } }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" } }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "luxon": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.22.2.tgz", + "integrity": "sha512-vq6eSaOOw1fKob+JXwfu0e3/UFUT4G4HTFRJab7dch8J1OdOGW/vXqCiJsY7rm2In+5gKNYx0EtnYT0Tc5V4Qw==" }, "make-iterator": { "version": "1.0.1", @@ -1519,43 +2283,43 @@ "object-visit": "^1.0.0" } }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", "requires": { - "readable-stream": "^2.0.1" + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "requires": { - "safe-buffer": "~5.1.0" + "is-extglob": "^2.1.0" } } } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -1577,22 +2341,17 @@ } }, "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "^1.0.0" + "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -1608,33 +2367,21 @@ } } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "multipipe": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", - "requires": { - "duplexer2": "0.0.2" - } + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "optional": true }, "nanomatch": { "version": "1.2.13", @@ -1654,15 +2401,42 @@ "to-regex": "^3.0.1" } }, - "natives": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.5.tgz", - "integrity": "sha512-1pJ+02gl2KJgCPFtpZGtuD4lGSJnIZvvFHCQTOeDRMSXjfu2GmYWuhI8NFMA4W2I5NNFRbfy/YCiVt4CgNpP8A==" + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "requires": { + "once": "^1.3.2" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-copy": { "version": "0.1.0", @@ -1692,12 +2466,28 @@ } } }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "requires": { - "isobject": "^3.0.0" + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, "object.defaults": { @@ -1728,33 +2518,38 @@ "isobject": "^3.0.1" } }, - "once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", "requires": { - "wrappy": "1" + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" } }, - "orchestrator": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", - "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "end-of-stream": "~0.1.5", - "sequencify": "~0.0.7", - "stream-consume": "~0.1.0" + "wrappy": "1" } }, "ordered-read-streams": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", - "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "requires": { + "readable-stream": "^2.0.1" + } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } }, "parse-filepath": { "version": "1.0.2", @@ -1766,6 +2561,19 @@ "path-root": "^0.1.1" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" + }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -1785,6 +2593,19 @@ "util": "^0.10.3" } }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1808,10 +2629,38 @@ "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, "popper.js": { - "version": "1.14.6", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz", - "integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA==" + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "posix-character-classes": { "version": "0.1.1", @@ -1824,9 +2673,9 @@ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" }, "prismjs": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.15.0.tgz", - "integrity": "sha512-Lf2JrFYx8FanHrjoV5oL8YHCclLQgbJcVZR+gikGGMqz6ub5QVWDTM6YIwm3BuPxM/LOV+rKns3LssXNLIf+DA==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.19.0.tgz", + "integrity": "sha512-IVFtbW9mCWm9eOIaEkNyo2Vl4NnEifis2GQ7/MLRG5TQe6t+4Sj9J5QWI9i3v+SS43uZBlCAOn+zYTVYQcPXJw==", "requires": { "clipboard": "^2.0.0" } @@ -1837,24 +2686,75 @@ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise-polyfill": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", "integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=" }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, "readable-stream": { - "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } }, "rechoir": { @@ -1874,6 +2774,30 @@ "safe-regex": "^1.1.0" } }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", @@ -1885,16 +2809,36 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", "requires": { - "path-parse": "^1.0.5" + "path-parse": "^1.0.6" } }, "resolve-dir": { @@ -1906,6 +2850,14 @@ "global-modules": "^1.0.0" } }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "requires": { + "value-or-function": "^3.0.0" + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -1917,34 +2869,11 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "requires": { - "glob": "^7.0.5" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - } + "glob": "^7.1.3" } }, "safe-buffer": { @@ -1966,28 +2895,32 @@ "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" }, "select2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/select2/-/select2-4.0.5.tgz", - "integrity": "sha1-eqxQaSVhmFs007guxV4ib4lg1Ao=", - "requires": { - "almond": "~0.3.1", - "jquery-mousewheel": "~3.1.13" - } + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/select2/-/select2-4.0.13.tgz", + "integrity": "sha512-1JeB87s6oN/TDxQQYCvS5EFoQyvV6eYMZZ0AeA4tdFDYWN3BAGZ8npr17UBFddU0lgAt3H0yjX3X6/ekOj1yjw==" }, "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "requires": { + "sver-compat": "^1.5.0" + } }, - "sequencify": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", - "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=" + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -2005,11 +2938,6 @@ } } }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -2113,11 +3041,11 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "requires": { - "atob": "^2.1.1", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -2134,6 +3062,34 @@ "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==" }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -2142,6 +3098,11 @@ "extend-shallow": "^3.0.0" } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -2161,15 +3122,33 @@ } } }, - "stream-consume": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", - "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==" + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==" + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } }, "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } }, "strip-ansi": { "version": "3.0.1", @@ -2180,72 +3159,47 @@ } }, "strip-bom": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", - "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "requires": { - "first-chunk-stream": "^1.0.0", "is-utf8": "^0.2.0" } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } }, "sweetalert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.0.tgz", - "integrity": "sha512-9YKj0SvjKyBfRWco50UOsIbXVeifYbxzT9Qda7EsqC01eafHGCSG0IR7g942ufjzt7lnwO8ZZBwr6emXv2fQrg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.2.tgz", + "integrity": "sha512-iWx7X4anRBNDa/a+AdTmvAzQtkN1+s4j/JJRWlHpYE8Qimkohs8/XnFcWeYHH2lMA8LRCa5tj2d244If3S/hzA==", "requires": { "es6-object-assign": "^1.1.0", "promise-polyfill": "^6.0.2" } }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "requires": { - "readable-stream": "^2.1.5", + "readable-stream": "~2.3.6", "xtend": "~4.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, - "tildify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", - "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "requires": { - "os-homedir": "^1.0.0" + "through2": "~2.0.0", + "xtend": "~4.0.0" } }, "time-stamp": { @@ -2254,17 +3208,26 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" }, "timeago": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/timeago/-/timeago-1.6.3.tgz", - "integrity": "sha512-x97d1X1KsNapWJTgCOOAy/59XagYu2WsDTAH/yvPsWi5bqtGbLPaVZBv3HZ3jTpakHR+JGGyrI9qC0yuvIAvnQ==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/timeago/-/timeago-1.6.7.tgz", + "integrity": "sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ==", "requires": { - "jquery": ">=1.2.3" + "jquery": ">=1.5.0 <4.0" } }, "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "to-absolute-glob": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz", - "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==" + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } }, "to-object-path": { "version": "0.3.0", @@ -2304,6 +3267,14 @@ "repeat-string": "^1.6.1" } }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "requires": { + "through2": "^2.0.3" + } + }, "toastr": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz", @@ -2312,47 +3283,61 @@ "jquery": ">=1.12.0" } }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" }, + "undertaker": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", + "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=" + }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", - "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } }, "unset-value": { "version": "1.0.0", @@ -2387,14 +3372,14 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" } } }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -2405,17 +3390,19 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, - "user-home": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=" - }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", "requires": { "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } } }, "util-deprecate": { @@ -2424,72 +3411,76 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "v8flags": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", + "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "requires": { - "user-home": "^1.1.1" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=" + }, "vinyl": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" } }, "vinyl-fs": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", - "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", - "requires": { - "defaults": "^1.0.0", - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" - }, - "dependencies": { - "clone": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", - "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" - } - }, - "vinyl": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", - "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", - "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" - } - } + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" } }, "which": { @@ -2500,15 +3491,62 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "^3.0.0" + } } } } diff --git a/modules/docs/app/VoloDocs.Web/package.json b/modules/docs/app/VoloDocs.Web/package.json index fca480e355..b711db27ba 100644 --- a/modules/docs/app/VoloDocs.Web/package.json +++ b/modules/docs/app/VoloDocs.Web/package.json @@ -6,4 +6,4 @@ "@abp/aspnetcore.mvc.ui.theme.basic": "^1.0.2", "@abp/docs": "^1.0.2" } -} \ No newline at end of file +} diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs index 57ade1bb4b..1000091b65 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs @@ -4,11 +4,13 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Newtonsoft.Json; +using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.Caching; using Volo.Docs.Documents; using Volo.Docs.Documents.FullSearch.Elastic; using Volo.Docs.Projects; +using Volo.Extensions; namespace Volo.Docs.Admin.Documents { @@ -38,15 +40,19 @@ namespace Volo.Docs.Admin.Documents { var project = await _projectRepository.GetAsync(input.ProjectId); - var navigationFile = await GetDocumentAsync( + var navigationDocument = await GetDocumentAsync( project, project.NavigationDocumentName, input.LanguageCode, input.Version ); - var nav = JsonConvert.DeserializeObject(navigationFile.Content); - var leafs = nav.Items.GetAllNodes(x => x.Items) + if (!JsonConvertExtensions.TryDeserializeObject(navigationDocument.Content, out var navigation)) + { + throw new UserFriendlyException($"Cannot validate navigation file '{project.NavigationDocumentName}' for the project {project.Name}."); + } + + var leafs = navigation.Items.GetAllNodes(x => x.Items) .Where(x => x.IsLeaf && !x.Path.IsNullOrWhiteSpace()) .ToList(); diff --git a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs index 9863995d15..4e6138d260 100644 --- a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs +++ b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs @@ -7,9 +7,11 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; +using Volo.Abp; using Volo.Abp.Caching; using Volo.Docs.Documents.FullSearch.Elastic; using Volo.Docs.Projects; +using Volo.Extensions; namespace Volo.Docs.Documents { @@ -32,8 +34,8 @@ namespace Volo.Docs.Documents IDistributedCache languageCache, IDistributedCache resourceCache, IDistributedCache documentUpdateCache, - IHostEnvironment hostEnvironment, - IDocumentFullSearch documentFullSearch, + IHostEnvironment hostEnvironment, + IDocumentFullSearch documentFullSearch, IOptions docsElasticSearchOptions) { _projectRepository = projectRepository; @@ -82,7 +84,10 @@ namespace Volo.Docs.Documents input.Version ); - var navigationNode = JsonConvert.DeserializeObject(navigationDocument.Content); + if (!JsonConvertExtensions.TryDeserializeObject(navigationDocument.Content, out var navigationNode)) + { + throw new UserFriendlyException($"Cannot validate navigation file '{project.NavigationDocumentName}' for the project {project.Name}."); + } var leafs = navigationNode.Items.GetAllNodes(x => x.Items) .Where(x => !x.Path.IsNullOrWhiteSpace()) @@ -173,11 +178,16 @@ namespace Volo.Docs.Documents input.Version ); - return JsonConvert.DeserializeObject(document.Content); + if (!JsonConvertExtensions.TryDeserializeObject(document.Content, out var documentParameters)) + { + throw new UserFriendlyException($"Cannot validate document parameters file '{project.ParametersDocumentName}' for the project {project.Name}."); + } + + return documentParameters; } catch (DocumentNotFoundException) { - Logger.LogWarning($"Parameter file ({project.ParametersDocumentName}) not found."); + Logger.LogWarning($"Parameter file ({project.ParametersDocumentName}) not found!"); return new DocumentParametersDto(); } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainConsts.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainConsts.cs new file mode 100644 index 0000000000..ff6bd56ee4 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Docs +{ + public class DocsDomainConsts + { + public static string LanguageConfigFileName = "docs-langs.json"; + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs index 3fa341a848..cc0f03a60a 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/FileSystem/Documents/FileSystemDocumentSource.cs @@ -4,11 +4,13 @@ using System.IO; using System.Security; using System.Threading.Tasks; using Newtonsoft.Json; +using Volo.Abp; using Volo.Abp.Domain.Services; using Volo.Abp.IO; using Volo.Docs.Documents; using Volo.Docs.FileSystem.Projects; using Volo.Docs.Projects; +using Volo.Extensions; namespace Volo.Docs.FileSystem.Documents { @@ -22,7 +24,7 @@ namespace Volo.Docs.FileSystem.Documents var path = Path.Combine(projectFolder, languageCode, documentName); CheckDirectorySecurity(projectFolder, path); - + var content = await FileHelper.ReadAllTextAsync(path); var localDirectory = ""; @@ -55,10 +57,15 @@ namespace Volo.Docs.FileSystem.Documents public async Task GetLanguageListAsync(Project project, string version) { - var path = Path.Combine(project.GetFileSystemPath(), "docs-langs.json"); - var configAsJson = await FileHelper.ReadAllTextAsync(path); + var path = Path.Combine(project.GetFileSystemPath(), DocsDomainConsts.LanguageConfigFileName); + var configJsonContent = await FileHelper.ReadAllTextAsync(path); + + if (!JsonConvertExtensions.TryDeserializeObject(configJsonContent, out var languageConfig)) + { + throw new UserFriendlyException($"Cannot validate language config file '{DocsDomainConsts.LanguageConfigFileName}' for the project {project.Name}."); + } - return JsonConvert.DeserializeObject(configAsJson); + return languageConfig; } public async Task GetResource(Project project, string resourceName, string languageCode, string version) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs index 3e287789e9..012fc7aed9 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs @@ -10,6 +10,8 @@ using Volo.Docs.GitHub.Projects; using Volo.Docs.Projects; using Newtonsoft.Json.Linq; using Octokit; +using Volo.Abp; +using Volo.Extensions; using Project = Volo.Docs.Projects.Project; namespace Volo.Docs.GitHub.Documents @@ -55,11 +57,11 @@ namespace Volo.Docs.GitHub.Documents var lastSignificantUpdateTime = !isNavigationDocument && !isParameterDocument && version == project.LatestVersionBranchName ? await GetLastSignificantUpdateTime( fileCommits, - project, + project, project.GetGitHubInnerUrl(languageCode, documentName), lastKnownSignificantUpdateTime, documentCreationTime - ) ?? lastKnownSignificantUpdateTime + ) ?? lastKnownSignificantUpdateTime : null; var document = new Document(GuidGenerator.Create(), @@ -179,11 +181,16 @@ namespace Volo.Docs.GitHub.Documents var rootUrl = project.GetGitHubUrl(version); var userAgent = project.GetGithubUserAgentOrNull(); - var url = CalculateRawRootUrl(rootUrl) + "docs-langs.json"; + var url = CalculateRawRootUrl(rootUrl) + DocsDomainConsts.LanguageConfigFileName; var configAsJson = await DownloadWebContentAsStringAsync(url, token, userAgent); - return JsonConvert.DeserializeObject(configAsJson); + if (!JsonConvertExtensions.TryDeserializeObject(configAsJson, out var languageConfig)) + { + throw new UserFriendlyException($"Cannot validate language config file '{DocsDomainConsts.LanguageConfigFileName}' for the project {project.Name} - v{version}."); + } + + return languageConfig; } private async Task> GetReleasesAsync(Project project) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Extensions/NewtonsoftJsonExtensions.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Extensions/NewtonsoftJsonExtensions.cs new file mode 100644 index 0000000000..200baa9c2c --- /dev/null +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Extensions/NewtonsoftJsonExtensions.cs @@ -0,0 +1,22 @@ +using System; +using Newtonsoft.Json; + +namespace Volo.Extensions +{ + public static class JsonConvertExtensions + { + public static bool TryDeserializeObject(string jsonContent, out T result) + { + try + { + result = JsonConvert.DeserializeObject(jsonContent); + return true; + } + catch + { + result = default; + return false; + } + } + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs index 4a4f3b2142..990f2a0dec 100644 --- a/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs @@ -7,16 +7,17 @@ using Newtonsoft.Json; using Scriban; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Volo.Docs.Documents; +using Volo.Abp; +using Volo.Extensions; namespace Volo.Docs.HtmlConverting { public class ScribanDocumentSectionRenderer : IDocumentSectionRenderer { - private const string jsonOpener = "````json"; - private const string jsonCloser = "````"; - private const string docs_param = "//[doc-params]"; - private const string docs_templates = "//[doc-template]"; + private const string JsonOpener = "````json"; + private const string JsonCloser = "````"; + private const string DocsParam = "//[doc-params]"; + private const string DocsTemplates = "//[doc-template]"; public ILogger Logger { get; set; } @@ -40,6 +41,7 @@ namespace Volo.Docs.HtmlConverting } var result = await scribanTemplate.RenderAsync(parameters); + return RemoveOptionsJson(result); } @@ -47,7 +49,7 @@ namespace Volo.Docs.HtmlConverting { try { - if (!document.Contains(jsonOpener) || !document.Contains(docs_param)) + if (!document.Contains(JsonOpener) || !document.Contains(DocsParam)) { return new Dictionary>(); } @@ -59,9 +61,14 @@ namespace Volo.Docs.HtmlConverting return new Dictionary>(); } - var pureJson = insideJsonSection.Replace(docs_param, "").Trim(); + var pureJson = insideJsonSection.Replace(DocsParam, "").Trim(); + + if (!JsonConvertExtensions.TryDeserializeObject>>(pureJson, out var availableParameters)) + { + throw new UserFriendlyException("ERROR-20200327: Cannot validate JSON content for `AvailableParameters`!"); + } - return JsonConvert.DeserializeObject>>(pureJson); + return await Task.FromResult(availableParameters); } catch (Exception) { @@ -70,12 +77,13 @@ namespace Volo.Docs.HtmlConverting } } - private string RemoveOptionsJson(string document) + private static string RemoveOptionsJson(string document) { var orgDocument = document; + try { - if (!document.Contains(jsonOpener) || !document.Contains(docs_param)) + if (!document.Contains(JsonOpener) || !document.Contains(DocsParam)) { return orgDocument; } @@ -88,8 +96,9 @@ namespace Volo.Docs.HtmlConverting } return document.Remove( - jsonBeginningIndex - jsonOpener.Length, (jsonEndingIndex + jsonCloser.Length) - (jsonBeginningIndex - jsonOpener.Length) - ); + jsonBeginningIndex - JsonOpener.Length, + (jsonEndingIndex + JsonCloser.Length) - (jsonBeginningIndex - JsonOpener.Length) + ); } catch (Exception) { @@ -97,25 +106,25 @@ namespace Volo.Docs.HtmlConverting } } - private (int, int, string) GetJsonBeginEndIndexesAndPureJson(string document) + private static (int, int, string) GetJsonBeginEndIndexesAndPureJson(string document) { var searchedIndex = 0; while (searchedIndex < document.Length) { - var jsonBeginningIndex = document.Substring(searchedIndex).IndexOf(jsonOpener, StringComparison.Ordinal) + jsonOpener.Length + searchedIndex; + var jsonBeginningIndex = document.Substring(searchedIndex).IndexOf(JsonOpener, StringComparison.Ordinal) + JsonOpener.Length + searchedIndex; if (jsonBeginningIndex < 0) { return (-1, -1, ""); } - var jsonEndingIndex = document.Substring(jsonBeginningIndex).IndexOf(jsonCloser, StringComparison.Ordinal) + jsonBeginningIndex; + var jsonEndingIndex = document.Substring(jsonBeginningIndex).IndexOf(JsonCloser, StringComparison.Ordinal) + jsonBeginningIndex; var insideJsonSection = document[jsonBeginningIndex..jsonEndingIndex]; - if (insideJsonSection.IndexOf(docs_param) < 0) + if (insideJsonSection.IndexOf(DocsParam, StringComparison.Ordinal) < 0) { - searchedIndex = jsonEndingIndex + jsonCloser.Length; + searchedIndex = jsonEndingIndex + JsonCloser.Length; continue; } @@ -129,68 +138,84 @@ namespace Volo.Docs.HtmlConverting { var templates = new List(); - while (documentContent.Contains(jsonOpener)) + while (documentContent.Contains(JsonOpener)) { var afterJsonOpener = documentContent.Substring( - documentContent.IndexOf(jsonOpener, StringComparison.Ordinal) + jsonOpener.Length); + documentContent.IndexOf(JsonOpener, StringComparison.Ordinal) + JsonOpener.Length + ); var betweenJsonOpenerAndCloser = afterJsonOpener.Substring(0, - afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal)); + afterJsonOpener.IndexOf(JsonCloser, StringComparison.Ordinal) + ); documentContent = afterJsonOpener.Substring( - afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal) + jsonCloser.Length); + afterJsonOpener.IndexOf(JsonCloser, StringComparison.Ordinal) + JsonCloser.Length + ); - if (!betweenJsonOpenerAndCloser.Contains(docs_templates)) + if (!betweenJsonOpenerAndCloser.Contains(DocsTemplates)) { continue; } - var json = betweenJsonOpenerAndCloser.Substring(betweenJsonOpenerAndCloser.IndexOf(docs_templates, StringComparison.Ordinal) + docs_templates.Length); + var json = betweenJsonOpenerAndCloser.Substring(betweenJsonOpenerAndCloser.IndexOf(DocsTemplates, StringComparison.Ordinal) + DocsTemplates.Length); - var template = JsonConvert.DeserializeObject(json); + if (!JsonConvertExtensions.TryDeserializeObject(json, out var template)) + { + throw new UserFriendlyException($"ERROR-20200327: Cannot validate JSON content for `AvailableParameters`!"); + } templates.Add(template); } - return templates; + return await Task.FromResult(templates); } - private string SetPartialTemplates(string document, List templates) + private static string SetPartialTemplates(string document, IReadOnlyCollection templates) { var newDocument = new StringBuilder(); - while (document.Contains(jsonOpener)) + while (document.Contains(JsonOpener)) { var beforeJson = document.Substring(0, - document.IndexOf(jsonOpener, StringComparison.Ordinal) + jsonOpener.Length); + document.IndexOf(JsonOpener, StringComparison.Ordinal) + JsonOpener.Length + ); var afterJsonOpener = document.Substring( - document.IndexOf(jsonOpener, StringComparison.Ordinal) + jsonOpener.Length); + document.IndexOf(JsonOpener, StringComparison.Ordinal) + JsonOpener.Length + ); var betweenJsonOpenerAndCloser = afterJsonOpener.Substring(0, - afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal)); + afterJsonOpener.IndexOf(JsonCloser, StringComparison.Ordinal) + ); - if (!betweenJsonOpenerAndCloser.Contains(docs_templates)) + if (!betweenJsonOpenerAndCloser.Contains(DocsTemplates)) { document = afterJsonOpener.Substring( - afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal) + jsonCloser.Length); - newDocument.Append(beforeJson + betweenJsonOpenerAndCloser + jsonCloser); + afterJsonOpener.IndexOf(JsonCloser, StringComparison.Ordinal) + JsonCloser.Length + ); + + newDocument.Append(beforeJson + betweenJsonOpenerAndCloser + JsonCloser); continue; } - var json = betweenJsonOpenerAndCloser.Substring(betweenJsonOpenerAndCloser.IndexOf(docs_templates, StringComparison.Ordinal) + docs_templates.Length); - - var templatePath = JsonConvert.DeserializeObject(json)?.Path; + var json = betweenJsonOpenerAndCloser.Substring( + betweenJsonOpenerAndCloser.IndexOf(DocsTemplates, StringComparison.Ordinal) + DocsTemplates.Length + ); - var template = templates.FirstOrDefault(t => t.Path == templatePath); + if (JsonConvertExtensions.TryDeserializeObject(json, out var documentPartialTemplateWithValuesDto)) + { + var template = templates.FirstOrDefault(t => t.Path == documentPartialTemplateWithValuesDto.Path); - var beforeTemplate = document.Substring(0, - document.IndexOf(jsonOpener, StringComparison.Ordinal)); + var beforeTemplate = document.Substring(0, + document.IndexOf(JsonOpener, StringComparison.Ordinal) + ); - newDocument.Append(beforeTemplate + template?.Content + jsonCloser); + newDocument.Append(beforeTemplate + template?.Content + JsonCloser); - document = afterJsonOpener.Substring( - afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal) + jsonCloser.Length); + document = afterJsonOpener.Substring( + afterJsonOpener.IndexOf(JsonCloser, StringComparison.Ordinal) + JsonCloser.Length + ); + } } newDocument.Append(document); diff --git a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj index ed9cf98af8..afec611259 100644 --- a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj +++ b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj @@ -19,6 +19,7 @@ + From 5c541897808bd96f08a21beac8247b2dbfe61587 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Fri, 27 Mar 2020 12:34:18 +0300 Subject: [PATCH 27/64] add home page #3381 --- .../Controllers/HomeController.cs | 19 ------ .../docs/app/VoloDocs.Web/Pages/Index.cshtml | 23 ++++++++ .../app/VoloDocs.Web/Pages/Index.cshtml.cs | 59 +++++++++++++++++++ 3 files changed, 82 insertions(+), 19 deletions(-) delete mode 100644 modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs create mode 100644 modules/docs/app/VoloDocs.Web/Pages/Index.cshtml create mode 100644 modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs diff --git a/modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs b/modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs deleted file mode 100644 index 17c6550502..0000000000 --- a/modules/docs/app/VoloDocs.Web/Controllers/HomeController.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Volo.Abp.AspNetCore.Mvc; - -namespace VoloDocs.Web.Controllers -{ - public class HomeController : AbpController - { - public void Index() - { - //if (!_urlUiOptions.RoutePrefix.IsNullOrWhiteSpace()) - //{ - // return Redirect("." + _urlUiOptions.RoutePrefix); - //} - - //return Page(); - - //return Redirect("/en/abp/latest"); - } - } -} diff --git a/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml b/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml new file mode 100644 index 0000000000..0a212f1499 --- /dev/null +++ b/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml @@ -0,0 +1,23 @@ +@page +@model VoloDocs.Web.Pages.IndexModel +@{ +} + +@if (!Model.Projects.Any()) +{ + + No projects found!
+ See
documentation to see how you can create a new one. + +} +else +{ +

Projects

+ + + @foreach (var project in Model.Projects) + { + @project.Name + } + +} \ No newline at end of file diff --git a/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs b/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs new file mode 100644 index 0000000000..537d650f83 --- /dev/null +++ b/modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Options; +using Volo.Docs; +using Volo.Docs.Projects; + +namespace VoloDocs.Web.Pages +{ + public class IndexModel : PageModel + { + public IReadOnlyList Projects { get; set; } + + private readonly DocsUiOptions _urlUiOptions; + + private readonly IProjectAppService _projectAppService; + + public IndexModel(IOptions urlOptions, IProjectAppService projectAppService) + { + _projectAppService = projectAppService; + _urlUiOptions = urlOptions.Value; + } + + public async Task OnGetAsync() + { + var projects = await _projectAppService.GetListAsync(); + + if (projects.Items.Count == 1) + { + return await RedirectToProjectAsync(projects.Items.First()); + } + else if (projects.Items.Count > 1) + { + Projects = projects.Items; + } + + return Page(); + } + + private async Task RedirectToProjectAsync(ProjectDto project, string language = "en", string version = null) + { + var path = GetUrlForProject(project, language, version); + return await Task.FromResult(Redirect(path)); + } + + //Eg: "/en/abp/latest" + public string GetUrlForProject(ProjectDto project, string language = "en", string version = null) + { + return "." + + _urlUiOptions.RoutePrefix.EnsureStartsWith('/').EnsureEndsWith('/') + + language.EnsureEndsWith('/') + + project.ShortName.EnsureEndsWith('/') + + (version ?? DocsAppConsts.Latest); + } + } +} From e08290b1c5ac4456c21c6cb6760427d50579a0a3 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 13:52:01 +0300 Subject: [PATCH 28/64] docs: add how to replace a layout section to component-replacement.md --- docs/en/UI/Angular/Component-Replacement.md | 53 +++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/docs/en/UI/Angular/Component-Replacement.md b/docs/en/UI/Angular/Component-Replacement.md index b17c61ae73..d718b46117 100644 --- a/docs/en/UI/Angular/Component-Replacement.md +++ b/docs/en/UI/Angular/Component-Replacement.md @@ -1,10 +1,10 @@ -# Component Replacement +## Component Replacement You can replace some ABP components with your custom components. The reason that you **can replace** but **cannot customize** default ABP components is disabling or changing a part of that component can cause problems. So we named those components as _Replaceable Components_. -## How to Replace a Component +### How to Replace a Component Create a new component that you want to use instead of an ABP component. Add that component to `declarations` and `entryComponents` in the `AppModule`. @@ -29,7 +29,54 @@ export class AppComponent { ![Example Usage](./images/component-replacement.gif) -## Available Replaceable Components + +### How to Replace a Layout + +Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced with the same way. + +> A layout component template should contain `` element. + +The below example describes how to replace the `ApplicationLayoutComponent`: + +Run the following command to generate a layout in `angular` folder: + +```bash +yarn ng generate component shared/my-application-layout --export --entryComponent + +# You don't need the --entryComponent option in Angular 9 +``` + +Add the following code in your layout template (`my-layout.component.html`) where you want the page to be loaded. + +```html + +``` + +Open the `app.component.ts` and add the below content: + +```js +import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent +import { MyApplicationLayoutComponent } from './shared/my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent +import { Store } from '@ngxs/store'; // imported Store +//... +export class AppComponent { + constructor(..., private store: Store) {} // injected Store + + ngOnInit() { + // added below content + this.store.dispatch( + new AddReplaceableComponent({ + component: MyApplicationLayoutComponent, + key: 'Theme.ApplicationLayoutComponent', + }), + ); + + //... + } +} +``` + +### Available Replaceable Components | Component key | Description | | -------------------------------------------------- | --------------------------------------------- | From 0ccce0bbf999b95ff224e8c8ca78046c937bf0c5 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 13:52:17 +0300 Subject: [PATCH 29/64] docs: fix the links --- docs/en/UI/Angular/Service-Proxies.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/UI/Angular/Service-Proxies.md b/docs/en/UI/Angular/Service-Proxies.md index 97b43b7513..6209350349 100644 --- a/docs/en/UI/Angular/Service-Proxies.md +++ b/docs/en/UI/Angular/Service-Proxies.md @@ -25,9 +25,9 @@ The files generated with the `--module all` option like below: ### Services -Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Http-Requests.md#restservice). +Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Http-Requests#restservice). -A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests.md#how-to-get-a-specific-api-endpoint-from-application-config) +A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config) The `providedIn` property of the services is defined as `'root'`. Therefore no need to add a service as a provider to a module. You can use a service by injecting it into a constructor as shown below: @@ -64,4 +64,4 @@ Initial values ​​can optionally be passed to each class constructor. ## What's Next? -* [HTTP Requests](./Http-Requests.md) +* [HTTP Requests](./Http-Requests) From 8d71b74281dd6fac4279970d14c47806c86e50ba Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Fri, 27 Mar 2020 14:31:09 +0300 Subject: [PATCH 30/64] style: add prettierrc and rules matching other angular projects --- templates/module/angular/.prettierrc | 5 +++ .../module/angular/src/app/app.module.ts | 42 +++++++++---------- 2 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 templates/module/angular/.prettierrc diff --git a/templates/module/angular/.prettierrc b/templates/module/angular/.prettierrc new file mode 100644 index 0000000000..5e2863a11f --- /dev/null +++ b/templates/module/angular/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 100, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/templates/module/angular/src/app/app.module.ts b/templates/module/angular/src/app/app.module.ts index 2705a3318b..93e09321ec 100644 --- a/templates/module/angular/src/app/app.module.ts +++ b/templates/module/angular/src/app/app.module.ts @@ -1,20 +1,20 @@ -import { AccountConfigModule } from "@abp/ng.account.config"; -import { CoreModule } from "@abp/ng.core"; -import { IdentityConfigModule } from "@abp/ng.identity.config"; -import { SettingManagementConfigModule } from "@abp/ng.setting-management.config"; -import { TenantManagementConfigModule } from "@abp/ng.tenant-management.config"; -import { ThemeSharedModule } from "@abp/ng.theme.shared"; -import { NgModule } from "@angular/core"; -import { BrowserModule } from "@angular/platform-browser"; -import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { NgxsLoggerPluginModule } from "@ngxs/logger-plugin"; -import { NgxsModule } from "@ngxs/store"; -import { OAuthModule } from "angular-oauth2-oidc"; -import { environment } from "../environments/environment"; -import { AppRoutingModule } from "./app-routing.module"; -import { AppComponent } from "./app.component"; -import { SharedModule } from "./shared/shared.module"; -import { MyProjectNameConfigModule } from "../../projects/my-project-name-config/src/public-api"; +import { AccountConfigModule } from '@abp/ng.account.config'; +import { CoreModule } from '@abp/ng.core'; +import { IdentityConfigModule } from '@abp/ng.identity.config'; +import { SettingManagementConfigModule } from '@abp/ng.setting-management.config'; +import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin'; +import { NgxsModule } from '@ngxs/store'; +import { OAuthModule } from 'angular-oauth2-oidc'; +import { MyProjectNameConfigModule } from '../../projects/my-project-name-config/src/public-api'; +import { environment } from '../environments/environment'; +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { SharedModule } from './shared/shared.module'; const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; @@ -23,11 +23,11 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; imports: [ ThemeSharedModule.forRoot(), CoreModule.forRoot({ - environment + environment, }), OAuthModule.forRoot(), NgxsModule.forRoot([]), - AccountConfigModule.forRoot({ redirectUrl: "/" }), + AccountConfigModule.forRoot({ redirectUrl: '/' }), IdentityConfigModule, TenantManagementConfigModule, SettingManagementConfigModule, @@ -37,8 +37,8 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })]; AppRoutingModule, SharedModule, - ...(environment.production ? [] : LOGGERS) + ...(environment.production ? [] : LOGGERS), ], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) export class AppModule {} From d72aada3b5bcfe05e9c33f60e1658d83c740bf9f Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Fri, 27 Mar 2020 15:02:18 +0300 Subject: [PATCH 31/64] refactor GithubRepositoryManager #3381 --- .../Documents/GithubRepositoryManager.cs | 68 +++++++++---------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs index 4627800832..754263989f 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs @@ -23,54 +23,30 @@ namespace Volo.Docs.GitHub.Documents public async Task GetFileRawStringContentAsync(string rawUrl, string token, string userAgent) { - var httpClient = _clientFactory.CreateClient(HttpClientName); - if (!token.IsNullOrWhiteSpace()) - { - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", token); - } - - if (!userAgent.IsNullOrWhiteSpace()) - { - httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent); - } + using var httpClient = CreateHttpClient(token, userAgent); return await httpClient.GetStringAsync(new Uri(rawUrl)); } public async Task GetFileRawByteArrayContentAsync(string rawUrl, string token, string userAgent) { - var httpClient = _clientFactory.CreateClient(HttpClientName); - if (!token.IsNullOrWhiteSpace()) - { - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", token); - } - - if (!userAgent.IsNullOrWhiteSpace()) - { - httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent); - } + using var httpClient = CreateHttpClient(token, userAgent); return await httpClient.GetByteArrayAsync(new Uri(rawUrl)); } - + public async Task> GetReleasesAsync(string name, string repositoryName, string token) { - var client = token.IsNullOrWhiteSpace() - ? new GitHubClient(new ProductHeaderValue(name)) - : new GitHubClient(new ProductHeaderValue(name), new InMemoryCredentialStore(new Credentials(token))); + var client = GetGitHubClient(name, token); + + var releases = await client.Repository.Release.GetAll(name, repositoryName); - return (await client - .Repository - .Release - .GetAll(name, repositoryName)).ToList(); + return releases.ToList(); } public async Task> GetFileCommitsAsync(string name, string repositoryName, string version, string filename, string token) { - var client = token.IsNullOrWhiteSpace() - ? new GitHubClient(new ProductHeaderValue(name)) - : new GitHubClient(new ProductHeaderValue(name), new InMemoryCredentialStore(new Credentials(token))); - + var client = GetGitHubClient(name, token); var repo = await client.Repository.Get(name, repositoryName); var request = new CommitRequest { Path = filename, Sha = version }; return await client.Repository.Commit.GetAll(repo.Id, request); @@ -78,12 +54,32 @@ namespace Volo.Docs.GitHub.Documents public async Task GetSingleCommitsAsync(string name, string repositoryName, string sha, string token) { - var client = token.IsNullOrWhiteSpace() - ? new GitHubClient(new ProductHeaderValue(name)) - : new GitHubClient(new ProductHeaderValue(name), new InMemoryCredentialStore(new Credentials(token))); - + var client = GetGitHubClient(name, token); var repo = await client.Repository.Get(name, repositoryName); return await client.Repository.Commit.Get(repo.Id, sha); } + + private HttpClient CreateHttpClient(string token, string userAgent) + { + var httpClient = _clientFactory.CreateClient(HttpClientName); + if (!token.IsNullOrWhiteSpace()) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", token); + } + + if (!userAgent.IsNullOrWhiteSpace()) + { + httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent); + } + + return httpClient; + } + + private static GitHubClient GetGitHubClient(string name, string token) + { + return token.IsNullOrWhiteSpace() + ? new GitHubClient(new ProductHeaderValue(name)) + : new GitHubClient(new ProductHeaderValue(name), new InMemoryCredentialStore(new Credentials(token))); + } } } From bb226783c5bda77bb08af71e65de49f73b77f466 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Fri, 27 Mar 2020 15:03:22 +0300 Subject: [PATCH 32/64] #3381 --- .../Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs index 754263989f..ef21353236 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs @@ -24,24 +24,19 @@ namespace Volo.Docs.GitHub.Documents public async Task GetFileRawStringContentAsync(string rawUrl, string token, string userAgent) { using var httpClient = CreateHttpClient(token, userAgent); - return await httpClient.GetStringAsync(new Uri(rawUrl)); } public async Task GetFileRawByteArrayContentAsync(string rawUrl, string token, string userAgent) { using var httpClient = CreateHttpClient(token, userAgent); - return await httpClient.GetByteArrayAsync(new Uri(rawUrl)); } public async Task> GetReleasesAsync(string name, string repositoryName, string token) { var client = GetGitHubClient(name, token); - - var releases = await client.Repository.Release.GetAll(name, repositoryName); - - return releases.ToList(); + return await client.Repository.Release.GetAll(name, repositoryName); } public async Task> GetFileCommitsAsync(string name, string repositoryName, string version, string filename, string token) From 2f7e69e1a27f2dc1732e4a55ef98d752b6f86f20 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 15:06:07 +0300 Subject: [PATCH 33/64] refactor: reduce codes and increase readability --- .../components/dynamic-layout.component.ts | 48 +++++++------------ 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index fc3926a1ea..e835dd777d 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -1,15 +1,13 @@ -import { Component, Input, OnDestroy, Type, Injector } from '@angular/core'; +import { Component, OnDestroy, Type } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router, UrlSegment } from '@angular/router'; -import { Select, Store } from '@ngxs/store'; -import { Observable, combineLatest } from 'rxjs'; +import { Store } from '@ngxs/store'; import snq from 'snq'; import { eLayoutType } from '../enums/common'; -import { Config } from '../models/config'; import { ABP } from '../models/common'; +import { ReplaceableComponents } from '../models/replaceable-components'; import { ConfigState } from '../states/config.state'; -import { takeUntilDestroy } from '../utils/rxjs-utils'; import { ReplaceableComponentsState } from '../states/replaceable-components.state'; -import { ReplaceableComponents } from '../models/replaceable-components'; +import { takeUntilDestroy } from '../utils/rxjs-utils'; @Component({ selector: 'abp-dynamic-layout', @@ -22,23 +20,9 @@ import { ReplaceableComponents } from '../models/replaceable-components'; `, }) export class DynamicLayoutComponent implements OnDestroy { - @Select(ReplaceableComponentsState.getComponent('Theme.ApplicationLayoutComponent')) - applicationLayout$: Observable; - - @Select(ReplaceableComponentsState.getComponent('Theme.AccountLayoutComponent')) - accountLayout$: Observable; - - @Select(ReplaceableComponentsState.getComponent('Theme.EmptyLayoutComponent')) - emptyLayout$: Observable; - layout: Type; - layouts = {} as { [key in eLayoutType]: Type }; - - expectedLayout: eLayoutType = eLayoutType.empty; - constructor(private router: Router, private route: ActivatedRoute, private store: Store) { - this.listenToLayouts(); const { routes } = this.store.selectSnapshot(ConfigState.getAll); router.events.pipe(takeUntilDestroy(this)).subscribe(event => { @@ -47,25 +31,25 @@ export class DynamicLayoutComponent implements OnDestroy { { path: router.url.replace('/', '') }, ] as any); - this.expectedLayout = + const layouts = { + application: this.getComponent('Theme.ApplicationLayoutComponent'), + account: this.getComponent('Theme.AccountLayoutComponent'), + empty: this.getComponent('Theme.EmptyApplicationLayoutComponent'), + }; + + const expectedLayout = (this.route.snapshot.data || {}).layout || findLayout(segments, routes); - this.layout = this.layouts[this.expectedLayout]; + this.layout = layouts[expectedLayout].component; } }); } - ngOnDestroy() {} - - listenToLayouts() { - combineLatest(this.applicationLayout$, this.accountLayout$, this.emptyLayout$) - .pipe(takeUntilDestroy(this)) - .subscribe(([application, account, empty]) => { - this.layouts.application = application.component; - this.layouts.account = account.component; - this.layouts.empty = empty.component; - }); + getComponent(key: string): ReplaceableComponents.ReplaceableComponent { + return this.store.selectSnapshot(ReplaceableComponentsState.getComponent(key)); } + + ngOnDestroy() {} } function findLayout(segments: UrlSegment[], routes: ABP.FullRoute[]): eLayoutType { From 91668b35f423dcca63be06f972e23a8a9e6bfb22 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 15:14:00 +0300 Subject: [PATCH 34/64] =?UTF-8?q?refactor(core):=20make=20getComponent=20m?= =?UTF-8?q?ethod=20in=20DynamicCompo=C4=B1nent=20private?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/src/lib/components/dynamic-layout.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index e835dd777d..71dd0f3fdf 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -45,7 +45,7 @@ export class DynamicLayoutComponent implements OnDestroy { }); } - getComponent(key: string): ReplaceableComponents.ReplaceableComponent { + private getComponent(key: string): ReplaceableComponents.ReplaceableComponent { return this.store.selectSnapshot(ReplaceableComponentsState.getComponent(key)); } From 1f2418b8721e0d991f2289bc4c57ca666de14c60 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Fri, 27 Mar 2020 15:48:37 +0300 Subject: [PATCH 35/64] fix(core): fix component key in dynamic layout --- .../core/src/lib/components/dynamic-layout.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts index 71dd0f3fdf..c07f11580f 100644 --- a/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts +++ b/npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts @@ -34,7 +34,7 @@ export class DynamicLayoutComponent implements OnDestroy { const layouts = { application: this.getComponent('Theme.ApplicationLayoutComponent'), account: this.getComponent('Theme.AccountLayoutComponent'), - empty: this.getComponent('Theme.EmptyApplicationLayoutComponent'), + empty: this.getComponent('Theme.EmptyLayoutComponent'), }; const expectedLayout = From 6239fcd585958de01da89725d3d3cbc9b244ef5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arma=C4=9Fan=20=C3=9Cnl=C3=BC?= <36102404+armgnunlu@users.noreply.github.com> Date: Fri, 27 Mar 2020 17:40:04 +0300 Subject: [PATCH 36/64] Resolved #3238 --- .../Pages/Documents/Project/Index.cshtml | 31 +- .../Pages/Documents/Search.cshtml | 88 +++-- .../Pages/Documents/Shared/Styles/vs.css | 240 ++++++------ .../Pages/Documents/Shared/Styles/vs.min.css | 2 +- .../Pages/Documents/Shared/Styles/vs.scss | 349 ++++++++++-------- 5 files changed, 384 insertions(+), 326 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml index 67e5352e22..6fcf402d70 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml @@ -68,7 +68,7 @@ }
-