proximiio-js-library 1.11.39 → 1.11.41

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.
@@ -164,6 +164,7 @@ export declare class Map {
164
164
  private clusterSource;
165
165
  private imageSourceManager;
166
166
  private onMapReadyListener;
167
+ private onMainSourceLoadedListener;
167
168
  private onMapFailedListener;
168
169
  private onPlaceSelectListener;
169
170
  private onFloorSelectListener;
@@ -193,6 +194,7 @@ export declare class Map {
193
194
  private selectedPolygon;
194
195
  private currentStep;
195
196
  private kioskPopup;
197
+ private mainSourceLoaded;
196
198
  constructor(options: Options);
197
199
  private initialize;
198
200
  private cancelObservers;
@@ -250,6 +252,7 @@ export declare class Map {
250
252
  private onPlaceSelect;
251
253
  private onFloorSelect;
252
254
  private onRouteUpdate;
255
+ private onRoutePreview;
253
256
  private onRouteCancel;
254
257
  private centerOnPoi;
255
258
  private centerOnRoute;
@@ -296,6 +299,17 @@ export declare class Map {
296
299
  * });
297
300
  */
298
301
  getMapReadyListener(): CustomSubject<boolean>;
302
+ /**
303
+ * @memberof Map
304
+ * @name getMainSourceLoadedListener
305
+ * @returns returns main source fully loaded listener
306
+ * @example
307
+ * const map = new Proximiio.Map();
308
+ * map.getMainSourceLoadedListener().subscribe(loaded => {
309
+ * console.log('map loaded', loaded);
310
+ * });
311
+ */
312
+ getMainSourceLoadedListener(): CustomSubject<boolean>;
299
313
  /**
300
314
  * @memberof Map
301
315
  * @name getMapFailedListener
@@ -408,6 +422,7 @@ export declare class Map {
408
422
  * @param idFrom {string} start feature id, optional for kiosk
409
423
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
410
424
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
425
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
411
426
  * @example
412
427
  * const map = new Proximiio.Map();
413
428
  * map.getMapReadyListener().subscribe(ready => {
@@ -415,7 +430,7 @@ export declare class Map {
415
430
  * map.findRouteByIds('finishId, 'startId');
416
431
  * });
417
432
  */
418
- findRouteByIds(idTo: string, idFrom?: string, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel): void;
433
+ findRouteByIds(idTo: string, idFrom?: string, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel, addToMap?: boolean): void;
419
434
  /**
420
435
  * This method will generate route based on selected features by their titles
421
436
  * @memberof Map
@@ -424,6 +439,7 @@ export declare class Map {
424
439
  * @param titleFrom {string} start feature title, optional for kiosk
425
440
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
426
441
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
442
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
427
443
  * @example
428
444
  * const map = new Proximiio.Map();
429
445
  * map.getMapReadyListener().subscribe(ready => {
@@ -431,7 +447,7 @@ export declare class Map {
431
447
  * map.findRouteByTitle('myFeatureTitle', 'anotherFeatureTitle');
432
448
  * });
433
449
  */
434
- findRouteByTitle(titleTo: string, titleFrom?: string, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel): void;
450
+ findRouteByTitle(titleTo: string, titleFrom?: string, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel, addToMap?: boolean): void;
435
451
  /**
436
452
  * This method will generate route based on selected features by their titles
437
453
  * @memberof Map
@@ -444,6 +460,7 @@ export declare class Map {
444
460
  * @param levelFrom {number} start level, optional for kiosk
445
461
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
446
462
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
463
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
447
464
  * @example
448
465
  * const map = new Proximiio.Map();
449
466
  * map.getMapReadyListener().subscribe(ready => {
@@ -451,7 +468,7 @@ export declare class Map {
451
468
  * map.findRouteByCoords(48.606703739771774, 17.833092384506614, 0, 48.60684545080579, 17.833450676669543, 0);
452
469
  * });
453
470
  */
454
- findRouteByCoords(latTo: number, lngTo: number, levelTo: number, latFrom?: number, lngFrom?: number, levelFrom?: number, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel): void;
471
+ findRouteByCoords(latTo: number, lngTo: number, levelTo: number, latFrom?: number, lngFrom?: number, levelFrom?: number, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel, addToMap?: boolean): void;
455
472
  /**
456
473
  * This method will generate route to nearest amenity feature
457
474
  * @memberof Map
@@ -460,6 +477,7 @@ export declare class Map {
460
477
  * @param idFrom {string} start feature id, optional for kiosk
461
478
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
462
479
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
480
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
463
481
  * @example
464
482
  * const map = new Proximiio.Map();
465
483
  * map.getMapReadyListener().subscribe(ready => {
@@ -467,7 +485,7 @@ export declare class Map {
467
485
  * map.findRouteToNearestFeature('amenityId');
468
486
  * });
469
487
  */
470
- findRouteToNearestFeature(amenityId: string, idFrom?: string, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel): void;
488
+ findRouteToNearestFeature(amenityId: string, idFrom?: string, accessibleRoute?: boolean, wayfindingConfig?: WayfindingConfigModel, addToMap?: boolean): void;
471
489
  /**
472
490
  * This method will cancel generated route
473
491
  * @memberof Map
@@ -71,6 +71,7 @@ export class Map {
71
71
  this.clusterSource = new ClusterSource();
72
72
  this.imageSourceManager = new ImageSourceManager();
73
73
  this.onMapReadyListener = new CustomSubject();
74
+ this.onMainSourceLoadedListener = new CustomSubject();
74
75
  this.onMapFailedListener = new CustomSubject();
75
76
  this.onPlaceSelectListener = new CustomSubject();
76
77
  this.onFloorSelectListener = new CustomSubject();
@@ -203,6 +204,7 @@ export class Map {
203
204
  this.hiddenAmenities = [];
204
205
  this.amenityCategories = {};
205
206
  this.currentStep = 0;
207
+ this.mainSourceLoaded = false;
206
208
  this.step = 0;
207
209
  this.animateRoute = () => {
208
210
  var _a;
@@ -406,12 +408,7 @@ export class Map {
406
408
  });
407
409
  }
408
410
  // @ts-ignore
409
- if (maplibregl.getRTLTextPluginStatus() !== 'loaded' &&
410
- maplibregl.getRTLTextPluginStatus() !== 'unavailable' &&
411
- maplibregl.getRTLTextPluginStatus() !== 'deferred') {
412
- // @ts-ignore
413
- maplibregl.setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', null, true);
414
- }
411
+ maplibregl.setRTLTextPlugin('https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js', null, true);
415
412
  this.map = new maplibregl.Map(Object.assign(Object.assign({}, this.defaultOptions.mapboxOptions), { container: this.defaultOptions.selector ? this.defaultOptions.selector : 'map' }));
416
413
  this.map.on('load', (e) => {
417
414
  this.onMapReady(e);
@@ -657,6 +654,14 @@ export class Map {
657
654
  });
658
655
  }
659
656
  this.onMapReadyListener.next(true);
657
+ setTimeout(() => {
658
+ map.on('sourcedata', (ev) => {
659
+ if (ev.sourceId === 'main' && ev.isSourceLoaded && !this.mainSourceLoaded) {
660
+ this.mainSourceLoaded = true;
661
+ this.onMainSourceLoadedListener.next(true);
662
+ }
663
+ });
664
+ }, 500);
660
665
  if (this.defaultOptions.useGpsLocation) {
661
666
  this.initGeoLocation();
662
667
  }
@@ -1067,7 +1072,10 @@ export class Map {
1067
1072
  if (this.map) {
1068
1073
  if (this.defaultOptions.routeAnimation.type === 'point' || this.defaultOptions.routeAnimation.type === 'puck') {
1069
1074
  const routingLineLayer = this.state.style.getLayer('proximiio-routing-line-remaining');
1075
+ const routingLineSymbolLayer = this.state.style.getLayer('proximiio-routing-line-symbols');
1070
1076
  if (routingLineLayer && this.defaultOptions.polygonsOptions.drawRouteUnderPolygons) {
1077
+ this.state.style.removeLayer('proximiio-routing-line-symbols');
1078
+ this.state.style.addLayer(routingLineSymbolLayer, 'proximiio-paths');
1071
1079
  this.state.style.removeLayer('proximiio-routing-line-remaining');
1072
1080
  this.state.style.addLayer(routingLineLayer, 'proximiio-paths');
1073
1081
  }
@@ -2108,6 +2116,7 @@ export class Map {
2108
2116
  details: this.defaultOptions.routeWithDetails ? this.routingSource.details : null,
2109
2117
  start: this.startPoint,
2110
2118
  end: this.endPoint,
2119
+ preview: false,
2111
2120
  });
2112
2121
  if (this.defaultOptions.sendAnalytics) {
2113
2122
  const logger = new WayfindingLogger({
@@ -2141,6 +2150,34 @@ export class Map {
2141
2150
  }
2142
2151
  return;
2143
2152
  }
2153
+ if (event === 'preview-finished') {
2154
+ if (this.routingSource.route) {
2155
+ this.currentStep = 0;
2156
+ const textNavigation = {
2157
+ steps: this.routingSource.steps,
2158
+ destination: this.endPoint,
2159
+ start: this.startPoint,
2160
+ };
2161
+ this.state = Object.assign(Object.assign({}, this.state), { loadingRoute: false, textNavigation });
2162
+ if (this.defaultOptions.forceFloorLevel !== null && this.defaultOptions.forceFloorLevel !== undefined) {
2163
+ this.routingSource.data.features = this.routingSource.data.features.map((f) => {
2164
+ if (f.properties.level !== this.defaultOptions.forceFloorLevel) {
2165
+ f.properties.level = this.defaultOptions.forceFloorLevel;
2166
+ }
2167
+ return f;
2168
+ });
2169
+ }
2170
+ this.onRouteFoundListener.next({
2171
+ route: this.routingSource.route,
2172
+ TBTNav: this.defaultOptions.enableTBTNavigation ? textNavigation : null,
2173
+ details: this.defaultOptions.routeWithDetails ? this.routingSource.details : null,
2174
+ start: this.startPoint,
2175
+ end: this.endPoint,
2176
+ preview: true,
2177
+ });
2178
+ }
2179
+ return;
2180
+ }
2144
2181
  if (event === 'route-undefined') {
2145
2182
  console.log('route not found');
2146
2183
  this.state = Object.assign(Object.assign({}, this.state), { loadingRoute: false });
@@ -2401,6 +2438,11 @@ export class Map {
2401
2438
  }
2402
2439
  this.state = Object.assign(Object.assign({}, this.state), { style: this.state.style });
2403
2440
  }
2441
+ onRoutePreview(start, finish) {
2442
+ this.startPoint = start;
2443
+ this.endPoint = finish;
2444
+ this.routingSource.update(start, finish, true);
2445
+ }
2404
2446
  onRouteCancel() {
2405
2447
  this.state = Object.assign(Object.assign({}, this.state), { textNavigation: null });
2406
2448
  if (this.defaultOptions.initPolygons) {
@@ -2522,7 +2564,12 @@ export class Map {
2522
2564
  }
2523
2565
  }
2524
2566
  onRestartRouteAnimation(delay) {
2567
+ var _a;
2525
2568
  if (this.defaultOptions.routeAnimation.type === 'point' || this.defaultOptions.routeAnimation.type === 'puck') {
2569
+ const route = this.routingSource.route[`path-part-${this.currentStep}`] &&
2570
+ ((_a = this.routingSource.route[`path-part-${this.currentStep}`].properties) === null || _a === void 0 ? void 0 : _a.level) === this.state.floor.level
2571
+ ? this.routingSource.route[`path-part-${this.currentStep}`]
2572
+ : lineString(this.routingSource.levelPoints[this.state.floor.level].map((i) => i.geometry.coordinates), { level: this.state.floor.level });
2526
2573
  clearInterval(this.animationInterval);
2527
2574
  // @ts-ignore
2528
2575
  this.map.getSource('pointAlong').setData({
@@ -2534,6 +2581,12 @@ export class Map {
2534
2581
  type: 'FeatureCollection',
2535
2582
  features: [],
2536
2583
  });
2584
+ setTimeout(() => {
2585
+ this.map.jumpTo({
2586
+ center: route.geometry.coordinates[0],
2587
+ });
2588
+ }, 100);
2589
+ this.map.setStyle(this.state.style);
2537
2590
  }
2538
2591
  setTimeout(() => {
2539
2592
  this.animateRoute();
@@ -2543,6 +2596,7 @@ export class Map {
2543
2596
  if (this.defaultOptions.isKiosk && this.defaultOptions.kioskSettings.showLabel && this.kioskPopup) {
2544
2597
  this.kioskPopup.setHTML(translations[this.defaultOptions.language].YOU_ARE_HERE);
2545
2598
  }
2599
+ this.state.style.setSource('main', this.geojsonSource);
2546
2600
  }
2547
2601
  getClosestFeature(amenityId, fromFeature) {
2548
2602
  let sameLevelfeatures = this.state.allFeatures.features.filter((i) => i.properties.amenity === amenityId &&
@@ -2604,6 +2658,19 @@ export class Map {
2604
2658
  getMapReadyListener() {
2605
2659
  return this.onMapReadyListener;
2606
2660
  }
2661
+ /**
2662
+ * @memberof Map
2663
+ * @name getMainSourceLoadedListener
2664
+ * @returns returns main source fully loaded listener
2665
+ * @example
2666
+ * const map = new Proximiio.Map();
2667
+ * map.getMainSourceLoadedListener().subscribe(loaded => {
2668
+ * console.log('map loaded', loaded);
2669
+ * });
2670
+ */
2671
+ getMainSourceLoadedListener() {
2672
+ return this.onMainSourceLoadedListener;
2673
+ }
2607
2674
  /**
2608
2675
  * @memberof Map
2609
2676
  * @name getMapFailedListener
@@ -2762,6 +2829,7 @@ export class Map {
2762
2829
  * @param idFrom {string} start feature id, optional for kiosk
2763
2830
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
2764
2831
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
2832
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
2765
2833
  * @example
2766
2834
  * const map = new Proximiio.Map();
2767
2835
  * map.getMapReadyListener().subscribe(ready => {
@@ -2769,7 +2837,7 @@ export class Map {
2769
2837
  * map.findRouteByIds('finishId, 'startId');
2770
2838
  * });
2771
2839
  */
2772
- findRouteByIds(idTo, idFrom, accessibleRoute, wayfindingConfig) {
2840
+ findRouteByIds(idTo, idFrom, accessibleRoute, wayfindingConfig, addToMap) {
2773
2841
  const fromFeature = idFrom
2774
2842
  ? this.state.allFeatures.features.find((f) => f.id === idFrom || f.properties.id === idFrom)
2775
2843
  : this.startPoint;
@@ -2778,7 +2846,12 @@ export class Map {
2778
2846
  if (wayfindingConfig) {
2779
2847
  this.routingSource.setConfig(wayfindingConfig);
2780
2848
  }
2781
- this.onRouteUpdate(fromFeature, toFeature);
2849
+ if (addToMap !== false) {
2850
+ this.onRouteUpdate(fromFeature, toFeature);
2851
+ }
2852
+ else {
2853
+ this.onRoutePreview(fromFeature, toFeature);
2854
+ }
2782
2855
  }
2783
2856
  /**
2784
2857
  * This method will generate route based on selected features by their titles
@@ -2788,6 +2861,7 @@ export class Map {
2788
2861
  * @param titleFrom {string} start feature title, optional for kiosk
2789
2862
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
2790
2863
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
2864
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
2791
2865
  * @example
2792
2866
  * const map = new Proximiio.Map();
2793
2867
  * map.getMapReadyListener().subscribe(ready => {
@@ -2795,7 +2869,7 @@ export class Map {
2795
2869
  * map.findRouteByTitle('myFeatureTitle', 'anotherFeatureTitle');
2796
2870
  * });
2797
2871
  */
2798
- findRouteByTitle(titleTo, titleFrom, accessibleRoute, wayfindingConfig) {
2872
+ findRouteByTitle(titleTo, titleFrom, accessibleRoute, wayfindingConfig, addToMap) {
2799
2873
  const fromFeature = titleFrom
2800
2874
  ? this.state.allFeatures.features.find((f) => f.properties.title === titleFrom)
2801
2875
  : this.startPoint;
@@ -2804,7 +2878,12 @@ export class Map {
2804
2878
  if (wayfindingConfig) {
2805
2879
  this.routingSource.setConfig(wayfindingConfig);
2806
2880
  }
2807
- this.onRouteUpdate(fromFeature, toFeature);
2881
+ if (addToMap !== false) {
2882
+ this.onRouteUpdate(fromFeature, toFeature);
2883
+ }
2884
+ else {
2885
+ this.onRoutePreview(fromFeature, toFeature);
2886
+ }
2808
2887
  }
2809
2888
  /**
2810
2889
  * This method will generate route based on selected features by their titles
@@ -2818,6 +2897,7 @@ export class Map {
2818
2897
  * @param levelFrom {number} start level, optional for kiosk
2819
2898
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
2820
2899
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
2900
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
2821
2901
  * @example
2822
2902
  * const map = new Proximiio.Map();
2823
2903
  * map.getMapReadyListener().subscribe(ready => {
@@ -2825,7 +2905,7 @@ export class Map {
2825
2905
  * map.findRouteByCoords(48.606703739771774, 17.833092384506614, 0, 48.60684545080579, 17.833450676669543, 0);
2826
2906
  * });
2827
2907
  */
2828
- findRouteByCoords(latTo, lngTo, levelTo, latFrom, lngFrom, levelFrom, accessibleRoute, wayfindingConfig) {
2908
+ findRouteByCoords(latTo, lngTo, levelTo, latFrom, lngFrom, levelFrom, accessibleRoute, wayfindingConfig, addToMap) {
2829
2909
  const fromFeature = latFrom && lngFrom && levelFrom
2830
2910
  ? feature({ type: 'Point', coordinates: [lngFrom, latFrom] }, { level: levelFrom })
2831
2911
  : this.startPoint;
@@ -2834,7 +2914,12 @@ export class Map {
2834
2914
  if (wayfindingConfig) {
2835
2915
  this.routingSource.setConfig(wayfindingConfig);
2836
2916
  }
2837
- this.onRouteUpdate(fromFeature, toFeature);
2917
+ if (addToMap !== false) {
2918
+ this.onRouteUpdate(fromFeature, toFeature);
2919
+ }
2920
+ else {
2921
+ this.onRoutePreview(fromFeature, toFeature);
2922
+ }
2838
2923
  }
2839
2924
  /**
2840
2925
  * This method will generate route to nearest amenity feature
@@ -2844,6 +2929,7 @@ export class Map {
2844
2929
  * @param idFrom {string} start feature id, optional for kiosk
2845
2930
  * @param accessibleRoute {boolean} if true generated routed will be accessible without stairs, etc., optional
2846
2931
  * @param wayfindingConfig {WayfindingConfigModel} wayfinding configuration, optional
2932
+ * @param addToMap {boolean} default true, if set to false route will not be added to map
2847
2933
  * @example
2848
2934
  * const map = new Proximiio.Map();
2849
2935
  * map.getMapReadyListener().subscribe(ready => {
@@ -2851,7 +2937,7 @@ export class Map {
2851
2937
  * map.findRouteToNearestFeature('amenityId');
2852
2938
  * });
2853
2939
  */
2854
- findRouteToNearestFeature(amenityId, idFrom, accessibleRoute, wayfindingConfig) {
2940
+ findRouteToNearestFeature(amenityId, idFrom, accessibleRoute, wayfindingConfig, addToMap) {
2855
2941
  const fromFeature = idFrom
2856
2942
  ? this.state.allFeatures.features.find((f) => f.id === idFrom || f.properties.id === idFrom)
2857
2943
  : this.startPoint;
@@ -2861,7 +2947,12 @@ export class Map {
2861
2947
  if (wayfindingConfig) {
2862
2948
  this.routingSource.setConfig(wayfindingConfig);
2863
2949
  }
2864
- this.onRouteUpdate(fromFeature, toFeature);
2950
+ if (addToMap !== false) {
2951
+ this.onRouteUpdate(fromFeature, toFeature);
2952
+ }
2953
+ else {
2954
+ this.onRoutePreview(fromFeature, toFeature);
2955
+ }
2865
2956
  }
2866
2957
  else {
2867
2958
  throw new Error(`Feature not found`);
@@ -1,6 +1,5 @@
1
1
  import DataSource from './data_source';
2
2
  import { FeatureCollection } from '../../../models/feature';
3
- import { convertToRTL } from '../../../common';
4
3
  export default class GeoJSONSource extends DataSource {
5
4
  constructor(features) {
6
5
  super('main');
@@ -34,9 +33,6 @@ export default class GeoJSONSource extends DataSource {
34
33
  ? feature.properties.title_i18n[language]
35
34
  : feature.properties.title;
36
35
  }
37
- if (language === 'ar') {
38
- feature.properties.title = convertToRTL(feature.properties.title);
39
- }
40
36
  }
41
37
  query(query, level = 0) {
42
38
  return this.data.features.filter((f) => f.properties.title && f.properties.title.toLowerCase().match(query.toLowerCase()));
@@ -31,7 +31,7 @@ export default class RoutingSource extends DataSource {
31
31
  constructor();
32
32
  toggleAccessible(value: any): void;
33
33
  setConfig(config: WayfindingConfigModel): void;
34
- update(start?: Feature, finish?: Feature): Promise<void>;
34
+ update(start?: Feature, finish?: Feature, preview?: boolean): Promise<void>;
35
35
  cancel(): void;
36
36
  }
37
37
  export {};
@@ -24,7 +24,7 @@ export default class RoutingSource extends DataSource {
24
24
  setConfig(config) {
25
25
  this.routing.setConfig(config);
26
26
  }
27
- update(start, finish) {
27
+ update(start, finish, preview) {
28
28
  return __awaiter(this, void 0, void 0, function* () {
29
29
  this.start = start;
30
30
  this.finish = finish;
@@ -64,10 +64,12 @@ export default class RoutingSource extends DataSource {
64
64
  // line.properties.amenity = 'chevron_right'
65
65
  // return line
66
66
  // })
67
- this.data = new FeatureCollection({
68
- features: [this.start, this.finish].concat(this.lines || []).filter((i) => i),
69
- });
70
- this.notify('loading-finished');
67
+ this.data = preview
68
+ ? new FeatureCollection({})
69
+ : new FeatureCollection({
70
+ features: [this.start, this.finish].concat(this.lines || []).filter((i) => i),
71
+ });
72
+ this.notify(preview ? 'preview-finished' : 'loading-finished');
71
73
  this.notify('feature-updated');
72
74
  }
73
75
  });