@panoramax/web-viewer 3.2.3-develop-54fea60b → 3.2.3-develop-17bc8da1

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.
@@ -4,7 +4,7 @@
4
4
  **Kind**: static class of <code>Panoramax.utils</code>
5
5
 
6
6
  * [.InitParameters](#Panoramax.utils.InitParameters)
7
- * [new InitParameters([componentAttrs], [urlParams])](#new_Panoramax.utils.InitParameters_new)
7
+ * [new InitParameters([componentAttrs], [urlParams], [browserStorage])](#new_Panoramax.utils.InitParameters_new)
8
8
  * [.getParentInit()](#Panoramax.utils.InitParameters+getParentInit)
9
9
  * [.getParentPostInit()](#Panoramax.utils.InitParameters+getParentPostInit)
10
10
  * [.getPSVInit()](#Panoramax.utils.InitParameters+getPSVInit)
@@ -14,7 +14,7 @@
14
14
 
15
15
  <a name="new_Panoramax.utils.InitParameters_new"></a>
16
16
 
17
- ### new InitParameters([componentAttrs], [urlParams])
17
+ ### new InitParameters([componentAttrs], [urlParams], [browserStorage])
18
18
  Merges all URL parameters and component attributes into a single set of coherent settings.
19
19
 
20
20
 
@@ -22,6 +22,7 @@ Merges all URL parameters and component attributes into a single set of coherent
22
22
  | --- | --- | --- |
23
23
  | [componentAttrs] | <code>object</code> | HTML attributes from parent component |
24
24
  | [urlParams] | <code>object</code> | Parameters extracted from URL |
25
+ | [browserStorage] | <code>object</code> | Parameters read from local/session storage |
25
26
 
26
27
  <a name="Panoramax.utils.InitParameters+getParentInit"></a>
27
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panoramax/web-viewer",
3
- "version": "3.2.3-develop-54fea60b",
3
+ "version": "3.2.3-develop-17bc8da1",
4
4
  "description": "Panoramax web viewer for geolocated pictures",
5
5
  "main": "build/index.js",
6
6
  "author": "Panoramax team",
@@ -3,7 +3,7 @@
3
3
  import "./Viewer.css";
4
4
  import { SYSTEM as PSSystem, DEFAULTS as PSDefaults } from "@photo-sphere-viewer/core";
5
5
  import URLHandler from "../../utils/URLHandler";
6
- import { linkMapAndPhoto } from "../../utils/map";
6
+ import { linkMapAndPhoto, saveMapParamsToLocalStorage, getMapParamsFromLocalStorage } from "../../utils/map";
7
7
  import Basic from "./Basic";
8
8
  import Photo, { PSV_DEFAULT_ZOOM, PSV_ANIM_DURATION } from "../ui/Photo";
9
9
  import MapMore from "../ui/MapMore";
@@ -139,7 +139,8 @@ export default class Viewer extends Basic {
139
139
  this.loader.setAttribute("value", 30);
140
140
  this._initParams = new InitParameters(
141
141
  InitParameters.GetComponentProperties(Viewer, this),
142
- Object.assign({}, this.urlHandler?.currentURLParams(), this.urlHandler?.currentURLParams(true))
142
+ Object.assign({}, this.urlHandler?.currentURLParams(), this.urlHandler?.currentURLParams(true)),
143
+ { map: getMapParamsFromLocalStorage() },
143
144
  );
144
145
 
145
146
  const myInitParams = this._initParams.getParentInit();
@@ -359,6 +360,7 @@ export default class Viewer extends Basic {
359
360
  async _initMap() {
360
361
  await new Promise(resolve => {
361
362
  this.map = new MapMore(this, this.mapContainer, this._initParams.getMapInit());
363
+ saveMapParamsToLocalStorage(this.map);
362
364
  this.map.once("users-changed", () => {
363
365
  this.loader.setAttribute("value", 75);
364
366
  resolve();
@@ -16,9 +16,10 @@ const MAP_NONE = ["none", "null", "false", false];
16
16
  * @class Panoramax.utils.InitParameters
17
17
  * @param {object} [componentAttrs] HTML attributes from parent component
18
18
  * @param {object} [urlParams] Parameters extracted from URL
19
+ * @param {object} [browserStorage] Parameters read from local/session storage
19
20
  */
20
21
  export default class InitParameters { // eslint-disable-line import/no-unused-modules
21
- constructor(componentAttrs = {}, urlParams = {}) {
22
+ constructor(componentAttrs = {}, urlParams = {}, browserStorage = {}) {
22
23
  // Skip URL parameters if disabled by component
23
24
  if(componentAttrs["url-parameters"] === "false") { urlParams = {}; }
24
25
 
@@ -29,6 +30,8 @@ export default class InitParameters { // eslint-disable-line import/no-unused-mo
29
30
  // Sanitize Map parameters
30
31
  let componentMap = {};
31
32
  if(typeof componentAttrs?.map === "object") { componentMap = componentAttrs.map; }
33
+ let browserMap = {};
34
+ if(typeof browserStorage?.map === "object") { browserMap = browserStorage.map; }
32
35
 
33
36
  // Extract map position from URL
34
37
  let urlMap = urlParams.map && urlParams.map !== "none" ? getMapPositionFromString(urlParams.map) : null;
@@ -41,10 +44,10 @@ export default class InitParameters { // eslint-disable-line import/no-unused-mo
41
44
  let users = urlParams.users || componentAttrs.users;
42
45
  let psv_speed = urlParams.speed || componentPsv?.transitionDuration;
43
46
  let psv_nav = urlParams.nav || componentPsv?.picturesNavigation;
44
- let map_theme = urlParams.theme || componentMap.theme;
45
- let map_background = urlParams.background || componentMap.background;
46
- let map_center = urlMap?.center || componentMap.center;
47
- let map_zoom = urlMap?.zoom || componentMap.zoom;
47
+ let map_theme = urlParams.theme || browserMap?.theme || componentMap.theme;
48
+ let map_background = urlParams.background || browserMap?.background || componentMap.background;
49
+ let map_center = urlMap?.center || browserMap?.center || componentMap.center;
50
+ let map_zoom = urlMap?.zoom || browserMap?.zoom || componentMap.zoom;
48
51
  let map_pitch = urlMap?.pitch || componentMap.pitch;
49
52
  let map_bearing = urlMap?.bearing || componentMap.bearing;
50
53
 
package/src/utils/map.js CHANGED
@@ -602,4 +602,44 @@ export function initMapKeyboardHandler(parent) {
602
602
  }
603
603
  };
604
604
  }.bind(parent.map.keyboard);
605
- }
605
+ }
606
+
607
+ const MAP_PARAMS_STORAGE = "pnx-map-parameters";
608
+
609
+ /**
610
+ * Reads map parameters from localStorage
611
+ * @private
612
+ */
613
+ export function getMapParamsFromLocalStorage() {
614
+ const params = localStorage.getItem(MAP_PARAMS_STORAGE);
615
+ if(!params) { return {}; }
616
+ try {
617
+ return JSON.parse(params);
618
+ }
619
+ catch(e) {
620
+ console.warn("Can't read map parameters stored in localStorage", e);
621
+ return {};
622
+ }
623
+ }
624
+
625
+ /**
626
+ * Save map parameters into localStorage.
627
+ * @private
628
+ */
629
+ export function saveMapParamsToLocalStorage(map) {
630
+ // Save map state in localStorage
631
+ const save = () => localStorage.setItem(MAP_PARAMS_STORAGE, JSON.stringify({
632
+ center: Object.fromEntries(Object.entries(map.getCenter()).map(([k,v]) => ([k,v.toFixed(7)]))),
633
+ zoom: map.getZoom().toFixed(1),
634
+ background: map.getBackground(),
635
+ theme: map._mapFilters.theme
636
+ }));
637
+
638
+ // Add events to know when to rewrite info
639
+ map.on("background-changed", save);
640
+ map.on("filters-changed", save);
641
+ map.on("moveend", save);
642
+ map.on("zoomend", save);
643
+ map.on("dragend", save);
644
+ map.on("boxzoomend", save);
645
+ }
@@ -4,8 +4,7 @@ import {
4
4
  } from "../../src/utils/InitParameters";
5
5
 
6
6
  describe("InitParameters", () => {
7
- let componentAttrs;
8
- let urlParams;
7
+ let componentAttrs, urlParams, browserStorage;
9
8
 
10
9
  beforeEach(() => {
11
10
  console.warn = jest.fn();
@@ -41,12 +40,16 @@ describe("InitParameters", () => {
41
40
  camera: "cam1",
42
41
  pic_score: "high",
43
42
  };
43
+
44
+ browserStorage = {
45
+ map: { theme: "qualityscore", background: "aerial", center: [-10, -20], zoom: 19 },
46
+ };
44
47
  });
45
48
 
46
49
  afterEach(() => jest.clearAllMocks());
47
50
 
48
51
  it("should initialize with componentAttrs and urlParams", () => {
49
- const initParams = new InitParameters(componentAttrs, urlParams);
52
+ const initParams = new InitParameters(componentAttrs, urlParams, browserStorage);
50
53
 
51
54
  expect(initParams._parentInit).toEqual({
52
55
  map: true,
@@ -111,6 +114,20 @@ describe("InitParameters", () => {
111
114
  });
112
115
  });
113
116
 
117
+ it("uses browserStorage parameters if no URL parameter is available", () => {
118
+ componentAttrs.map.raster = {};
119
+ const initParams = new InitParameters(componentAttrs, undefined, browserStorage);
120
+ expect(initParams._mapAny).toEqual({
121
+ theme: "qualityscore",
122
+ background: "aerial",
123
+ center: [-10,-20],
124
+ zoom: 19,
125
+ pitch: undefined,
126
+ bearing: undefined,
127
+ users: "user1,user2",
128
+ });
129
+ });
130
+
114
131
  it("should sanitize objects correctly", () => {
115
132
  const initParams = new InitParameters(componentAttrs, urlParams);
116
133
  const obj = { a: 1, b: undefined, c: 3 };
@@ -0,0 +1,11 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`getMapParamsFromLocalStorage should return parsed params if they exist in localStorage 1`] = `
4
+ Array [
5
+ Array [
6
+ "pnx-map-parameters",
7
+ ],
8
+ ]
9
+ `;
10
+
11
+ exports[`saveMapParamsToLocalStorage should save map parameters to localStorage 1`] = `Array []`;
@@ -12,30 +12,30 @@ describe("getThumbGif", () => {
12
12
  });
13
13
 
14
14
  describe("isLabelLayer", () => {
15
- it("works with just text-field", () => {
16
- const layer = { type: "symbol", layout: { "text-field": "Label" } };
17
- expect(map.isLabelLayer(layer)).toBe(true);
18
- });
19
-
20
- it("works with text-field + minzoom < 15", () => {
21
- const layer = { type: "symbol", layout: { "text-field": "Label" }, minzoom: 10 };
22
- expect(map.isLabelLayer(layer)).toBe(true);
23
- });
24
-
25
- it("works with text-field + minzoom >= 15", () => {
26
- const layer = { type: "symbol", layout: { "text-field": "Label" }, minzoom: 15 };
27
- expect(map.isLabelLayer(layer)).toBe(false);
28
- });
29
-
30
- it("works with non-symbol layer", () => {
31
- const layer = { type: "fill", layout: { "text-field": "Label" } };
32
- expect(map.isLabelLayer(layer)).toBe(false);
33
- });
34
-
35
- it("works without text-field", () => {
36
- const layer = { type: "symbol", layout: {} };
37
- expect(map.isLabelLayer(layer)).toBe(false);
38
- });
15
+ it("works with just text-field", () => {
16
+ const layer = { type: "symbol", layout: { "text-field": "Label" } };
17
+ expect(map.isLabelLayer(layer)).toBe(true);
18
+ });
19
+
20
+ it("works with text-field + minzoom < 15", () => {
21
+ const layer = { type: "symbol", layout: { "text-field": "Label" }, minzoom: 10 };
22
+ expect(map.isLabelLayer(layer)).toBe(true);
23
+ });
24
+
25
+ it("works with text-field + minzoom >= 15", () => {
26
+ const layer = { type: "symbol", layout: { "text-field": "Label" }, minzoom: 15 };
27
+ expect(map.isLabelLayer(layer)).toBe(false);
28
+ });
29
+
30
+ it("works with non-symbol layer", () => {
31
+ const layer = { type: "fill", layout: { "text-field": "Label" } };
32
+ expect(map.isLabelLayer(layer)).toBe(false);
33
+ });
34
+
35
+ it("works without text-field", () => {
36
+ const layer = { type: "symbol", layout: {} };
37
+ expect(map.isLabelLayer(layer)).toBe(false);
38
+ });
39
39
  });
40
40
 
41
41
  describe("getUserLayerId", () => {
@@ -65,3 +65,62 @@ describe("switchCoefValue", () => {
65
65
  expect(res).toEqual({id: "bla", paint: { "circle-radius": ["bla", ["get", "coef_360"]]}, layout: {"circle-sort": "coef_360"}})
66
66
  });
67
67
  });
68
+
69
+
70
+ Object.defineProperty(window, "localStorage", { value: {
71
+ setItem: jest.fn(),
72
+ getItem: jest.fn(),
73
+ } });
74
+
75
+ describe("getMapParamsFromLocalStorage", () => {
76
+ it("should return an empty object if no params are stored", () => {
77
+ localStorage.getItem.mockReturnValue(null);
78
+ const result = map.getMapParamsFromLocalStorage();
79
+ expect(result).toEqual({});
80
+ });
81
+
82
+ it("should return parsed params if they exist in localStorage", () => {
83
+ const params = { center: { lat: "48.8566000", lng: "2.3522000" }, zoom: "10.0", background: "aerial", theme: "age" };
84
+ localStorage.getItem.mockReturnValue(JSON.stringify(params));
85
+ const result = map.getMapParamsFromLocalStorage();
86
+ expect(localStorage.getItem.mock.calls).toMatchSnapshot();
87
+ expect(result).toEqual(params);
88
+ });
89
+
90
+ it("should return an empty object if JSON parsing fails", () => {
91
+ console.warn = jest.fn();
92
+ localStorage.getItem.mockReturnValue("invalid-json");
93
+ const result = map.getMapParamsFromLocalStorage();
94
+ expect(result).toEqual({});
95
+ expect(console.warn.mock.calls[0][0]).toEqual("Can't read map parameters stored in localStorage");
96
+ });
97
+ });
98
+
99
+ describe("saveMapParamsToLocalStorage", () => {
100
+ const mockMap = {
101
+ getCenter: jest.fn().mockReturnValue({ lat: 48.8566, lng: 2.3522 }),
102
+ getZoom: jest.fn().mockReturnValue(10),
103
+ getBackground: jest.fn().mockReturnValue("streets"),
104
+ _mapFilters: { theme: "qualityscore" },
105
+ on: jest.fn(),
106
+ };
107
+
108
+ beforeEach(() => {
109
+ jest.clearAllMocks();
110
+ });
111
+
112
+ it("should add event listeners to the map", () => {
113
+ map.saveMapParamsToLocalStorage(mockMap);
114
+ expect(mockMap.on).toHaveBeenCalledWith("background-changed", expect.any(Function));
115
+ expect(mockMap.on).toHaveBeenCalledWith("filters-changed", expect.any(Function));
116
+ expect(mockMap.on).toHaveBeenCalledWith("moveend", expect.any(Function));
117
+ expect(mockMap.on).toHaveBeenCalledWith("zoomend", expect.any(Function));
118
+ expect(mockMap.on).toHaveBeenCalledWith("dragend", expect.any(Function));
119
+ expect(mockMap.on).toHaveBeenCalledWith("boxzoomend", expect.any(Function));
120
+ });
121
+
122
+ it("should save map parameters to localStorage", () => {
123
+ map.saveMapParamsToLocalStorage(mockMap);
124
+ expect(localStorage.setItem.mock.calls).toMatchSnapshot();
125
+ });
126
+ });