@colijnit/configurator 1.0.20 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/.idea/Configurator.iml +12 -0
  2. package/.idea/codeStyles/Project.xml +21 -0
  3. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  4. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  5. package/.idea/misc.xml +9 -0
  6. package/.idea/modules.xml +8 -0
  7. package/.idea/vcs.xml +6 -0
  8. package/angular.json +193 -0
  9. package/configurator.iml +11 -0
  10. package/dist/configurator/index.html +14 -0
  11. package/dist/configurator/main-es2015.js +3129 -0
  12. package/dist/configurator/main-es2015.js.map +1 -0
  13. package/dist/configurator/main-es5.js +5482 -0
  14. package/dist/configurator/main-es5.js.map +1 -0
  15. package/dist/configurator/polyfills-es2015.js +4520 -0
  16. package/dist/configurator/polyfills-es2015.js.map +1 -0
  17. package/dist/configurator/polyfills-es5.js +18375 -0
  18. package/dist/configurator/polyfills-es5.js.map +1 -0
  19. package/dist/configurator/runtime-es2015.js +155 -0
  20. package/dist/configurator/runtime-es2015.js.map +1 -0
  21. package/dist/configurator/runtime-es5.js +155 -0
  22. package/dist/configurator/runtime-es5.js.map +1 -0
  23. package/dist/configurator/styles-es2015.js +450 -0
  24. package/dist/configurator/styles-es2015.js.map +1 -0
  25. package/dist/configurator/styles-es5.js +432 -0
  26. package/dist/configurator/styles-es5.js.map +1 -0
  27. package/dist/configurator/vendor-es2015.js +155551 -0
  28. package/dist/configurator/vendor-es2015.js.map +1 -0
  29. package/dist/configurator/vendor-es5.js +183588 -0
  30. package/dist/configurator/vendor-es5.js.map +1 -0
  31. package/ng-package.json +9 -0
  32. package/package.json +47 -14
  33. package/src/app/app.component.ts +222 -0
  34. package/src/app/app.module.ts +34 -0
  35. package/src/app/builder.ts +480 -0
  36. package/src/app/components/answers/answer/answer.component.ts +61 -0
  37. package/src/app/components/answers/answers.component.ts +41 -0
  38. package/src/app/components/answers/answers.module.ts +26 -0
  39. package/src/app/components/selections/selections.component.ts +131 -0
  40. package/src/app/components/selections/selections.module.ts +20 -0
  41. package/src/app/components/shared/loader/loader.component.scss +33 -0
  42. package/src/app/components/shared/loader/loader.component.ts +20 -0
  43. package/src/app/components/shared/shared.module.ts +16 -0
  44. package/src/app/directives/visibility-observer-master.directive.ts +71 -0
  45. package/src/app/directives/visibility-observer.directive.ts +74 -0
  46. package/src/app/services/configurator.service.ts +86 -0
  47. package/src/app/services/image-cache.service.ts +56 -0
  48. package/src/app/services/locator.service.ts +6 -0
  49. package/src/environments/environment.prod.ts +3 -0
  50. package/src/environments/environment.ts +8 -0
  51. package/src/helper/variation-helper.ts +220 -0
  52. package/src/index.html +14 -0
  53. package/src/main.ts +11 -0
  54. package/src/model/material.ts +22 -0
  55. package/src/model/variation-settings.ts +14 -0
  56. package/src/model/variation.ts +11 -0
  57. package/src/polyfills.ts +73 -0
  58. package/{public_api.d.ts → src/public_api.ts} +6 -6
  59. package/src/style/shared.scss +173 -0
  60. package/src/style/styles.scss +45 -0
  61. package/src/tsconfig.app.json +16 -0
  62. package/src/tsconfig.spec.json +19 -0
  63. package/src/utils/asset.utils.ts +88 -0
  64. package/src/utils/file.utils.ts +156 -0
  65. package/src/utils/file.utils.unit.test.ts +8 -0
  66. package/src/utils/image.utils.ts +54 -0
  67. package/src/utils/object.utils.ts +52 -0
  68. package/src/utils/scene-utils.ts +119 -0
  69. package/src/utils/threed.utils.ts +219 -0
  70. package/src/utils/variation-utils.ts +216 -0
  71. package/tsconfig.json +23 -0
  72. package/tslint.json +132 -0
  73. package/app/builder.d.ts +0 -53
  74. package/app/components/answers/answer/answer.component.d.ts +0 -11
  75. package/app/components/answers/answers.component.d.ts +0 -8
  76. package/app/components/answers/answers.module.d.ts +0 -2
  77. package/app/components/selections/selections.component.d.ts +0 -22
  78. package/app/components/selections/selections.module.d.ts +0 -2
  79. package/app/components/shared/loader/loader.component.d.ts +0 -2
  80. package/app/components/shared/shared.module.d.ts +0 -2
  81. package/app/directives/visibility-observer-master.directive.d.ts +0 -9
  82. package/app/directives/visibility-observer.directive.d.ts +0 -13
  83. package/app/services/configurator.service.d.ts +0 -22
  84. package/app/services/image-cache.service.d.ts +0 -10
  85. package/app/services/locator.service.d.ts +0 -4
  86. package/bundles/colijnit-configurator.umd.js +0 -2745
  87. package/bundles/colijnit-configurator.umd.js.map +0 -1
  88. package/bundles/colijnit-configurator.umd.min.js +0 -17
  89. package/bundles/colijnit-configurator.umd.min.js.map +0 -1
  90. package/colijnit-configurator.d.ts +0 -10
  91. package/colijnit-configurator.metadata.json +0 -1
  92. package/esm2015/app/builder.js +0 -477
  93. package/esm2015/app/components/answers/answer/answer.component.js +0 -69
  94. package/esm2015/app/components/answers/answers.component.js +0 -43
  95. package/esm2015/app/components/answers/answers.module.js +0 -29
  96. package/esm2015/app/components/selections/selections.component.js +0 -134
  97. package/esm2015/app/components/selections/selections.module.js +0 -23
  98. package/esm2015/app/components/shared/loader/loader.component.js +0 -24
  99. package/esm2015/app/components/shared/shared.module.js +0 -21
  100. package/esm2015/app/directives/visibility-observer-master.directive.js +0 -51
  101. package/esm2015/app/directives/visibility-observer.directive.js +0 -57
  102. package/esm2015/app/services/configurator.service.js +0 -94
  103. package/esm2015/app/services/image-cache.service.js +0 -66
  104. package/esm2015/app/services/locator.service.js +0 -10
  105. package/esm2015/colijnit-configurator.js +0 -11
  106. package/esm2015/helper/variation-helper.js +0 -216
  107. package/esm2015/model/material.js +0 -11
  108. package/esm2015/model/variation-settings.js +0 -6
  109. package/esm2015/model/variation.js +0 -3
  110. package/esm2015/public_api.js +0 -7
  111. package/esm2015/utils/asset.utils.js +0 -74
  112. package/esm2015/utils/file.utils.js +0 -139
  113. package/esm2015/utils/image.utils.js +0 -52
  114. package/esm2015/utils/object.utils.js +0 -49
  115. package/esm2015/utils/scene-utils.js +0 -94
  116. package/esm2015/utils/threed.utils.js +0 -222
  117. package/esm2015/utils/variation-utils.js +0 -224
  118. package/esm5/app/builder.js +0 -591
  119. package/esm5/app/components/answers/answer/answer.component.js +0 -64
  120. package/esm5/app/components/answers/answers.component.js +0 -27
  121. package/esm5/app/components/answers/answers.module.js +0 -32
  122. package/esm5/app/components/selections/selections.component.js +0 -104
  123. package/esm5/app/components/selections/selections.module.js +0 -26
  124. package/esm5/app/components/shared/loader/loader.component.js +0 -16
  125. package/esm5/app/components/shared/shared.module.js +0 -24
  126. package/esm5/app/directives/visibility-observer-master.directive.js +0 -64
  127. package/esm5/app/directives/visibility-observer.directive.js +0 -59
  128. package/esm5/app/services/configurator.service.js +0 -160
  129. package/esm5/app/services/image-cache.service.js +0 -69
  130. package/esm5/app/services/locator.service.js +0 -13
  131. package/esm5/colijnit-configurator.js +0 -11
  132. package/esm5/helper/variation-helper.js +0 -268
  133. package/esm5/model/material.js +0 -13
  134. package/esm5/model/variation-settings.js +0 -8
  135. package/esm5/model/variation.js +0 -7
  136. package/esm5/public_api.js +0 -7
  137. package/esm5/utils/asset.utils.js +0 -106
  138. package/esm5/utils/file.utils.js +0 -151
  139. package/esm5/utils/image.utils.js +0 -56
  140. package/esm5/utils/object.utils.js +0 -56
  141. package/esm5/utils/scene-utils.js +0 -98
  142. package/esm5/utils/threed.utils.js +0 -279
  143. package/esm5/utils/variation-utils.js +0 -327
  144. package/fesm2015/colijnit-configurator.js +0 -2109
  145. package/fesm2015/colijnit-configurator.js.map +0 -1
  146. package/fesm5/colijnit-configurator.js +0 -2527
  147. package/fesm5/colijnit-configurator.js.map +0 -1
  148. package/helper/variation-helper.d.ts +0 -14
  149. package/model/material.d.ts +0 -17
  150. package/model/variation-settings.d.ts +0 -14
  151. package/model/variation.d.ts +0 -10
  152. package/utils/asset.utils.d.ts +0 -13
  153. package/utils/file.utils.d.ts +0 -27
  154. package/utils/image.utils.d.ts +0 -8
  155. package/utils/object.utils.d.ts +0 -7
  156. package/utils/scene-utils.d.ts +0 -7
  157. package/utils/threed.utils.d.ts +0 -16
  158. package/utils/variation-utils.d.ts +0 -12
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "./node_modules/ng-packagr/ng-package.schema.json",
3
+ "lib": {
4
+ "entryFile": "src/public_api.ts"
5
+ },
6
+ "whitelistedNonPeerDependencies": [
7
+ "."
8
+ ]
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colijnit/configurator",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
4
4
  "description": "iOne configurator",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -9,6 +9,26 @@
