@panoramax/web-viewer 3.2.3 → 4.0.0-develop-9f9cf858
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.
- package/.gitlab-ci.yml +13 -6
- package/CHANGELOG.md +56 -1
- package/CODE_OF_CONDUCT.md +1 -1
- package/README.md +1 -1
- package/build/editor.html +10 -1
- package/build/index.css +12 -12
- package/build/index.css.map +1 -1
- package/build/index.html +1 -1
- package/build/index.js +2126 -14
- package/build/index.js.map +1 -1
- package/build/map.html +1 -1
- package/build/photo.html +1 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff +0 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff2 +0 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff +0 -0
- package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff2 +0 -0
- package/build/viewer.html +12 -1
- package/build/widgets.html +1 -0
- package/config/jest/mocks.js +201 -0
- package/config/paths.js +2 -0
- package/config/webpack.config.js +52 -0
- package/docs/03_URL_settings.md +14 -16
- package/docs/05_Compatibility.md +59 -76
- package/docs/09_Develop.md +46 -11
- package/docs/90_Releases.md +2 -2
- package/docs/images/class_diagram.drawio +60 -45
- package/docs/images/class_diagram.jpg +0 -0
- package/docs/images/screenshot.jpg +0 -0
- package/docs/index.md +135 -0
- package/docs/reference/components/core/Basic.md +196 -0
- package/docs/reference/components/core/CoverageMap.md +210 -0
- package/docs/reference/components/core/Editor.md +224 -0
- package/docs/reference/components/core/PhotoViewer.md +307 -0
- package/docs/reference/components/core/Viewer.md +350 -0
- package/docs/reference/components/layout/BottomDrawer.md +35 -0
- package/docs/reference/components/layout/CorneredGrid.md +29 -0
- package/docs/reference/components/layout/Mini.md +45 -0
- package/docs/reference/components/layout/Tabs.md +45 -0
- package/docs/reference/components/menus/MapBackground.md +32 -0
- package/docs/reference/components/menus/MapFilters.md +15 -0
- package/docs/reference/components/menus/MapLayers.md +15 -0
- package/docs/reference/components/menus/MapLegend.md +15 -0
- package/docs/reference/components/menus/PictureLegend.md +16 -0
- package/docs/reference/components/menus/PictureMetadata.md +15 -0
- package/docs/reference/components/menus/PlayerOptions.md +15 -0
- package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
- package/docs/reference/components/menus/ReportForm.md +15 -0
- package/docs/reference/components/menus/ShareMenu.md +15 -0
- package/docs/reference/components/ui/Button.md +40 -0
- package/docs/reference/components/ui/ButtonGroup.md +36 -0
- package/docs/reference/components/ui/CopyButton.md +38 -0
- package/docs/reference/components/ui/Grade.md +32 -0
- package/docs/reference/components/ui/LinkButton.md +45 -0
- package/docs/reference/components/ui/ListGroup.md +22 -0
- package/docs/reference/components/ui/Loader.md +56 -0
- package/docs/reference/components/ui/Map.md +239 -0
- package/docs/reference/components/ui/MapMore.md +256 -0
- package/docs/reference/components/ui/Photo.md +385 -0
- package/docs/reference/components/ui/Popup.md +56 -0
- package/docs/reference/components/ui/ProgressBar.md +32 -0
- package/docs/reference/components/ui/QualityScore.md +45 -0
- package/docs/reference/components/ui/SearchBar.md +63 -0
- package/docs/reference/components/ui/TogglableGroup.md +39 -0
- package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
- package/docs/reference/components/ui/widgets/Legend.md +49 -0
- package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
- package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
- package/docs/reference/components/ui/widgets/OSMEditors.md +15 -0
- package/docs/reference/components/ui/widgets/PictureLegendActions.md +32 -0
- package/docs/reference/components/ui/widgets/Player.md +33 -0
- package/docs/reference/components/ui/widgets/Zoom.md +15 -0
- package/docs/reference/utils/API.md +334 -0
- package/docs/reference/utils/InitParameters.md +68 -0
- package/docs/reference/utils/URLHandler.md +107 -0
- package/docs/reference.md +79 -0
- package/docs/shortcuts.md +11 -0
- package/docs/tutorials/aerial_imagery.md +19 -0
- package/docs/tutorials/authentication.md +10 -0
- package/docs/tutorials/custom_widgets.md +59 -0
- package/docs/tutorials/map_style.md +39 -0
- package/docs/tutorials/migrate_v4.md +153 -0
- package/docs/tutorials/synced_coverage.md +43 -0
- package/mkdocs.yml +66 -5
- package/package.json +22 -17
- package/public/editor.html +21 -29
- package/public/index.html +17 -12
- package/public/map.html +19 -18
- package/public/photo.html +55 -0
- package/public/viewer.html +22 -26
- package/public/widgets.html +306 -0
- package/scripts/doc.js +79 -0
- package/src/components/core/Basic.css +48 -0
- package/src/components/core/Basic.js +349 -0
- package/src/components/core/CoverageMap.css +9 -0
- package/src/components/core/CoverageMap.js +139 -0
- package/src/components/core/Editor.css +23 -0
- package/src/components/core/Editor.js +390 -0
- package/src/components/core/PhotoViewer.css +48 -0
- package/src/components/core/PhotoViewer.js +499 -0
- package/src/components/core/Viewer.css +98 -0
- package/src/components/core/Viewer.js +564 -0
- package/src/components/core/index.js +12 -0
- package/src/components/index.js +13 -0
- package/src/components/layout/BottomDrawer.js +257 -0
- package/src/components/layout/CorneredGrid.js +112 -0
- package/src/components/layout/Mini.js +117 -0
- package/src/components/layout/Tabs.js +133 -0
- package/src/components/layout/index.js +9 -0
- package/src/components/menus/MapBackground.js +106 -0
- package/src/components/menus/MapFilters.js +400 -0
- package/src/components/menus/MapLayers.js +143 -0
- package/src/components/menus/MapLegend.js +34 -0
- package/src/components/menus/PictureLegend.js +257 -0
- package/src/components/menus/PictureMetadata.js +317 -0
- package/src/components/menus/PlayerOptions.js +95 -0
- package/src/components/menus/QualityScoreDoc.js +36 -0
- package/src/components/menus/ReportForm.js +133 -0
- package/src/components/menus/Share.js +100 -0
- package/src/components/menus/index.js +15 -0
- package/src/components/styles.js +383 -0
- package/src/components/ui/Button.js +77 -0
- package/src/components/ui/ButtonGroup.css +57 -0
- package/src/components/ui/ButtonGroup.js +68 -0
- package/src/components/ui/CopyButton.js +106 -0
- package/src/components/ui/Grade.js +54 -0
- package/src/components/ui/LinkButton.js +67 -0
- package/src/components/ui/ListGroup.js +66 -0
- package/src/components/ui/Loader.js +203 -0
- package/src/components/{Map.css → ui/Map.css} +5 -17
- package/src/components/{Map.js → ui/Map.js} +148 -156
- package/src/components/ui/MapMore.js +324 -0
- package/src/components/{Photo.css → ui/Photo.css} +6 -6
- package/src/components/{Photo.js → ui/Photo.js} +313 -101
- package/src/components/ui/Popup.js +145 -0
- package/src/components/ui/ProgressBar.js +104 -0
- package/src/components/ui/QualityScore.js +147 -0
- package/src/components/ui/SearchBar.js +367 -0
- package/src/components/ui/TogglableGroup.js +157 -0
- package/src/components/ui/index.js +22 -0
- package/src/components/ui/widgets/GeoSearch.css +21 -0
- package/src/components/ui/widgets/GeoSearch.js +139 -0
- package/src/components/ui/widgets/Legend.js +113 -0
- package/src/components/ui/widgets/MapFiltersButton.js +104 -0
- package/src/components/ui/widgets/MapLayersButton.js +79 -0
- package/src/components/ui/widgets/OSMEditors.js +155 -0
- package/src/components/ui/widgets/PictureLegendActions.js +117 -0
- package/src/components/ui/widgets/Player.css +7 -0
- package/src/components/ui/widgets/Player.js +151 -0
- package/src/components/ui/widgets/Zoom.js +82 -0
- package/src/components/ui/widgets/index.js +13 -0
- package/src/img/loader_base.jpg +0 -0
- package/src/img/panoramax.svg +13 -0
- package/src/img/switch_big.svg +20 -10
- package/src/index.js +7 -9
- package/src/translations/br.json +1 -0
- package/src/translations/da.json +38 -15
- package/src/translations/de.json +5 -3
- package/src/translations/en.json +35 -15
- package/src/translations/eo.json +38 -15
- package/src/translations/es.json +1 -1
- package/src/translations/fr.json +36 -16
- package/src/translations/hu.json +1 -1
- package/src/translations/it.json +39 -16
- package/src/translations/ja.json +182 -1
- package/src/translations/nl.json +106 -6
- package/src/translations/pl.json +1 -1
- package/src/translations/sv.json +182 -0
- package/src/translations/zh_Hant.json +35 -14
- package/src/utils/API.js +109 -49
- package/src/utils/InitParameters.js +388 -0
- package/src/utils/PhotoAdapter.js +1 -0
- package/src/utils/URLHandler.js +362 -0
- package/src/utils/geocoder.js +152 -0
- package/src/utils/{I18n.js → i18n.js} +7 -3
- package/src/utils/index.js +11 -0
- package/src/utils/{Map.js → map.js} +256 -77
- package/src/utils/picture.js +442 -0
- package/src/utils/utils.js +324 -0
- package/src/utils/widgets.js +55 -0
- package/tests/components/core/Basic.test.js +121 -0
- package/tests/components/core/BasicMock.js +25 -0
- package/tests/components/core/CoverageMap.test.js +20 -0
- package/tests/components/core/Editor.test.js +20 -0
- package/tests/components/core/PhotoViewer.test.js +57 -0
- package/tests/components/core/Viewer.test.js +84 -0
- package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +73 -0
- package/tests/components/core/__snapshots__/Viewer.test.js.snap +145 -0
- package/tests/components/ui/CopyButton.test.js +52 -0
- package/tests/components/ui/Loader.test.js +55 -0
- package/tests/components/{Map.test.js → ui/Map.test.js} +73 -61
- package/tests/components/{Photo.test.js → ui/Photo.test.js} +97 -63
- package/tests/components/ui/Popup.test.js +26 -0
- package/tests/components/ui/QualityScore.test.js +18 -0
- package/tests/components/ui/SearchBar.test.js +110 -0
- package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +33 -0
- package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
- package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
- package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +70 -6
- package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
- package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
- package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
- package/tests/utils/API.test.js +83 -83
- package/tests/utils/InitParameters.test.js +499 -0
- package/tests/utils/URLHandler.test.js +401 -0
- package/tests/utils/__snapshots__/API.test.js.snap +10 -0
- package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
- package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +1 -1
- package/tests/utils/__snapshots__/map.test.js.snap +11 -0
- package/tests/utils/__snapshots__/picture.test.js.snap +327 -0
- package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
- package/tests/utils/geocoder.test.js +37 -0
- package/tests/utils/{I18n.test.js → i18n.test.js} +8 -8
- package/tests/utils/map.test.js +126 -0
- package/tests/utils/picture.test.js +745 -0
- package/tests/utils/utils.test.js +288 -0
- package/tests/utils/widgets.test.js +31 -0
- package/docs/01_Start.md +0 -149
- package/docs/02_Usage.md +0 -831
- package/docs/04_Advanced_examples.md +0 -216
- package/src/Editor.css +0 -37
- package/src/Editor.js +0 -361
- package/src/StandaloneMap.js +0 -114
- package/src/Viewer.css +0 -203
- package/src/Viewer.js +0 -1246
- package/src/components/CoreView.css +0 -70
- package/src/components/CoreView.js +0 -175
- package/src/components/Loader.css +0 -74
- package/src/components/Loader.js +0 -120
- package/src/img/loader_hd.jpg +0 -0
- package/src/utils/Exif.js +0 -193
- package/src/utils/Utils.js +0 -631
- package/src/utils/Widgets.js +0 -562
- package/src/viewer/URLHash.js +0 -469
- package/src/viewer/Widgets.css +0 -880
- package/src/viewer/Widgets.js +0 -1470
- package/tests/Editor.test.js +0 -126
- package/tests/StandaloneMap.test.js +0 -45
- package/tests/Viewer.test.js +0 -366
- package/tests/__snapshots__/Editor.test.js.snap +0 -298
- package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
- package/tests/__snapshots__/Viewer.test.js.snap +0 -195
- package/tests/components/CoreView.test.js +0 -92
- package/tests/components/Loader.test.js +0 -38
- package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
- package/tests/utils/Exif.test.js +0 -124
- package/tests/utils/Map.test.js +0 -113
- package/tests/utils/Utils.test.js +0 -300
- package/tests/utils/Widgets.test.js +0 -107
- package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
- package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
- package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
- package/tests/viewer/URLHash.test.js +0 -559
- package/tests/viewer/Widgets.test.js +0 -127
- package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
- package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/* Main container */
|
|
2
|
-
.gvs {
|
|
3
|
-
container-type: inline-size;
|
|
4
|
-
box-sizing: border-box;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.gvs * { box-sizing: border-box; }
|
|
8
|
-
|
|
9
|
-
/* Colors */
|
|
10
|
-
:root {
|
|
11
|
-
--white: #ffffff;
|
|
12
|
-
--black: #181818;
|
|
13
|
-
--black-pale: #1b1a17;
|
|
14
|
-
--red: #f70000;
|
|
15
|
-
--red-pale: #ff726f;
|
|
16
|
-
--grey: #f5f5f5;
|
|
17
|
-
--grey-pale: #cfd2cf;
|
|
18
|
-
--grey-semi-dark: #808080;
|
|
19
|
-
--grey-dark: #3e3e3e;
|
|
20
|
-
--blue: #2954e9;
|
|
21
|
-
--blue-dark: #0a1f69;
|
|
22
|
-
--blue-semi: #d7dffc;
|
|
23
|
-
--blue-pale: #f2f5ff;
|
|
24
|
-
--blue-geovisio: #34495e;
|
|
25
|
-
--beige: #f5f3ec;
|
|
26
|
-
--yellow: #fec868;
|
|
27
|
-
--orange: #ff6f00;
|
|
28
|
-
--orange-pale: #fffafa;
|
|
29
|
-
--green: #7ec636;
|
|
30
|
-
--green-pale: #f0ffee;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/* Titles */
|
|
34
|
-
.gvs h3 {
|
|
35
|
-
font-size: 1.1em;
|
|
36
|
-
line-height: 1.1em;
|
|
37
|
-
font-weight: 500;
|
|
38
|
-
margin: 10px 0 10px 0;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.gvs h4 {
|
|
42
|
-
font-size: 1.0em;
|
|
43
|
-
line-height: 1.0em;
|
|
44
|
-
font-weight: 500;
|
|
45
|
-
margin: 15px 0;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.gvs h4:first-of-type { margin-top: 0; }
|
|
49
|
-
|
|
50
|
-
.gvs h4 svg {
|
|
51
|
-
height: 18px;
|
|
52
|
-
vertical-align: sub;
|
|
53
|
-
margin-right: 2px;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.gvs h4 a svg {
|
|
57
|
-
height: 16px;
|
|
58
|
-
vertical-align: sub;
|
|
59
|
-
margin-left: 2px;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/* Hidden elements on mobile */
|
|
63
|
-
@container (max-width: 576px) {
|
|
64
|
-
.gvs-mobile-hidden { display: none !important; }
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/* Hidden elements on print */
|
|
68
|
-
@media print {
|
|
69
|
-
.gvs-print-hidden { display: none !important; }
|
|
70
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import "./CoreView.css";
|
|
2
|
-
import API from "../utils/API";
|
|
3
|
-
import { getTranslations } from "../utils/I18n";
|
|
4
|
-
import { DEFAULT_TILES } from "../utils/Map";
|
|
5
|
-
import { BASE_PANORAMA_ID, isInIframe, isInternetFast } from "../utils/Utils";
|
|
6
|
-
import PACKAGE_JSON from "../../package.json";
|
|
7
|
-
import Loader from "./Loader";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Core view is an abstract class used for setting up any of the main Panoramax JS view components.
|
|
12
|
-
*
|
|
13
|
-
* It is used to prepare API, internationalization, options checks... for Viewer, StandaloneMap and Editor classes.
|
|
14
|
-
*
|
|
15
|
-
* @param {string|Element} container The DOM element to create viewer into
|
|
16
|
-
* @param {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
|
|
17
|
-
* @param {object} [options] View options.
|
|
18
|
-
* @param {string} [options.selectedSequence] The ID of sequence to highlight on load (defaults to none)
|
|
19
|
-
* @param {string} [options.selectedPicture] The ID of picture to highlight on load (defaults to none)
|
|
20
|
-
* @param {object} [options.fetchOptions=null] Set custom options for fetch calls made against API ([same syntax as fetch options parameter](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters))
|
|
21
|
-
* @param {string|string[]} [options.users] List of user IDs to default use for display. Defaults to all users.
|
|
22
|
-
* @param {string|object} [options.style] The map's MapLibre style. This can be an a JSON object conforming to the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-gl-js-docs/style-spec/), or a URL string pointing to one. Defaults to OSMFR vector tiles.
|
|
23
|
-
*
|
|
24
|
-
* @property {object} _t The translations labels
|
|
25
|
-
* @property {string} _selectedSeqId The selected sequence ID
|
|
26
|
-
* @property {string} _selectedPicId The selected picture ID
|
|
27
|
-
* @property {API} _api The API handler
|
|
28
|
-
* @property {Loader} _loader The initial loader message
|
|
29
|
-
* @property {object} _options The stored options
|
|
30
|
-
* @property {Element} container The DOM container
|
|
31
|
-
*/
|
|
32
|
-
export default class CoreView extends EventTarget {
|
|
33
|
-
constructor(container, endpoint, options = {}) {
|
|
34
|
-
super();
|
|
35
|
-
|
|
36
|
-
this._options = options;
|
|
37
|
-
if(this._options == null) { this._options = {}; }
|
|
38
|
-
if(!this._options.users) { this._options.users = ["geovisio"]; }
|
|
39
|
-
if(typeof this._options.users === "string") { this._options.users = [this._options.users]; }
|
|
40
|
-
if(!this._options.style) { this._options.style = DEFAULT_TILES; }
|
|
41
|
-
|
|
42
|
-
if(!this._options.testing) {
|
|
43
|
-
// Display version in logs
|
|
44
|
-
console.info(`📷 Panoramax ${this.getClassName()} - Version ${PACKAGE_JSON.version} (${__COMMIT_HASH__})
|
|
45
|
-
|
|
46
|
-
🆘 Issues can be reported at ${PACKAGE_JSON.repository.url}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Translations
|
|
50
|
-
this._t = getTranslations(this._options.lang);
|
|
51
|
-
|
|
52
|
-
// Internet speed check
|
|
53
|
-
this._isInternetFast = null;
|
|
54
|
-
isInternetFast().then(isFast => this._isInternetFast = isFast);
|
|
55
|
-
|
|
56
|
-
// Selected IDs
|
|
57
|
-
this._selectedSeqId = this._options.selectedSequence || null;
|
|
58
|
-
this._selectedPicId = this._options.selectedPicture || null;
|
|
59
|
-
|
|
60
|
-
// Container init
|
|
61
|
-
this.container = typeof container === "string" ? document.getElementById(container) : container;
|
|
62
|
-
if(!(this.container instanceof Element)) { throw new Error("Container is not a valid HTML element, does it exist in your page ?"); }
|
|
63
|
-
this.container.classList.add("gvs", `gvs-${this.getClassName().toLocaleLowerCase()}`);
|
|
64
|
-
if(isInIframe()) { this.container.classList.add("gvs-iframed"); }
|
|
65
|
-
|
|
66
|
-
// Loader init
|
|
67
|
-
this.loaderContainer = document.createElement("div");
|
|
68
|
-
this.container.appendChild(this.loaderContainer);
|
|
69
|
-
this._loader = new Loader(this, this.loaderContainer);
|
|
70
|
-
|
|
71
|
-
// API init)
|
|
72
|
-
endpoint = (endpoint || "").replace("/api/search", "/api");
|
|
73
|
-
try {
|
|
74
|
-
this._api = new API(endpoint, {
|
|
75
|
-
users: this._options.users,
|
|
76
|
-
fetch: this._options?.fetchOptions,
|
|
77
|
-
style: this._options.style,
|
|
78
|
-
});
|
|
79
|
-
this._api.onceReady()
|
|
80
|
-
.then(() => {
|
|
81
|
-
let unavailable = this._api.getUnavailableFeatures();
|
|
82
|
-
let available = this._api.getAvailableFeatures();
|
|
83
|
-
available = unavailable.length === 0 ? "✅ All features available" : "✅ Available features: "+available.join(", ");
|
|
84
|
-
unavailable = unavailable.length === 0 ? "" : "🚫 Unavailable features: "+unavailable.join(", ");
|
|
85
|
-
console.info(`🌐 Connected to API "${this._api._metadata.name}" (${this._api._endpoint})
|
|
86
|
-
ℹ️ API runs STAC ${this._api._metadata.stac_version} ${this._api._metadata.geovisio_version ? "& GeoVisio "+this._api._metadata.geovisio_version : ""}
|
|
87
|
-
${available}
|
|
88
|
-
${unavailable}
|
|
89
|
-
`.trim());
|
|
90
|
-
})
|
|
91
|
-
.catch(e => this._loader.dismiss(e, this._t.gvs.error_api));
|
|
92
|
-
}
|
|
93
|
-
catch(e) {
|
|
94
|
-
this._loader.dismiss(e, this._t.gvs.error_api);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* This allows to retrieve an always correct class name.
|
|
100
|
-
* This is crap, but avoids issues with Webpack & so on.
|
|
101
|
-
*
|
|
102
|
-
* Each inheriting class must override this method.
|
|
103
|
-
*/
|
|
104
|
-
getClassName() {
|
|
105
|
-
return "CoreView";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Ends all form of life in this object.
|
|
110
|
-
*
|
|
111
|
-
* This is useful for Single Page Applications (SPA), to remove various event listeners.
|
|
112
|
-
*/
|
|
113
|
-
destroy() {
|
|
114
|
-
delete this._options;
|
|
115
|
-
delete this._t;
|
|
116
|
-
delete this._api;
|
|
117
|
-
delete this._loader;
|
|
118
|
-
this.loaderContainer.remove();
|
|
119
|
-
delete this.loaderContainer;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Is the view running in a small container (small embed or smartphone)
|
|
124
|
-
* @returns {boolean} True if container is small
|
|
125
|
-
*/
|
|
126
|
-
isWidthSmall() {
|
|
127
|
-
return this.container?.offsetWidth < 576;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Is the view running in a small-height container (small embed or smartphone)
|
|
132
|
-
* @returns {boolean} True if container height is small
|
|
133
|
-
*/
|
|
134
|
-
isHeightSmall() {
|
|
135
|
-
return this.container?.offsetHeight < 400;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Change the currently picture and/or sequence.
|
|
140
|
-
* Calling the method without parameters unselects.
|
|
141
|
-
* @param {string} [seqId] The sequence UUID
|
|
142
|
-
* @param {string} [picId] The picture UUID
|
|
143
|
-
* @param {boolean} [force=false] Force select even if already selected
|
|
144
|
-
*/
|
|
145
|
-
select(seqId = null, picId = null, force = false) {
|
|
146
|
-
if(picId === BASE_PANORAMA_ID) { picId = null; }
|
|
147
|
-
const prevSeqId = this._selectedSeqId || null;
|
|
148
|
-
const prevPicId = this._selectedPicId || null;
|
|
149
|
-
if(!force && prevPicId == picId && prevSeqId == seqId) { return; } // Avoid running if already selected
|
|
150
|
-
|
|
151
|
-
this._selectedSeqId = seqId;
|
|
152
|
-
this._selectedPicId = picId;
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Event for sequence/picture selection
|
|
156
|
-
*
|
|
157
|
-
* @event select
|
|
158
|
-
* @memberof CoreView
|
|
159
|
-
* @type {object}
|
|
160
|
-
* @property {object} detail Event information
|
|
161
|
-
* @property {string} detail.seqId The selected sequence ID
|
|
162
|
-
* @property {string} detail.picId The selected picture ID (or null if not a precise picture clicked)
|
|
163
|
-
* @property {string} [detail.prevSeqId] The previously selected sequence ID (or null if none)
|
|
164
|
-
* @property {string} [detail.prevPicId] The previously selected picture ID (or null if none)
|
|
165
|
-
*/
|
|
166
|
-
this.dispatchEvent(new CustomEvent("select", {
|
|
167
|
-
detail: {
|
|
168
|
-
seqId,
|
|
169
|
-
picId,
|
|
170
|
-
prevSeqId,
|
|
171
|
-
prevPicId,
|
|
172
|
-
}
|
|
173
|
-
}));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
.gvs .gvs-loader {
|
|
2
|
-
position: relative;
|
|
3
|
-
width: 100%;
|
|
4
|
-
height: 100%;
|
|
5
|
-
z-index: 0;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.gvs .gvs-loader.gvs-loader-visible {
|
|
9
|
-
visibility: visible;
|
|
10
|
-
opacity: 1;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/* Loader overlay */
|
|
14
|
-
.gvs .gvs-loader {
|
|
15
|
-
visibility: hidden;
|
|
16
|
-
position: absolute;
|
|
17
|
-
top: 0;
|
|
18
|
-
right: 0;
|
|
19
|
-
left: 0;
|
|
20
|
-
bottom: 0;
|
|
21
|
-
opacity: 0;
|
|
22
|
-
display: flex;
|
|
23
|
-
flex-direction: column;
|
|
24
|
-
justify-content: center;
|
|
25
|
-
gap: 20px;
|
|
26
|
-
align-items: center;
|
|
27
|
-
background: #37474F;
|
|
28
|
-
z-index: 9000;
|
|
29
|
-
transition: all 0.5s;
|
|
30
|
-
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
|
31
|
-
font-weight: 550;
|
|
32
|
-
color: white;
|
|
33
|
-
font-size: 1.4em;
|
|
34
|
-
text-align: center;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.gvs .gvs-loader a {
|
|
38
|
-
color: white !important;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* Flashing text */
|
|
42
|
-
.gvs .gvs-loader > div > span {
|
|
43
|
-
animation: blinker 2s linear infinite;
|
|
44
|
-
}
|
|
45
|
-
@keyframes blinker { 50% { opacity: 0.3; } }
|
|
46
|
-
|
|
47
|
-
/* Rotating logo */
|
|
48
|
-
.gvs .gvs-loader .gvs-loader-img {
|
|
49
|
-
width: 150px;
|
|
50
|
-
animation: rotating 1.4s linear infinite;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@keyframes rotating {
|
|
54
|
-
from { transform: rotate(0deg); }
|
|
55
|
-
to { transform: rotate(360deg); }
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/* Call to action button */
|
|
59
|
-
.gvs .gvs-loader .gvs-loader-cta {
|
|
60
|
-
border: none;
|
|
61
|
-
border-radius: 7px;
|
|
62
|
-
font-weight: 500;
|
|
63
|
-
font-size: 1.0em;
|
|
64
|
-
color: white;
|
|
65
|
-
background-color: #EF6C00;
|
|
66
|
-
cursor: pointer;
|
|
67
|
-
margin: 10px;
|
|
68
|
-
padding: 5px 30px;
|
|
69
|
-
box-shadow: 2px 2px 2px rgba(0,0,0,0.3);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.gvs .gvs-loader .gvs-loader-cta:hover {
|
|
73
|
-
background-color: #F57C00;
|
|
74
|
-
}
|
package/src/components/Loader.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import "./Loader.css";
|
|
2
|
-
import LogoDead from "../img/logo_dead.svg";
|
|
3
|
-
import LoaderImg from "../img/marker.svg";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Loader is a full-screen loading message.
|
|
7
|
-
* @private
|
|
8
|
-
*
|
|
9
|
-
* @param {CoreView} parent The parent view
|
|
10
|
-
* @param {Element} container The DOM element to create loader into
|
|
11
|
-
*/
|
|
12
|
-
export default class Loader {
|
|
13
|
-
constructor(parent, container) {
|
|
14
|
-
this._parent = parent;
|
|
15
|
-
this.container = container;
|
|
16
|
-
this.container.classList.add("gvs-loader", "gvs-loader-visible");
|
|
17
|
-
|
|
18
|
-
// Logo
|
|
19
|
-
const logo = document.createElement("img");
|
|
20
|
-
logo.src = LoaderImg;
|
|
21
|
-
logo.alt = "";
|
|
22
|
-
logo.title = this._parent._t.map.loading;
|
|
23
|
-
logo.classList.add("gvs-loader-img");
|
|
24
|
-
this.container.appendChild(logo);
|
|
25
|
-
|
|
26
|
-
// Label (1 serious, then fun ones)
|
|
27
|
-
const labelWrapper = document.createElement("div");
|
|
28
|
-
const label = document.createElement("span");
|
|
29
|
-
const nextLabelFun = () => (
|
|
30
|
-
this._parent._t.gvs.loading_labels_fun[
|
|
31
|
-
Math.floor(Math.random() * this._parent._t.gvs.loading_labels_fun.length)
|
|
32
|
-
]
|
|
33
|
-
);
|
|
34
|
-
label.innerHTML = this._parent._t.gvs.loading_labels_serious[
|
|
35
|
-
Math.floor(Math.random() * this._parent._t.gvs.loading_labels_serious.length)
|
|
36
|
-
];
|
|
37
|
-
const nextLabelFct = () => setTimeout(() => {
|
|
38
|
-
label.innerHTML = nextLabelFun();
|
|
39
|
-
this._loaderLabelChanger = nextLabelFct();
|
|
40
|
-
}, 500 + Math.random() * 1000);
|
|
41
|
-
this._loaderLabelChanger = nextLabelFct();
|
|
42
|
-
labelWrapper.appendChild(label);
|
|
43
|
-
|
|
44
|
-
this.container.appendChild(labelWrapper);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Is the loader visible ?
|
|
49
|
-
* @returns {boolean} True if visible
|
|
50
|
-
*/
|
|
51
|
-
isVisible() {
|
|
52
|
-
return this.container.classList.contains("gvs-loader-visible");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Dismiss loader, or show error
|
|
57
|
-
* @param {object} [err] Optional error object to show in browser console
|
|
58
|
-
* @param {str} [errMeaningful] Optional error message to show to user
|
|
59
|
-
* @param {fct} [next] Optional function to run after loader dismiss
|
|
60
|
-
*/
|
|
61
|
-
dismiss(err = null, errMeaningful = null, next = null) {
|
|
62
|
-
clearTimeout(this._loaderLabelChanger);
|
|
63
|
-
|
|
64
|
-
if(!err) {
|
|
65
|
-
this.container.classList.remove("gvs-loader-visible");
|
|
66
|
-
setTimeout(() => this.container.remove(), 2000);
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Event for viewer being ready to use (API loaded)
|
|
70
|
-
*
|
|
71
|
-
* @event ready
|
|
72
|
-
* @memberof CoreView
|
|
73
|
-
*/
|
|
74
|
-
const readyEvt = new CustomEvent("ready");
|
|
75
|
-
this._parent.dispatchEvent(readyEvt);
|
|
76
|
-
|
|
77
|
-
if(next) { next(); }
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
if(err !== true) { console.error(err); }
|
|
81
|
-
|
|
82
|
-
// Change content
|
|
83
|
-
this.container.children[0].src = LogoDead;
|
|
84
|
-
this.container.children[0].style.width = "200px";
|
|
85
|
-
this.container.children[0].style.animation = "unset";
|
|
86
|
-
|
|
87
|
-
let errHtml;
|
|
88
|
-
if(next) {
|
|
89
|
-
errHtml = `<button class="gvs-loader-cta">${this._parent._t.gvs.error_click}</button>`;
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
errHtml = `<small>${this._parent._t.gvs.error_retry}</small>`;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if(errMeaningful) { errHtml = errMeaningful + "<br />" + errHtml; }
|
|
96
|
-
this.container.children[1].innerHTML = `${this._parent._t.gvs.error}<br />${errHtml}`;
|
|
97
|
-
if(next) {
|
|
98
|
-
this.container.addEventListener("click", next);
|
|
99
|
-
}
|
|
100
|
-
const errLabel = errMeaningful || "Panoramax JS had a blocking exception";
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Event for viewer failing to initially load
|
|
104
|
-
*
|
|
105
|
-
* @event broken
|
|
106
|
-
* @memberof CoreView
|
|
107
|
-
* @type {object}
|
|
108
|
-
* @property {object} detail Event information
|
|
109
|
-
* @property {string} detail.error The user-friendly error message to display
|
|
110
|
-
*/
|
|
111
|
-
const brokenEvt = new CustomEvent("broken", {
|
|
112
|
-
detail: { error: errLabel }
|
|
113
|
-
});
|
|
114
|
-
this._parent.dispatchEvent(brokenEvt);
|
|
115
|
-
|
|
116
|
-
// Throw error
|
|
117
|
-
throw new Error(errLabel);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
package/src/img/loader_hd.jpg
DELETED
|
Binary file
|
package/src/utils/Exif.js
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read float value from EXIF tags (to handle fractions & all)
|
|
3
|
-
* @param {*} val The input EXIF tag value
|
|
4
|
-
* @returns {number|undefined} The parsed value, or undefined if value is not readable
|
|
5
|
-
* @private
|
|
6
|
-
*/
|
|
7
|
-
export function getExifFloat(val) {
|
|
8
|
-
// Null-like values
|
|
9
|
-
if(
|
|
10
|
-
[null, undefined, ""].includes(val)
|
|
11
|
-
|| typeof val === "string" && val.trim() === ""
|
|
12
|
-
) {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
// Already valid number
|
|
16
|
-
else if(typeof val === "number") {
|
|
17
|
-
return val;
|
|
18
|
-
}
|
|
19
|
-
// String
|
|
20
|
-
else if(typeof val === "string") {
|
|
21
|
-
// Check if looks like a fraction
|
|
22
|
-
if(/^-?\d+(\.\d+)?\/-?\d+(\.\d+)?$/.test(val)) {
|
|
23
|
-
const parts = val.split("/").map(p => parseFloat(p));
|
|
24
|
-
return parts[0] / parts[1];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Try a direct cast to float
|
|
28
|
-
try { return parseFloat(val); }
|
|
29
|
-
catch(e) {} // eslint-disable-line no-empty
|
|
30
|
-
|
|
31
|
-
// Unrecognized
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
else { return undefined; }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Find in picture metadata the GPS precision.
|
|
39
|
-
* @param {object} picture The GeoJSON picture feature
|
|
40
|
-
* @returns {string} The precision value (poor, fair, moderate, good, excellent, ideal, unknown)
|
|
41
|
-
* @private
|
|
42
|
-
*/
|
|
43
|
-
export function getGPSPrecision(picture) {
|
|
44
|
-
let quality = "❓";
|
|
45
|
-
const gpsHPosError = picture?.properties?.["quality:horizontal_accuracy"] || getExifFloat(picture?.properties?.exif?.["Exif.GPSInfo.GPSHPositioningError"]);
|
|
46
|
-
const gpsDop = getExifFloat(picture?.properties?.exif?.["Exif.GPSInfo.GPSDOP"]);
|
|
47
|
-
|
|
48
|
-
if(gpsHPosError !== undefined) {
|
|
49
|
-
quality = `${gpsHPosError} m`;
|
|
50
|
-
}
|
|
51
|
-
else if(gpsDop !== undefined) {
|
|
52
|
-
if(gpsDop < 1) { quality = "ideal"; }
|
|
53
|
-
else if(gpsDop < 2) { quality = "excellent"; }
|
|
54
|
-
else if(gpsDop < 5) { quality = "good"; }
|
|
55
|
-
else if(gpsDop < 10) { quality = "moderate"; }
|
|
56
|
-
else if(gpsDop < 20) { quality = "fair"; }
|
|
57
|
-
else { quality = "poor"; }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return quality;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Compute PSV sphere correction based on picture metadata & EXIF tags.
|
|
65
|
-
* @param {object} picture The GeoJSON picture feature
|
|
66
|
-
* @returns {object} The PSV sphereCorrection value
|
|
67
|
-
* @private
|
|
68
|
-
*/
|
|
69
|
-
export function getSphereCorrection(picture) {
|
|
70
|
-
// Photo direction
|
|
71
|
-
let dir = picture.properties?.["view:azimuth"];
|
|
72
|
-
if(dir === undefined) {
|
|
73
|
-
const v = getExifFloat(picture.properties?.exif?.["Exif.GPSInfo.GPSImgDirection"]);
|
|
74
|
-
if(v !== undefined) {
|
|
75
|
-
dir = v;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
dir = dir || 0;
|
|
79
|
-
|
|
80
|
-
// Yaw
|
|
81
|
-
let yaw = picture.properties?.["pers:yaw"];
|
|
82
|
-
let exifFallbacks = ["Xmp.GPano.PoseHeadingDegrees", "Xmp.Camera.Yaw", "Exif.MpfInfo.MPFYawAngle"];
|
|
83
|
-
if(yaw === undefined) {
|
|
84
|
-
for(let exif of exifFallbacks) {
|
|
85
|
-
const v = getExifFloat(picture.properties?.exif?.[exif]);
|
|
86
|
-
if(v !== undefined) {
|
|
87
|
-
yaw = v;
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
yaw = yaw || 0;
|
|
93
|
-
|
|
94
|
-
// Check if yaw is applicable: different from photo direction
|
|
95
|
-
if(Math.round(dir) === Math.round(yaw) && yaw > 0) {
|
|
96
|
-
console.warn("Picture with UUID", picture.id, "has same GPS Image direction and Yaw, could cause rendering issues");
|
|
97
|
-
// yaw = 0;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Pitch
|
|
101
|
-
let pitch = picture.properties?.["pers:pitch"];
|
|
102
|
-
exifFallbacks = ["Xmp.GPano.PosePitchDegrees", "Xmp.Camera.Pitch", "Exif.MpfInfo.MPFPitchAngle"];
|
|
103
|
-
if(pitch === undefined) {
|
|
104
|
-
for(let exif of exifFallbacks) {
|
|
105
|
-
const v = getExifFloat(picture.properties?.exif?.[exif]);
|
|
106
|
-
if(v !== undefined) {
|
|
107
|
-
pitch = v;
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
pitch = pitch || 0;
|
|
113
|
-
|
|
114
|
-
// Roll
|
|
115
|
-
let roll = picture.properties?.["pers:roll"];
|
|
116
|
-
exifFallbacks = ["Xmp.GPano.PoseRollDegrees", "Xmp.Camera.Roll", "Exif.MpfInfo.MPFRollAngle"];
|
|
117
|
-
if(roll === undefined) {
|
|
118
|
-
for(let exif of exifFallbacks) {
|
|
119
|
-
const v = getExifFloat(picture.properties?.exif?.[exif]);
|
|
120
|
-
if(v !== undefined) {
|
|
121
|
-
roll = v;
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
roll = roll || 0;
|
|
127
|
-
|
|
128
|
-
// Send result
|
|
129
|
-
return pitch !== 0 && roll !== 0 ? {
|
|
130
|
-
pan: yaw * Math.PI / 180,
|
|
131
|
-
tilt: pitch * Math.PI / 180,
|
|
132
|
-
roll: roll * Math.PI / 180,
|
|
133
|
-
} : {};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Compute PSV panoData for cropped panorama based on picture metadata & EXIF tags.
|
|
138
|
-
* @param {object} picture The GeoJSON picture feature
|
|
139
|
-
* @returns {object} The PSV panoData values
|
|
140
|
-
* @private
|
|
141
|
-
*/
|
|
142
|
-
export function getCroppedPanoData(picture) {
|
|
143
|
-
let res;
|
|
144
|
-
|
|
145
|
-
if(picture.properties?.["pers:interior_orientation"]) {
|
|
146
|
-
if(
|
|
147
|
-
picture.properties["pers:interior_orientation"]?.["visible_area"]
|
|
148
|
-
&& picture.properties["pers:interior_orientation"]?.["sensor_array_dimensions"]
|
|
149
|
-
) {
|
|
150
|
-
const va = picture.properties["pers:interior_orientation"]["visible_area"];
|
|
151
|
-
const sad = picture.properties["pers:interior_orientation"]["sensor_array_dimensions"];
|
|
152
|
-
try {
|
|
153
|
-
res = {
|
|
154
|
-
fullWidth: parseInt(sad[0]),
|
|
155
|
-
fullHeight: parseInt(sad[1]),
|
|
156
|
-
croppedX: parseInt(va[0]),
|
|
157
|
-
croppedY: parseInt(va[1]),
|
|
158
|
-
croppedWidth: parseInt(sad[0]) - parseInt(va[2]) - parseInt(va[0]),
|
|
159
|
-
croppedHeight: parseInt(sad[1]) - parseInt(va[3]) - parseInt(va[1]),
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
catch(e) {
|
|
163
|
-
console.warn("Invalid pers:interior_orientation values for cropped panorama "+picture.id);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if(!res && picture.properties?.exif) {
|
|
169
|
-
try {
|
|
170
|
-
res = {
|
|
171
|
-
fullWidth: parseInt(picture.properties.exif?.["Xmp.GPano.FullPanoWidthPixels"]),
|
|
172
|
-
fullHeight: parseInt(picture.properties.exif?.["Xmp.GPano.FullPanoHeightPixels"]),
|
|
173
|
-
croppedX: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaLeftPixels"]),
|
|
174
|
-
croppedY: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaTopPixels"]),
|
|
175
|
-
croppedWidth: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaImageWidthPixels"]),
|
|
176
|
-
croppedHeight: parseInt(picture.properties.exif?.["Xmp.GPano.CroppedAreaImageHeightPixels"]),
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
catch(e) {
|
|
180
|
-
console.warn("Invalid XMP.GPano values for cropped panorama "+picture.id);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Check if crop is really necessary
|
|
185
|
-
if(res) {
|
|
186
|
-
res = Object.fromEntries(Object.entries(res || {}).filter(e => !isNaN(e[1])));
|
|
187
|
-
if(res.fullWidth == res.croppedWidth && res.fullHeight == res.croppedHeight) {
|
|
188
|
-
res = {};
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return res || {};
|
|
193
|
-
}
|