9
9
  "npm": "^7.*"
10
10
  },
11
11
  "repository": "npm/npm",
12
+ "scripts": {
13
+ "build": "ng build",
14
+ "ng": "ng",
15
+ "start": "ng serve --port=4600",
16
+ "package": "npm run build && ng-packagr -p ng-package.json",
17
+ "postPackage": "node-sass src/style -o dist/css"
18
+ },
19
+ "jest": {
20
+ "testEnvironment": "node",
21
+ "verbose": true,
22
+ "roots": [
23
+ "<rootDir>/src"
24
+ ],
25
+ "transform": {
26
+ "^.+\\.tsx?$": "ts-jest"
27
+ },
28
+ "transformIgnorePatterns": [
29
+ "<rootDir>/node_modules/jsencrypt"
30
+ ]
31
+ },
12
32
  "dependencies": {
13
33
  "@angular/animations": "^9.1.13",
14
34
  "@angular/cdk": "^9.2.4",
@@ -20,8 +40,8 @@
20
40
  "@angular/forms": "^9.1.13",
21
41
  "@angular/platform-browser": "~9.1.13",
22
42
  "@angular/platform-browser-dynamic": "~9.1.13",
23
- "@colijnit/configuratorapi": "1.0.19",
24
- "@colijnit/ioneconnector": "3.1.23",
43
+ "@colijnit/configuratorapi": "1.0.20",
44
+ "@colijnit/ioneconnector": "3.1.24",
25
45
  "axios": "0.21.1",
26
46
  "core-js": "^2.5.4",
27
47
  "jest": "26.6.3",
@@ -31,17 +51,30 @@
31
51
  "tslib": "^1.13.0",
32
52
  "zone.js": "~0.10.2"
33
53
  },
54
+ "devDependencies": {
55
+ "@angular-builders/custom-webpack": "^9.2.0",
56
+ "@angular-devkit/build-angular": "~0.901.15",
57
+ "@angular-devkit/build-ng-packagr": "~0.901.15",
58
+ "@angular/compiler-cli": "^9.1.13",
59
+ "@angularclass/hmr": "^2.1.3",
60
+ "@types/jasmine": "^2.8.8",
61
+ "@types/jasminewd2": "~2.0.2",
62
+ "@types/jest": "^24.0.12",
63
+ "@types/node": "^12.11.1",
64
+ "@types/webgl2": "0.0.5",
65
+ "jasmine-core": "^3.4.0",
66
+ "jasmine-spec-reporter": "~4.2.1",
67
+ "jest": "^24.8.0",
68
+ "ng-packagr": "^9.0.0",
69
+ "three": "0.125.2",
70
+ "ts-jest": "^24.0.2",
71
+ "ts-node-dev": "^1.0.0-pre.31",
72
+ "tslint": "~5.9.1",
73
+ "tslint-consistent-codestyle": "^1.16.0",
74
+ "typescript": "~3.8.3",
75
+ "webpack-cli": "^3.3.1"
76
+ },
34
77
  "peerDependencies": {
35
78
  "three": ">= 0.125.x"
36
- },
37
- "main": "bundles/colijnit-configurator.umd.js",
38
- "module": "fesm5/colijnit-configurator.js",
39
- "es2015": "fesm2015/colijnit-configurator.js",
40
- "esm5": "esm5/colijnit-configurator.js",
41
- "esm2015": "esm2015/colijnit-configurator.js",
42
- "fesm5": "fesm5/colijnit-configurator.js",
43
- "fesm2015": "fesm2015/colijnit-configurator.js",
44
- "typings": "colijnit-configurator.d.ts",
45
- "metadata": "colijnit-configurator.metadata.json",
46
- "sideEffects": false
79
+ }
47
80
  }
@@ -0,0 +1,222 @@
1
+ import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from "@angular/core";
2
+ import * as THREE from "three";
3
+ import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
4
+ import {Builder} from "./builder";
5
+ import {Options} from "@colijnit/ioneconnector/build/model/options";
6
+ import axios from "axios";
7
+ import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
8
+ import {Subscription} from "rxjs";
9
+ import {Selection} from "@colijnit/configuratorapi/build/model/selection";
10
+ import {Answer} from "@colijnit/configuratorapi/build/model/answer";
11
+ import {ConfiguratorService} from './services/configurator.service';
12
+ import {AmbientLight, DirectionalLight, HemisphereLight, Object3D, SpotLight} from 'three';
13
+
14
+ // @ts-ignore
15
+ window.loadGLTF = async (file: string) => {
16
+ const gltfLoader: GLTFLoader = new GLTFLoader();
17
+ try {
18
+ return new Promise((resolve: Function) => {
19
+ gltfLoader.load(file, (obj) => {
20
+ resolve(obj.scene);
21
+ });
22
+ });
23
+ } catch (e) {
24
+ console.error(e);
25
+ return null;
26
+ }
27
+ };
28
+ // @ts-ignore
29
+ window.downloadVariation = async (fileName: string) => {
30
+ const response: any = await axios({
31
+ method: "get",
32
+ url: `https://cdn1.colijn-it.nl/${fileName}`,
33
+ responseType: "arraybuffer"
34
+ });
35
+ if (response.status !== 200) {
36
+ throw Error(response.statusText);
37
+ }
38
+ return await response.data;
39
+ };
40
+
41
+ @Component({
42
+ selector: 'app-root',
43
+ template: `
44
+ <div #canvas class="canvas"></div>
45
+ <selections *ngIf="!showAnswers" [selections]="selections" (selectionClick)="selectSelection($event)"></selections>
46
+ <answers *ngIf="showAnswers" [selectionTitle]="selectionTitle" [answers]="answers" (answerClick)="selectAnswer($event)"></answers>
47
+ `
48
+ })
49
+ export class AppComponent implements AfterViewInit, OnDestroy {
50
+
51
+ @ViewChild('canvas', {read: ElementRef})
52
+ public canvasElement: ElementRef;
53
+
54
+ public selectionTitle: string;
55
+ public selections: Selection[] = [];
56
+ public answers: Answer[] = [];
57
+
58
+ public showAnswers: boolean = false;
59
+ public showSelections: boolean = true;
60
+
61
+ private _builder: Builder;
62
+ private _object: THREE.Object3D;
63
+ private scene: THREE.Scene = new THREE.Scene();
64
+ private renderer: THREE.WebGLRenderer;
65
+ private camera: THREE.PerspectiveCamera;
66
+ private controls: OrbitControls;
67
+ private _subs: Subscription[] = [];
68
+ private _options: Options = {
69
+ url: "http://130.62.7.180:8082/ione",
70
+ version: "251",
71
+ schema: "815"
72
+ };
73
+ private _instanceId: string = undefined;
74
+ private _sku: string = '1000234793';
75
+
76
+ constructor(private _configuratorService: ConfiguratorService) {}
77
+
78
+ ngAfterViewInit(): void {
79
+ this._builder = new Builder(this.scene, this._options, true);
80
+ this._subs.push(
81
+ this._builder.selectionsReceived.subscribe((selections: Selection[]) => {
82
+ this.selections = selections;
83
+ }),
84
+ this._builder.answersReceived.subscribe((answers: Answer[]) => {
85
+ this.answers = answers;
86
+ this.showAnswers = this.answers.length > 0;
87
+ }),
88
+ this._builder.modelLoaded.subscribe((object: Object3D) => {
89
+ if (object && this.answers.length === 0) {
90
+ this._loadModel(object);
91
+ }
92
+ })
93
+ );
94
+ this._initScene();
95
+ this._initRenderer();
96
+ this._initCameraAndControls();
97
+ this._initLights();
98
+ // this._test();
99
+ this._animate();
100
+ this._builder.buildModel(this._sku, this._instanceId);
101
+ }
102
+
103
+ ngOnDestroy(): void {
104
+ this._subs.forEach(s => s.unsubscribe());
105
+ }
106
+
107
+ public disableControls(): void {
108
+ this.controls.enabled = false;
109
+ }
110
+
111
+ public enableControls(): void {
112
+ this.controls.enabled = true;
113
+ }
114
+
115
+ public selectSelection(selection: Selection): void {
116
+ this._builder.selectSelection(selection);
117
+ this.selectionTitle = selection.commercialQuestion;
118
+ this.showSelections = false;
119
+ }
120
+
121
+ public selectAnswer(answer: Answer): void {
122
+ this._builder.selectAnswer(answer);
123
+ this.selectionTitle = ' . . / ' + answer.answer;
124
+ }
125
+
126
+ private _initScene(): void {
127
+ this.scene = new THREE.Scene();
128
+ }
129
+
130
+ private _initRenderer(): void {
131
+ this.renderer = new THREE.WebGLRenderer({
132
+ alpha: true,
133
+ antialias: true,
134
+ preserveDrawingBuffer: true
135
+ });
136
+ this.renderer.setClearColor(0xfafafa);
137
+ this.renderer.autoClear = true;
138
+ this.renderer.setPixelRatio(window.devicePixelRatio);
139
+ this.renderer.shadowMap.enabled = true;
140
+ // Use PCFShadowMap to enable radius else use PCFSoftShadowMap
141
+ this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
142
+ this.renderer.outputEncoding = THREE.LinearEncoding;
143
+ // this.renderer.gammaFactor = 2.2;
144
+ // this.renderer.toneMappingExposure = 1.1;
145
+ this.canvasElement.nativeElement.appendChild(this.renderer.domElement);
146
+ }
147
+
148
+ private _initCameraAndControls(): void {
149
+ this.camera = new THREE.PerspectiveCamera(45, 1, 0.01, 100);
150
+ this.controls = new OrbitControls( this.camera, this.renderer.domElement );
151
+ this.camera.position.set(0, 3, 1.5);
152
+ this.controls.update();
153
+ }
154
+
155
+ private _animate(): void {
156
+ this.renderer.setAnimationLoop(() => this._render());
157
+ }
158
+
159
+ private _render(): void {
160
+ this._checkCanvasResize();
161
+ this.renderer.render(this.scene, this.camera);
162
+ }
163
+
164
+ private _checkCanvasResize(): void {
165
+ const canvas = this.renderer.domElement;
166
+ if (this.canvasElement.nativeElement.clientWidth !== canvas.width || this.canvasElement.nativeElement.clientHeight !== canvas.height) {
167
+ this._updateWindowSize();
168
+ }
169
+ }
170
+
171
+ private _updateWindowSize() {
172
+ this._resizeRendererToDisplaySize();
173
+ const canvas = this.renderer.domElement;
174
+ const aspect = canvas.width / canvas.height;
175
+ this.camera.aspect = aspect;
176
+ (this.camera as THREE.PerspectiveCamera).updateProjectionMatrix();
177
+ }
178
+
179
+ private _resizeRendererToDisplaySize() {
180
+ this.renderer.setSize(this.canvasElement.nativeElement.clientWidth, this.canvasElement.nativeElement.clientHeight, false);
181
+ }
182
+
183
+ private _test(): void {
184
+ const mat: THREE.MeshBasicMaterial = new THREE.MeshBasicMaterial({color: 'blue'});
185
+ const geo: THREE.BoxGeometry = new THREE.BoxGeometry(1, 1, 1);
186
+ const mesh: THREE.Mesh = new THREE.Mesh(geo, mat);
187
+ this.scene.add(mesh);
188
+ }
189
+
190
+ private async _loadModel(object: Object3D): Promise<void> {
191
+ object.name = this._sku;
192
+ object.visible = true;
193
+ const objectFromScene = this.scene.getObjectByName(this._sku);
194
+ if (objectFromScene) {
195
+ this.scene.remove(objectFromScene);
196
+ }
197
+ this.scene.add(object);
198
+ }
199
+
200
+ private _initLights(): void {
201
+ const ambientLight: AmbientLight = new AmbientLight('#595959', 1.2);
202
+ ambientLight.name = 'ambientLight';
203
+ const directional: DirectionalLight = new DirectionalLight('white', 1);
204
+ directional.position.set(0, 4, 0);
205
+ directional.castShadow = true;
206
+ const spotLight1 = new SpotLight('white', 4, 25, 0.7, 0, 0.7);
207
+ spotLight1.position.set(-15, 9 , -15);
208
+ spotLight1.castShadow = true;
209
+ const spotLight2 = new SpotLight('white', 4, 25, 0.7, 0, 0.7);
210
+ spotLight2.position.set(15, 9 , -15);
211
+ spotLight2.castShadow = true;
212
+ const spotLight3 = new SpotLight('white', 4, 25, 0.7, 0, 0.7);
213
+ spotLight3.position.set(-15, 9 , 15);
214
+ spotLight3.castShadow = true;
215
+ const spotLight4 = new SpotLight('white', 4, 25, 0.7, 0, 0.7);
216
+ spotLight4.position.set(15, 9 , 15);
217
+ spotLight4.castShadow = true;
218
+ const hemiSphere = new HemisphereLight('white', 'white', 0.15);
219
+
220
+ this.scene.add(ambientLight, directional, spotLight1, spotLight2, spotLight3, spotLight4, hemiSphere);
221
+ }
222
+ }
@@ -0,0 +1,34 @@
1
+ import {NgModule} from '@angular/core';
2
+ import {AppComponent} from "./app.component";
3
+ import {CommonModule} from "@angular/common";
4
+ import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
5
+ import {SelectionsModule} from "./components/selections/selections.module";
6
+ import {AnswersModule} from "./components/answers/answers.module";
7
+ import {Builder} from './builder';
8
+
9
+ @NgModule({
10
+ imports: [
11
+ BrowserAnimationsModule,
12
+ CommonModule,
13
+ SelectionsModule,
14
+ AnswersModule
15
+ ],
16
+ providers: [
17
+ Builder
18
+ ],
19
+ declarations: [
20
+ AppComponent
21
+ ],
22
+ entryComponents: [
23
+ AppComponent
24
+ ],
25
+ exports: [
26
+ AppComponent
27
+ ],
28
+ bootstrap: [
29
+ AppComponent
30
+ ]
31
+ })
32
+
33
+ export class AppModule {
34
+ }