From 5b7223e63c71c3a0b5956cbaa3151ec195e28850 Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 23 Nov 2023 12:07:40 +1000 Subject: [PATCH 01/19] Enable typescript linting --- .eslintrc | 312 ++++++++++++++++++++------------------------------- gulpfile.js | 4 + package.json | 3 + 3 files changed, 127 insertions(+), 192 deletions(-) diff --git a/.eslintrc b/.eslintrc index c0d2b2cc5f3..0499cb5e57d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,11 @@ { - "extends": ["eslint:recommended", "plugin:react/recommended"], - "parser": "@babel/eslint-parser", + "root": true, + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended" + ], + "parser": "@typescript-eslint/parser", "parserOptions": { "requireConfigFile": false, "ecmaVersion": 2018, @@ -15,7 +20,7 @@ "commonjs": true, "es6": true }, - "plugins": ["react"], + "plugins": ["react", "react-hooks", "@typescript-eslint"], "globals": { "process": true }, @@ -25,113 +30,65 @@ } }, "rules": { - "react/jsx-no-undef": 0, + "jsx-control-statements/jsx-use-if-tag": "off", + "jsx-control-statements/jsx-for-require-body": "off", + "react-hooks/exhaustive-deps": "error", + "react/jsx-boolean-value": ["error", "never", { "always": [] }], + "react/jsx-closing-bracket-location": ["error", "line-aligned"], + "react/jsx-closing-tag-location": "error", + "react/jsx-curly-spacing": ["error", "never", { "allowMultiline": true }], + "react/no-arrow-function-lifecycle": "error", + "react/no-invalid-html-attribute": "error", + "react/jsx-no-useless-fragment": "error", + "react/jsx-no-constructed-context-values": "error", + "react/jsx-fragments": ["error", "syntax"], + "react/jsx-no-duplicate-props": ["error", { "ignoreCase": true }], + "react/jsx-pascal-case": [ + "error", + { + "allowAllCaps": true, + "ignore": [] + } + ], + "react/no-danger": "warn", + "react/no-did-update-set-state": "error", + "react/no-will-update-set-state": "error", + "react/self-closing-comp": "error", + "react/jsx-no-undef": ["error", { "allowGlobals": true }], /*Possible Errors */ - "no-cond-assign": [1, "except-parens"], - "no-console": 0, - "no-constant-condition": 1, - "no-control-regex": 1, - "no-debugger": 1, - "no-dupe-args": 1, - "no-dupe-keys": 1, - "no-duplicate-case": 0, - "no-empty-character-class": 1, - "no-empty": 0, - "no-ex-assign": 1, - "no-extra-boolean-cast": 1, - "no-extra-parens": 0, - "no-extra-semi": 1, - "no-func-assign": 1, + "no-console": "off", "no-inner-declarations": [1, "functions"], - "no-invalid-regexp": 1, - "no-irregular-whitespace": 1, - "no-negated-in-lhs": 1, - "no-obj-calls": 1, - "no-regex-spaces": 1, - "no-reserved-keys": 0, - "no-sparse-arrays": 1, - "no-unexpected-multiline": 1, - "no-unreachable": 1, - "use-isnan": 1, - "valid-jsdoc": 0, - "valid-typeof": 1, /* Best Practices */ - "accessor-pairs": 0, - "block-scoped-var": 0, // see Babel section - "complexity": 0, - "consistent-return": 0, - "curly": 0, - "default-case": 0, - "dot-notation": [ - 0, - { - "allowKeywords": true, - "allowPattern": "" - } - ], - "dot-location": [1, "property"], - "eqeqeq": 1, - "guard-for-in": 0, - "no-alert": 1, - "no-caller": 1, - "no-div-regex": 1, - "no-else-return": 0, - "no-eq-null": 0, - "no-eval": 1, - "no-extend-native": 1, - "no-extra-bind": 0, - "no-fallthrough": 0, - "no-floating-decimal": 1, - "no-implied-eval": 1, - "no-iterator": 1, - "no-labels": 1, - "no-lone-blocks": 1, - "no-loop-func": 1, - "no-multi-spaces": 0, - "no-multi-str": 0, - "no-native-reassign": 1, - "no-new-func": 1, - "no-new-wrappers": 1, - "no-new": 1, - "no-octal-escape": 1, - "no-octal": 1, - "no-param-reassign": 0, - "no-process-env": 0, - "no-proto": 1, - "no-redeclare": 1, - "no-return-assign": 1, - "no-script-url": 1, - "no-self-compare": 0, - "no-sequences": 1, - "no-throw-literal": 0, - "no-unused-expressions": 0, - "no-void": 0, - "no-warning-comments": [ - 0, - { - "terms": ["todo", "tofix"], - "location": "start" - } - ], - "no-with": 1, - "radix": 1, - "vars-on-top": 0, + "no-alert": ["error"], + "no-caller": ["error"], + "no-div-regex": ["error"], + "no-eval": ["error"], + "no-extend-native": ["error"], + "no-fallthrough": 0, // + "no-floating-decimal": ["error"], + "no-implied-eval": ["error"], + "no-iterator": ["error"], + "no-labels": ["error"], + "no-lone-blocks": ["error"], + "no-loop-func": ["error"], + "no-new-func": ["error"], + "no-new-wrappers": ["error"], + "no-new": ["error"], + "no-octal-escape": ["error"], + "no-proto": ["error"], + "no-return-assign": ["error"], + "no-script-url": ["error"], + "no-sequences": ["error"], + "radix": "error", "wrap-iife": [1, "inside"], - "yoda": [0, "never"], /* Strict Mode */ "strict": [0, "global"], /* Variables */ - "no-catch-shadow": 0, - "no-delete-var": 1, "no-label-var": 1, - "no-shadow-restricted-names": 1, - "no-shadow": 0, - "no-undef-init": 1, - "no-undefined": 0, "no-unused-vars": [ 1, { @@ -139,107 +96,78 @@ "args": "none" } ], - "no-use-before-define": 0, - - /* Node.js */ - "handle-callback-err": 0, - "no-mixed-requires": 1, - "no-new-require": 1, - "no-path-concat": 1, - "no-process-exit": 1, - "no-restricted-modules": [1, ""], // add any unwanted Node.js core modules - "no-sync": 1, - /* Stylistic Issues */ - "array-bracket-spacing": [0, "never"], - "brace-style": [ - 0, - "1tbs", - { - "allowSingleLine": true - } - ], "camelcase": [ 0, { "properties": "always" } ], - "comma-spacing": [ - 0, - { - "before": false, - "after": true - } - ], - "comma-style": [1, "last"], - "comma-dangle": 0, - "computed-property-spacing": 0, - "consistent-this": 0, - "eol-last": 0, - "func-names": 0, - "func-style": 0, - "indent": [0, 4, { "SwitchCase": 1 }], - "key-spacing": [ - 0, - { - "beforeColon": false, - "afterColon": true - } - ], - "linebreak-style": 0, - "max-nested-callbacks": [0, 3], - "new-cap": 0, // see Babel section - "new-parens": 1, - "newline-after-var": 0, - "no-array-constructor": 1, - "no-continue": 0, - "no-inline-comments": 0, - "no-lonely-if": 0, - "no-mixed-spaces-and-tabs": 0, - "no-multiple-empty-lines": [ - 0, - { - "max": 1 - } - ], - "no-nested-ternary": 0, + "no-array-constructor": "error", "no-new-object": 1, - "no-spaced-func": 0, - "no-ternary": 0, - "no-trailing-spaces": 0, - "no-underscore-dangle": 0, - "no-unneeded-ternary": 1, - "object-curly-spacing": 0, // see Babel section - "one-var": [0, "never"], - "padded-blocks": [0, "never"], - "quote-props": [0, "as-needed"], - "quotes": [0, "single"], - "semi-spacing": [ - 1, - { - "before": false, - "after": true - } - ], - "semi": [1, "always"], - "sort-vars": 0, - "space-after-keywords": 0, - "space-before-blocks": [0, "always"], - "space-before-function-paren": [0, "never"], - "space-in-parens": [0, "never"], - "space-infix-ops": 0, - "space-unary-ops": 0, - "spaced-comment": [0, "always"], - "wrap-regex": 0, + "no-unneeded-ternary": 1 /* ECMAScript 6 */, + "prefer-const": "error" + }, + "overrides": [ + { + "files": ["**/*.ts", "**/*.tsx"], + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaFeatures": { + "jsx": true, + "modules": true, + "legacyDecorators": true + }, + "ecmaVersion": 2018, + "sourceType": "module", + "project": "./tsconfig.json" + }, + "plugins": ["react", "jsx-control-statements", "@typescript-eslint"], + "rules": { + "jsx-control-statements/jsx-jcs-no-undef": "off", + "jsx-control-statements/jsx-use-if-tag": "off", + "jsx-control-statements/jsx-for-require-body": "off", + "react-hooks/exhaustive-deps": "error", + "react/jsx-boolean-value": ["error", "never", { "always": [] }], + "react/jsx-closing-bracket-location": ["error", "line-aligned"], + "react/jsx-closing-tag-location": "error", + "react/jsx-curly-spacing": [ + "error", + "never", + { "allowMultiline": true } + ], + "react/no-arrow-function-lifecycle": "error", + "react/no-invalid-html-attribute": "error", + "react/jsx-no-useless-fragment": "error", + "react/jsx-no-constructed-context-values": "error", + "react/jsx-fragments": ["error", "syntax"], + "react/jsx-no-duplicate-props": ["error", { "ignoreCase": true }], + "react/jsx-pascal-case": [ + "error", + { + "allowAllCaps": true, + "ignore": [] + } + ], + "react/no-danger": "warn", + "react/no-did-update-set-state": "error", + "react/no-will-update-set-state": "error", + "react/self-closing-comp": "error", + "react/jsx-no-undef": ["error", { "allowGlobals": true }], + "react/prop-types": "off", - /* ECMAScript 6 */ - "constructor-super": 1, - "generator-star-spacing": 0, // see Babel section - "no-this-before-super": 1, - "no-var": 0, - "object-shorthand": 0, // see Babel section - "prefer-const": 1, - "no-useless-escape": 0 - } + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-this-alias": "off" + } + } + ] } diff --git a/gulpfile.js b/gulpfile.js index 1389dac0c91..a4d9e40c1f3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -52,6 +52,10 @@ gulp.task("lint", function (done) { ".jsx", "--ext", ".js", + "--ext", + ".ts", + "--ext", + ".tsx", "--ignore-pattern", "lib/ThirdParty", "--max-warnings", diff --git a/package.json b/package.json index 8b580324a8b..a8c9166a5a5 100644 --- a/package.json +++ b/package.json @@ -192,9 +192,12 @@ "@types/dateformat": "^3.0.1", "@types/node": "^18.15.11", "@types/webpack": "4.41.33", + "@typescript-eslint/eslint-plugin": "^6.12.0", + "@typescript-eslint/parser": "^6.12.0", "babel-plugin-styled-components": "^1.10.7", "eslint": "^7.20.0", "eslint-plugin-react": "^7.19.0", + "eslint-plugin-react-hooks": "^4.6.0", "fork-ts-checker-notifier-webpack-plugin": "^6.0.0", "fork-ts-checker-webpack-plugin": "^6.0.0", "glob-all": "^3.0.1", From 2a5929c2518685756ee68273772b3af85b33e40a Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Thu, 23 Nov 2023 16:21:28 +1000 Subject: [PATCH 02/19] Exclude rules where refactor might have side effects --- .eslintrc | 18 ++++++++++-------- gulpfile.js | 8 +------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/.eslintrc b/.eslintrc index 0499cb5e57d..8d5aee913f1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,6 +20,7 @@ "commonjs": true, "es6": true }, + "ignorePatterns": ["*.scss.d.ts"], "plugins": ["react", "react-hooks", "@typescript-eslint"], "globals": { "process": true @@ -30,8 +31,6 @@ } }, "rules": { - "jsx-control-statements/jsx-use-if-tag": "off", - "jsx-control-statements/jsx-for-require-body": "off", "react-hooks/exhaustive-deps": "error", "react/jsx-boolean-value": ["error", "never", { "always": [] }], "react/jsx-closing-bracket-location": ["error", "line-aligned"], @@ -128,12 +127,8 @@ "sourceType": "module", "project": "./tsconfig.json" }, - "plugins": ["react", "jsx-control-statements", "@typescript-eslint"], + "plugins": ["react", "@typescript-eslint"], "rules": { - "jsx-control-statements/jsx-jcs-no-undef": "off", - "jsx-control-statements/jsx-use-if-tag": "off", - "jsx-control-statements/jsx-for-require-body": "off", - "react-hooks/exhaustive-deps": "error", "react/jsx-boolean-value": ["error", "never", { "always": [] }], "react/jsx-closing-bracket-location": ["error", "line-aligned"], "react/jsx-closing-tag-location": "error", @@ -166,7 +161,14 @@ "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-namespace": "off", "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-this-alias": "off" + "@typescript-eslint/no-this-alias": "off", + + // @TODO: enable these rules + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-loss-of-precision": "off", + "@typescript-eslint/no-unsafe-declaration-merging": "off", + "react-hooks/exhaustive-deps": "off" } } ] diff --git a/gulpfile.js b/gulpfile.js index a4d9e40c1f3..9e76c71b0f3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -49,13 +49,7 @@ gulp.task("lint", function (done) { "lib", "test", "--ext", - ".jsx", - "--ext", - ".js", - "--ext", - ".ts", - "--ext", - ".tsx", + ".jsx,.js,.ts,.tsx", "--ignore-pattern", "lib/ThirdParty", "--max-warnings", From 29378b3d38bbab78596e07f8e9a5fa1570ff92ff Mon Sep 17 00:00:00 2001 From: Lawrence Owen Date: Mon, 27 Nov 2023 09:10:07 +1000 Subject: [PATCH 03/19] Fix more lint and ts errors --- doc/js/rewrite-links.js | 4 +- lib/Core/CorsProxy.ts | 8 +- lib/Core/TerriaError.ts | 2 +- lib/Core/animation.ts | 2 +- lib/Core/createDiscreteTimes.ts | 2 +- lib/Core/flattenNested.ts | 4 +- lib/Core/hashFromString.ts | 2 +- lib/Core/injectTerms.ts | 2 +- lib/Core/loadJson.ts | 4 +- lib/Core/markdownToHtml.ts | 12 +- lib/Map/Cesium/CesiumRenderLoopPauser.ts | 6 +- lib/Map/Cesium/CesiumSelectionIndicator.ts | 4 +- .../ProtomapsImageryProvider.ts | 12 +- lib/Map/Leaflet/LeafletSelectionIndicator.ts | 8 +- lib/Map/Leaflet/LeafletVisualizer.ts | 12 +- .../PickedFeatures/featureDataToGeoJson.ts | 2 +- lib/Map/Region/RegionProvider.ts | 6 +- lib/Map/Vector/Reproject.ts | 4 +- lib/Map/Vector/rectangleToLatLngBounds.ts | 8 +- lib/ModelMixins/CatalogMemberMixin.ts | 4 +- lib/ModelMixins/Cesium3dTilesMixin.ts | 4 +- lib/ModelMixins/DiscretelyTimeVaryingMixin.ts | 18 +- lib/ModelMixins/GeojsonMixin.ts | 10 +- lib/ModelMixins/TableMixin.ts | 6 +- lib/Models/CameraView.ts | 2 +- .../YDYRCatalogFunctionJob.ts | 2 +- .../CatalogGroups/OpenDataSoftCatalogGroup.ts | 2 +- .../CatalogGroups/ThreddsCatalogGroup.ts | 2 +- .../CatalogItems/ApiTableCatalogItem.ts | 2 +- .../CatalogItems/CartoMapV3CatalogItem.ts | 10 +- .../Catalog/CatalogItems/GpxCatalogItem.ts | 2 +- .../MapboxVectorTileCatalogItem.ts | 2 +- .../SenapsLocationsCatalogItem.ts | 6 +- .../CatalogReferences/MagdaReference.ts | 2 +- lib/Models/Catalog/Ckan/CkanCatalogGroup.ts | 12 +- lib/Models/Catalog/Ckan/CkanItemReference.ts | 12 +- lib/Models/Catalog/Esri/ArcGisCatalogGroup.ts | 12 +- .../Esri/ArcGisFeatureServerCatalogGroup.ts | 6 +- .../Esri/ArcGisFeatureServerCatalogItem.ts | 2 +- .../Esri/ArcGisMapServerCatalogItem.ts | 10 +- .../Catalog/Esri/ArcGisPortalCatalogGroup.ts | 6 +- .../Catalog/Esri/ArcGisPortalItemReference.ts | 4 +- lib/Models/Catalog/Gltf/AssImpCatalogItem.ts | 4 +- lib/Models/Catalog/Gtfs/GtfsCatalogItem.ts | 12 +- lib/Models/Catalog/Ows/CswCatalogGroup.ts | 6 +- .../SensorObservationServiceCatalogItem.ts | 10 +- .../Ows/WebFeatureServiceCapabilities.ts | 8 +- .../Ows/WebFeatureServiceCatalogItem.ts | 6 +- .../Ows/WebFeatureServiceSearchProvider.ts | 220 ++++++++++++++++++ .../Catalog/Ows/WebMapServiceCapabilities.ts | 4 +- .../Ows/WebMapServiceCapabilitiesStratum.ts | 8 +- .../Catalog/Ows/WebMapServiceCatalogGroup.ts | 2 +- .../Catalog/Ows/WebMapServiceCatalogItem.ts | 2 +- .../Ows/WebMapTileServiceCatalogItem.ts | 17 +- .../WebProcessingServiceCatalogFunction.ts | 10 +- .../SdmxJson/SdmxJsonDataflowStratum.ts | 14 +- .../Catalog/SdmxJson/SdmxJsonServerStratum.ts | 6 +- lib/Models/Catalog/registerCatalogMembers.ts | 2 +- lib/Models/Cesium.ts | 38 +-- lib/Models/Definition/createCombinedModel.ts | 4 +- lib/Models/Definition/upsertModelFromJson.ts | 2 +- .../FunctionParameters/PointParameter.ts | 2 +- .../IndexedItemSearchProvider.ts | 2 +- lib/Models/Leaflet.ts | 4 +- .../AustralianGazetteerSearchProvider.ts | 2 +- lib/Models/Terria.ts | 6 +- lib/Models/TimelineStack.ts | 6 +- lib/Models/UserDrawing.ts | 16 +- lib/ReactViews/.eslintrc | 40 ---- .../Chart/ChartExpandAndDownloadButtons.tsx | 6 +- lib/ReactViews/Custom/Chart/chart.scss.d.ts | 2 +- lib/ReactViews/Custom/Chart/legends.scss.d.ts | 2 +- .../Custom/Collapsible/Collapsible.tsx | 4 +- .../Custom/Collapsible/collapsible.scss.d.ts | 2 +- .../Custom/FeedbackLinkCustomComponent.tsx | 2 +- .../Custom/SOSChartCustomComponent.ts | 2 +- lib/ReactViews/DataCatalog/DataCatalog.jsx | 2 +- .../DataCatalog/data-catalog-item.scss.d.ts | 2 +- .../ExplorerWindow/Tabs/MyDataTab/AddData.jsx | 4 +- .../Tabs/MyDataTab/MyDataTab.jsx | 2 +- .../FeatureInfo/FeatureInfoSection.tsx | 2 +- lib/ReactViews/Feedback/FeedbackForm.tsx | 6 +- lib/ReactViews/Generic/TooltipWrapper.tsx | 2 +- lib/ReactViews/Generic/prompt.scss.d.ts | 2 +- .../Guidance/guidance-dot.scss.d.ts | 2 +- lib/ReactViews/Guidance/guidance.scss.d.ts | 2 +- .../HelpScreens/help-panel.scss.d.ts | 2 +- .../DataAttribution/DataAttributionModal.tsx | 2 +- .../Map/BottomBar/Credits/MapCredits.tsx | 2 +- .../Map/BottomLeftBar/BottomLeftBar.tsx | 4 +- .../Map/MapNavigation/CollapsedNavigation.tsx | 6 +- .../Items/Compass/GyroscopeGuidance.jsx | 114 +++++---- .../Map/MapNavigation/MapNavigation.tsx | 6 +- .../Map/Panels/LangPanel/LangPanel.tsx | 3 +- .../Panels/SharePanel/Print/PrintDatasets.tsx | 2 +- .../Map/Panels/SharePanel/Print/PrintView.tsx | 2 +- .../SharePanel/Print/PrintWorkbench.tsx | 2 +- .../Map/Panels/SharePanel/SharePanel.tsx | 4 +- .../Panels/SharePanel/SharePanelContent.tsx | 2 +- .../Panels/SharePanel/ShareUrl/ShareUrl.tsx | 188 ++++++++------- .../Map/Panels/ToolsPanel/CountDatasets.tsx | 4 +- .../Map/Panels/ToolsPanel/ToolsPanel.jsx | 1 + .../TerriaViewerWrapper/Splitter/dragHook.ts | 4 +- lib/ReactViews/Mobile/MobileHeader.jsx | 4 +- lib/ReactViews/Mobile/MobileMenuItem.tsx | 3 +- lib/ReactViews/Notification/Notification.tsx | 2 +- .../Notification/shareConvertNotification.tsx | 31 +-- .../Notification/terriaErrorNotification.tsx | 22 +- lib/ReactViews/Preview/DataPreviewUrl.jsx | 2 + lib/ReactViews/Preview/Description.jsx | 12 +- lib/ReactViews/Preview/WarningBox.tsx | 2 +- lib/ReactViews/RelatedMaps/RelatedMaps.tsx | 3 +- .../RelatedMaps/related-maps.scss.d.ts | 2 +- .../Search/LocationSearchResults.jsx | 179 ++++++++++++++ lib/ReactViews/Search/search-result.scss.d.ts | 17 ++ .../sidebar-dataset-search-results.scss.d.ts | 2 +- .../Search/sidebar-search.scss.d.ts | 2 +- lib/ReactViews/SelectableDimensions/Color.tsx | 2 +- .../ColorSchemeOptionRenderer.tsx | 2 +- .../MarkerOptionRenderer.tsx | 2 +- .../SelectableDimensions/Select.tsx | 4 +- lib/ReactViews/SidePanel/SidePanel.tsx | 2 +- lib/ReactViews/SidePanel/side-panel.scss.d.ts | 2 +- .../StandardUserInterface/Portal.tsx | 2 +- .../StandardUserInterface.tsx | 4 +- .../customizable/menu-button.scss.d.ts | 2 +- lib/ReactViews/Story/StoryBuilder.tsx | 8 +- lib/ReactViews/Story/StoryPanel/StoryBody.tsx | 35 ++- .../Story/StoryPanel/StoryPanel.tsx | 2 +- lib/ReactViews/Tools/DiffTool/DiffTool.tsx | 10 +- .../Tools/DiffTool/LocationPicker.tsx | 2 +- .../Tools/DiffTool/diff-tool.scss.d.ts | 2 +- .../PedestrianMode/DropPedestrianToGround.tsx | 16 +- .../Tools/PedestrianMode/MovementControls.tsx | 36 ++- lib/ReactViews/Tools/ToolModal.tsx | 4 +- .../WelcomeMessage/welcome-message.scss.d.ts | 2 +- lib/ReactViews/Workbench/Controls/Legend.tsx | 6 +- .../Workbench/Controls/TimerSection.jsx | 36 ++- .../Workbench/Controls/ViewingControls.tsx | 2 +- .../Workbench/PositionRightOfWorkbench.tsx | 4 +- lib/ReactViews/Workbench/WorkbenchButton.tsx | 2 +- lib/ReactViews/Workbench/WorkbenchItem.tsx | 2 +- lib/ReactViews/Workbench/WorkbenchList.tsx | 2 +- .../Workbench/WorkbenchSplitScreen.tsx | 2 +- .../Workflow/SelectableDimensionWorkflow.tsx | 2 +- .../Checkbox/Elements/HiddenCheckbox.tsx | 14 +- lib/Styled/Input.tsx | 2 +- lib/Table/Csv.ts | 2 +- lib/Table/MergedStyleMapLegend.ts | 2 +- lib/Table/TableColorMap.ts | 6 +- lib/Table/TableColumn.ts | 20 +- lib/Table/TableStyle.ts | 6 +- lib/Table/TableStyleMap.ts | 4 +- .../createRegionMappedImageryProvider.ts | 4 +- lib/Table/tableFeatureInfoContext.ts | 4 +- lib/Traits/ArrayNestedStrataMap.ts | 4 +- lib/Traits/Decorators/anyTrait.ts | 2 +- .../Decorators/modelReferenceArrayTrait.ts | 2 +- lib/Traits/Decorators/modelReferenceTrait.ts | 2 +- lib/Traits/Decorators/objectArrayTrait.ts | 2 +- lib/Traits/Decorators/primitiveArrayTrait.ts | 2 +- lib/Traits/Decorators/primitiveTrait.ts | 2 +- lib/Traits/NestedStrataMap.ts | 6 +- lib/Traits/TraitsClasses/GeoJsonTraits.ts | 2 +- lib/ViewModels/WorkbenchItem/Inputs.ts | 3 +- test/Core/TerriaErrorSpec.ts | 2 +- test/Map/ReprojectSpec.ts | 2 +- test/ModelMixins/GltfMixinSpec.ts | 3 +- test/ModelMixins/TableMixinSpec.ts | 2 +- test/ModelMixins/TileErrorHandlerMixinSpec.ts | 76 ++++-- .../CatalogGroups/ThreddsCatalogGroupSpec.ts | 6 +- .../CatalogItems/ApiTableCatalogItemSpec.ts | 8 +- .../Cesium3DTilesCatalogItemSpec.ts | 18 +- .../CatalogItems/CzmlCatalogItemSpec.ts | 6 +- .../CatalogItems/IonImageryCatalogItemSpec.ts | 4 +- .../OpenStreetMapCatalogItemSpec.ts | 4 +- .../CatalogItems/SenapsCatalogItemSpec.ts | 2 +- ...SensorObservationServiceCatalogItemSpec.ts | 10 +- .../Catalog/Ckan/CkanCatalogGroupSpec.ts | 42 ++-- .../Ows/WebMapServiceCatalogItemSpec.ts | 38 ++- .../Ows/WebMapTileServiceCatalogItemSpec.ts | 2 +- .../WebProcessingServiceCatalogGroupSpec.ts | 2 +- .../Catalog/createUrlReferenceFromUrlSpec.ts | 2 +- .../Catalog/esri/ArcGisCatalogGroupSpec.ts | 16 +- .../ArcGisFeatureServerCatalogGroupSpec.ts | 6 +- .../esri/ArcGisMapServerCatalogGroupSpec.ts | 12 +- .../esri/ArcGisPortalCatatalogGroupSpec.ts | 26 +-- .../IndexedItemSearchProviderSpec.ts | 5 +- test/Models/LeafletSpec.ts | 8 +- .../MapNavigation/MapNavigationModelSpec.ts | 6 +- test/Models/MapboxMapCatalogItemSpec.ts | 2 +- test/Models/MapboxStyleCatalogItemSpec.ts | 2 +- test/Models/UserDrawingSpec.ts | 28 +-- .../parseCustomMarkdownToReactTsSpec.ts | 4 +- .../Custom/Chart/ChartCustomComponentSpec.tsx | 12 +- .../DataCatalog/CatalogGroupSpec.tsx | 15 +- test/ReactViews/FeatureInfoPanelSpec.tsx | 2 +- test/ReactViews/FeatureInfoSectionSpec.tsx | 92 ++++---- .../Panels/SharePanel/BuildShareLinkSpec.ts | 6 +- .../ItemSearchTool/ItemSearchToolSpec.tsx | 1 - .../ItemSearchTool/MockSearchableItem.ts | 6 +- .../Tools/ItemSearchTool/SearchFormSpec.tsx | 2 +- test/ReactViews/Tour/TourPortalSpec.tsx | 1 - test/ReactViews/WarningBoxSpec.tsx | 5 +- .../Controls/ChartItemSelectorSpec.tsx | 5 +- .../Workbench/Controls/DateTimePickerSpec.tsx | 2 +- .../Workbench/Controls/LegendSpec.tsx | 1 - .../Controls/ViewingControlsSpec.tsx | 7 +- test/Traits/UrlTraitsSpec.ts | 3 - test/Traits/objectTraitSpec.ts | 2 +- test/Types/FlattenedFromTraitsSpec.ts | 2 +- test/Types/ModelPropertiesFromTraitsSpec.ts | 3 +- test/Types/TypeChecks.ts | 4 +- .../MapNavigation/MapToolbarSpec.ts | 4 +- test/ViewModels/TerriaViewerSpec.ts | 2 - test/ViewModels/ViewingControlsMenuSpec.ts | 3 +- 216 files changed, 1290 insertions(+), 894 deletions(-) create mode 100644 lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts delete mode 100644 lib/ReactViews/.eslintrc create mode 100644 lib/ReactViews/Search/LocationSearchResults.jsx create mode 100644 lib/ReactViews/Search/search-result.scss.d.ts diff --git a/doc/js/rewrite-links.js b/doc/js/rewrite-links.js index 09007119df2..78f2173803b 100644 --- a/doc/js/rewrite-links.js +++ b/doc/js/rewrite-links.js @@ -11,5 +11,7 @@ document.querySelectorAll("a").forEach(function (a) { a.target = "_blank"; a.rel = "noreferrer noopener"; } - } catch {} + } catch { + return; + } }); diff --git a/lib/Core/CorsProxy.ts b/lib/Core/CorsProxy.ts index 365e915eeb2..6231c3925fc 100644 --- a/lib/Core/CorsProxy.ts +++ b/lib/Core/CorsProxy.ts @@ -101,7 +101,7 @@ export default class CorsProxy { } host = host.toLowerCase(); - for (var i = 0; i < domains.length; i++) { + for (let i = 0; i < domains.length; i++) { if (host.match("(^|\\.)" + domains[i] + "$")) { return true; } @@ -123,7 +123,7 @@ export default class CorsProxy { } getProxyBaseURL(proxyFlag: string | undefined) { - var flag = proxyFlag === undefined ? "" : "_" + proxyFlag + "/"; + const flag = proxyFlag === undefined ? "" : "_" + proxyFlag + "/"; return this.baseProxyUrl + flag; } @@ -161,8 +161,8 @@ export default class CorsProxy { return false; } - var uri = new URI(url); - var host = uri.host(); + const uri = new URI(url); + const host = uri.host(); if (host === "") { // do not proxy local files diff --git a/lib/Core/TerriaError.ts b/lib/Core/TerriaError.ts index 8a68f916cf7..0e4b23afd2e 100644 --- a/lib/Core/TerriaError.ts +++ b/lib/Core/TerriaError.ts @@ -304,7 +304,7 @@ export default class TerriaError { /** Has any error in the error tree been raised to the user? */ get raisedToUser() { - return this.flatten().find((error) => error._raisedToUser) ? true : false; + return !!this.flatten().find((error) => error._raisedToUser); } /** Resolve error seveirty */ diff --git a/lib/Core/animation.ts b/lib/Core/animation.ts index 262d04d674e..4a18f7e8322 100644 --- a/lib/Core/animation.ts +++ b/lib/Core/animation.ts @@ -48,7 +48,7 @@ const animationTimeout = ( */ export const animateEnd = (element: Element | null) => { - let timeoutID: ReturnType | undefined = undefined; + const timeoutID: ReturnType | undefined = undefined; return Promise.race([ transitionEnd(element), animationTimeout(timeoutID) diff --git a/lib/Core/createDiscreteTimes.ts b/lib/Core/createDiscreteTimes.ts index d6bfb7ea703..38785f6ca12 100644 --- a/lib/Core/createDiscreteTimes.ts +++ b/lib/Core/createDiscreteTimes.ts @@ -67,7 +67,7 @@ export default function createDiscreteTimesFromIsoSegments( } } - let current = start.clone(); + const current = start.clone(); let count = 0; // Add intervals starting at start until: diff --git a/lib/Core/flattenNested.ts b/lib/Core/flattenNested.ts index bdb27386cf9..091dad03975 100644 --- a/lib/Core/flattenNested.ts +++ b/lib/Core/flattenNested.ts @@ -14,8 +14,8 @@ export default function flattenNested(array: NestedArray): T[] { } function flattenNestedLoop(array: NestedArray, result: T[]) { - for (var i = 0; i < array.length; i++) { - var value = array[i]; + for (let i = 0; i < array.length; i++) { + const value = array[i]; if (Array.isArray(value)) { flattenNestedLoop(value, result); } else { diff --git a/lib/Core/hashFromString.ts b/lib/Core/hashFromString.ts index 32cc1d14f65..6f1d2533394 100644 --- a/lib/Core/hashFromString.ts +++ b/lib/Core/hashFromString.ts @@ -3,7 +3,7 @@ export default function hashFromString(s: string) { return Math.abs( s.split("").reduce(function (prev, c) { - var hash = (prev << 5) - prev + c.charCodeAt(0); + const hash = (prev << 5) - prev + c.charCodeAt(0); return hash; }, 0) ); diff --git a/lib/Core/injectTerms.ts b/lib/Core/injectTerms.ts index 59d0123e926..6bf4b95a669 100644 --- a/lib/Core/injectTerms.ts +++ b/lib/Core/injectTerms.ts @@ -73,7 +73,7 @@ const injectTerms = (string: string, termDictionary: Term[]): string => { let injectIndex = 0; const injectedBoldSet = new Set(); while (1) { - let tooltipTerms = new Map(); + const tooltipTerms = new Map(); termDictionary.forEach((item: any) => tooltipTerms.set( diff --git a/lib/Core/loadJson.ts b/lib/Core/loadJson.ts index 61be0e52a59..c0cd6d20705 100644 --- a/lib/Core/loadJson.ts +++ b/lib/Core/loadJson.ts @@ -6,10 +6,10 @@ export default function loadJson( body?: any, asForm: boolean = false ): Promise { - let responseType: XMLHttpRequestResponseType = "json"; + const responseType: XMLHttpRequestResponseType = "json"; let jsonPromise: Promise; - let params: any = { + const params: any = { url: urlOrResource, headers: headers }; diff --git a/lib/Core/markdownToHtml.ts b/lib/Core/markdownToHtml.ts index 76d80e84a74..be4adedc27a 100644 --- a/lib/Core/markdownToHtml.ts +++ b/lib/Core/markdownToHtml.ts @@ -1,17 +1,17 @@ "use strict"; -var defined = require("terriajs-cesium/Source/Core/defined").default; -var MarkdownIt = require("markdown-it"); -var DOMPurify = require("dompurify/dist/purify"); +const defined = require("terriajs-cesium/Source/Core/defined").default; +const MarkdownIt = require("markdown-it"); +const DOMPurify = require("dompurify/dist/purify"); import injectTerms from "./injectTerms"; import { Term } from "../ReactViewModels/defaultTerms"; -var md = new MarkdownIt({ +const md = new MarkdownIt({ html: true, linkify: true }); -var htmlRegex = /^\s*<[^>]+>/; +const htmlRegex = /^\s*<[^>]+>/; export interface MarkdownOptions { // requires tooltipTerms as well @@ -44,7 +44,7 @@ function markdownToHtml( // If the text looks like html, don't try to interpret it as Markdown because // we'll probably break it in the process. // It would wrap non-standard tags such as hi in a

, which is bad. - var unsafeHtml: string; + let unsafeHtml: string; if (htmlRegex.test(markdownString)) { unsafeHtml = markdownString; } else { diff --git a/lib/Map/Cesium/CesiumRenderLoopPauser.ts b/lib/Map/Cesium/CesiumRenderLoopPauser.ts index f0b0b2350df..4ad8dcf74d4 100644 --- a/lib/Map/Cesium/CesiumRenderLoopPauser.ts +++ b/lib/Map/Cesium/CesiumRenderLoopPauser.ts @@ -45,7 +45,7 @@ export default class CesiumRenderLoopPauser { ); this._boundNotifyRepaintRequired = this.notifyRepaintRequired.bind(this); - var canvas = this.cesiumWidget.canvas; + const canvas = this.cesiumWidget.canvas; canvas.addEventListener( "mousemove", this._boundNotifyRepaintRequired, @@ -156,7 +156,7 @@ export default class CesiumRenderLoopPauser { parameters, transferableObjects ) { - var result = that._originalScheduleTask.call( + const result = that._originalScheduleTask.call( this, parameters, transferableObjects @@ -165,7 +165,7 @@ export default class CesiumRenderLoopPauser { if (!defined(this._originalWorkerMessageSinkRepaint)) { this._originalWorkerMessageSinkRepaint = this._worker.onmessage; - var taskProcessor = this; + const taskProcessor = this; this._worker.onmessage = function (event: any) { taskProcessor._originalWorkerMessageSinkRepaint(event); diff --git a/lib/Map/Cesium/CesiumSelectionIndicator.ts b/lib/Map/Cesium/CesiumSelectionIndicator.ts index 0e35272641e..f7747e00834 100644 --- a/lib/Map/Cesium/CesiumSelectionIndicator.ts +++ b/lib/Map/Cesium/CesiumSelectionIndicator.ts @@ -25,8 +25,8 @@ declare module "terriajs-cesium/Source/Scene/Scene" { } } -var screenSpacePos = new Cartesian2(); -var offScreen = "-1000px"; +const screenSpacePos = new Cartesian2(); +const offScreen = "-1000px"; export default class CesiumSelectionIndicator { /** diff --git a/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts b/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts index 20fd81084c6..5a893d0f46a 100644 --- a/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts +++ b/lib/Map/ImageryProvider/ProtomapsImageryProvider.ts @@ -163,7 +163,7 @@ export class GeojsonSource implements TileSource { // request a particular tile const tile = (await this.tileIndex).getTile(c.z, c.x, c.y) as GeojsonVtTile; - let result = new Map(); + const result = new Map(); const scale = tileSize / geojsonvtExtent; if (tile && tile.features && tile.features.length > 0) { @@ -176,7 +176,7 @@ export class GeojsonSource implements TileSource { let numVertices = 0; // Calculate bbox - let bbox: Bbox = { + const bbox: Bbox = { minX: Infinity, minY: Infinity, maxX: -Infinity, @@ -373,7 +373,7 @@ export default class ProtomapsImageryProvider if (typeof this.data === "string") { if (this.data.endsWith(".pmtiles")) { this.source = new PmtilesSource(this.data, false); - let cache = new TileCache(this.source, 1024); + const cache = new TileCache(this.source, 1024); this.view = new View(cache, this.maximumNativeZoom, 2); } else if ( this.data.endsWith(".json") || @@ -382,7 +382,7 @@ export default class ProtomapsImageryProvider this.source = new GeojsonSource(this.data); } else { this.source = new ZxySource(this.data, false); - let cache = new TileCache(this.source, 1024); + const cache = new TileCache(this.source, 1024); this.view = new View(cache, this.maximumNativeZoom, 2); } } @@ -469,7 +469,7 @@ export default class ProtomapsImageryProvider this.labelers.add(coords.z, tileMap); - let labelData = this.labelers.getIndex(tile.z); + const labelData = this.labelers.getIndex(tile.z); const bbox = { minX: 256 * coords.x - BUF, @@ -573,7 +573,7 @@ export default class ProtomapsImageryProvider const bufferBbox = bbox(buffer); // Get array of all features - let geojsonFeatures: Feature[] = this.source.geojsonObject.features; + const geojsonFeatures: Feature[] = this.source.geojsonObject.features; const pickedFeatures: Feature[] = []; diff --git a/lib/Map/Leaflet/LeafletSelectionIndicator.ts b/lib/Map/Leaflet/LeafletSelectionIndicator.ts index 9b0159e8bfb..79ed9474b5e 100644 --- a/lib/Map/Leaflet/LeafletSelectionIndicator.ts +++ b/lib/Map/Leaflet/LeafletSelectionIndicator.ts @@ -67,7 +67,7 @@ export default class LeafletSelectionIndicator { this._selectionIndicatorTween = undefined; } - var style = this._selectionIndicatorDomElement.style; + const style = this._selectionIndicatorDomElement.style; this._selectionIndicatorIsAppearing = true; @@ -111,7 +111,7 @@ export default class LeafletSelectionIndicator { this._selectionIndicatorTween = undefined; } - var style = this._selectionIndicatorDomElement.style; + const style = this._selectionIndicatorDomElement.style; this._selectionIndicatorIsAppearing = false; @@ -147,9 +147,9 @@ export default class LeafletSelectionIndicator { return; } - var feature = this._leaflet.terria.selectedFeature; + const feature = this._leaflet.terria.selectedFeature; if (isDefined(feature) && isDefined(feature.position)) { - var cartographic = Ellipsoid.WGS84.cartesianToCartographic( + const cartographic = Ellipsoid.WGS84.cartesianToCartographic( feature.position.getValue( this._leaflet.terria.timelineClock.currentTime ), diff --git a/lib/Map/Leaflet/LeafletVisualizer.ts b/lib/Map/Leaflet/LeafletVisualizer.ts index f83edf57c24..4068597663e 100644 --- a/lib/Map/Leaflet/LeafletVisualizer.ts +++ b/lib/Map/Leaflet/LeafletVisualizer.ts @@ -1022,7 +1022,7 @@ class LeafletGeomVisualizer { polyline.setLatLngs(latlngs); } - for (let prop in polylineOptions) { + for (const prop in polylineOptions) { if ((polylineOptions)[prop] !== (polyline.options)[prop]) { polyline.setStyle(polylineOptions); break; @@ -1215,7 +1215,7 @@ function positionToLatLng( position: Cartesian3, bounds: LatLngBounds | undefined ) { - var cartographic = Ellipsoid.WGS84.cartesianToCartographic(position); + const cartographic = Ellipsoid.WGS84.cartesianToCartographic(position); let lon = CesiumMath.toDegrees(cartographic.longitude); if (bounds !== undefined) { if (_isCloseToEasternAntiMeridian(bounds)) { @@ -1232,7 +1232,7 @@ function positionToLatLng( } function hierarchyToLatLngs(hierarchy: PolygonHierarchy) { - let holes: L.LatLng[][] = []; + const holes: L.LatLng[][] = []; const positions = Array.isArray(hierarchy) ? hierarchy : hierarchy.positions; if (hierarchy.holes.length > 0) { hierarchy.holes.forEach((hole) => { @@ -1320,10 +1320,10 @@ function getValueOrUndefined(property: Property | undefined, time: JulianDate) { } function convertEntityPositionsToLatLons(positions: Cartesian3[]): L.LatLng[] { - var carts = Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions); - var latlngs: L.LatLng[] = []; + const carts = Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions); + const latlngs: L.LatLng[] = []; let lastLongitude; - for (var p = 0; p < carts.length; p++) { + for (let p = 0; p < carts.length; p++) { let lon = CesiumMath.toDegrees(carts[p].longitude); if (lastLongitude !== undefined) { diff --git a/lib/Map/PickedFeatures/featureDataToGeoJson.ts b/lib/Map/PickedFeatures/featureDataToGeoJson.ts index 9e1c801442a..9f0711c1815 100644 --- a/lib/Map/PickedFeatures/featureDataToGeoJson.ts +++ b/lib/Map/PickedFeatures/featureDataToGeoJson.ts @@ -185,7 +185,7 @@ function getEsriFeature( if (geojsonGeom) { return { - type: "Feature" as "Feature", + type: "Feature" as const, properties: isJsonObject(featureData.attributes) ? featureData.attributes : {}, diff --git a/lib/Map/Region/RegionProvider.ts b/lib/Map/Region/RegionProvider.ts index faffff8d19b..19a4b05fc2d 100644 --- a/lib/Map/Region/RegionProvider.ts +++ b/lib/Map/Region/RegionProvider.ts @@ -393,7 +393,7 @@ export default class RegionProvider { } else { r = s.toLowerCase().trim(); } - let replacements = this[replacementsProp]; + const replacements = this[replacementsProp]; if (replacements === undefined || replacements.length === 0) { return r; } @@ -429,8 +429,8 @@ export default class RegionProvider { "dataReplacements" ); - let id = this._idIndex[code]; - let idAfterReplacement = this._idIndex[codeAfterReplacement]; + const id = this._idIndex[code]; + const idAfterReplacement = this._idIndex[codeAfterReplacement]; if (!isDefined(id) && !isDefined(idAfterReplacement)) { return -1; diff --git a/lib/Map/Vector/Reproject.ts b/lib/Map/Vector/Reproject.ts index 1312fc285ec..b883042f591 100644 --- a/lib/Map/Vector/Reproject.ts +++ b/lib/Map/Vector/Reproject.ts @@ -62,7 +62,7 @@ export default { sourceCode in Proj4Definitions ? new proj4.Proj(Proj4Definitions[sourceCode]) : undefined; - var dest = + const dest = destCode in Proj4Definitions ? new proj4.Proj(Proj4Definitions[destCode]) : undefined; @@ -86,7 +86,7 @@ export default { return true; } - var url = new urijs(proj4ServiceBaseUrl).segment(code).toString(); + const url = new urijs(proj4ServiceBaseUrl).segment(code).toString(); return loadText(url) .then(function (proj4Text: string) { Proj4Definitions[code] = proj4Text; diff --git a/lib/Map/Vector/rectangleToLatLngBounds.ts b/lib/Map/Vector/rectangleToLatLngBounds.ts index 8ff2a4b182f..a3ea79f8e96 100644 --- a/lib/Map/Vector/rectangleToLatLngBounds.ts +++ b/lib/Map/Vector/rectangleToLatLngBounds.ts @@ -8,9 +8,9 @@ import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; * @return {L.latLngBounds} The equivalent Leaflet latLngBounds. */ export default function rectangleToLatLngBounds(rectangle: Rectangle) { - var west = CesiumMath.toDegrees(rectangle.west); - var south = CesiumMath.toDegrees(rectangle.south); - var east = CesiumMath.toDegrees(rectangle.east); - var north = CesiumMath.toDegrees(rectangle.north); + const west = CesiumMath.toDegrees(rectangle.west); + const south = CesiumMath.toDegrees(rectangle.south); + const east = CesiumMath.toDegrees(rectangle.east); + const north = CesiumMath.toDegrees(rectangle.north); return L.latLngBounds([south, west], [north, east]); } diff --git a/lib/ModelMixins/CatalogMemberMixin.ts b/lib/ModelMixins/CatalogMemberMixin.ts index 6c0f31ba47c..c0d32b929c9 100644 --- a/lib/ModelMixins/CatalogMemberMixin.ts +++ b/lib/ModelMixins/CatalogMemberMixin.ts @@ -123,9 +123,9 @@ function CatalogMemberMixin>(Base: T) { @computed get nameSortKey() { - var parts = (this.nameInCatalog || "").split(/(\d+)/); + const parts = (this.nameInCatalog || "").split(/(\d+)/); return parts.map(function (part) { - var parsed = parseInt(part, 10); + const parsed = parseInt(part, 10); if (parsed === parsed) { return parsed; } else { diff --git a/lib/ModelMixins/Cesium3dTilesMixin.ts b/lib/ModelMixins/Cesium3dTilesMixin.ts index ff17939e254..6c272449a81 100644 --- a/lib/ModelMixins/Cesium3dTilesMixin.ts +++ b/lib/ModelMixins/Cesium3dTilesMixin.ts @@ -214,7 +214,7 @@ function Cesium3dTilesMixin>(Base: T) { */ private computeModelMatrixFromTransformationTraits(modelMatrix: Matrix4) { let scale = Matrix4.getScale(modelMatrix, new Cartesian3()); - let position = Matrix4.getTranslation(modelMatrix, new Cartesian3()); + const position = Matrix4.getTranslation(modelMatrix, new Cartesian3()); let orientation = Quaternion.fromRotationMatrix( Matrix4.getMatrix3(modelMatrix, new Matrix3()) ); @@ -348,7 +348,7 @@ function Cesium3dTilesMixin>(Base: T) { return; } - let resource: IonResource | undefined = await IonResource.fromAssetId( + const resource: IonResource | undefined = await IonResource.fromAssetId( ionAssetId, { accessToken: diff --git a/lib/ModelMixins/DiscretelyTimeVaryingMixin.ts b/lib/ModelMixins/DiscretelyTimeVaryingMixin.ts index 10a0fd98cd5..ff5b1ac4fd4 100644 --- a/lib/ModelMixins/DiscretelyTimeVaryingMixin.ts +++ b/lib/ModelMixins/DiscretelyTimeVaryingMixin.ts @@ -115,7 +115,9 @@ function DiscretelyTimeVaryingMixin< tag: dt.tag !== undefined ? dt.tag : dt.time }); } - } catch {} + } catch { + return; + } } asJulian.sort((a, b) => JulianDate.compare(a.time, b.time)); return asJulian; @@ -436,15 +438,15 @@ export type ObjectifiedHours = DatesObject; * whose values are objects whose keys are days, whose values are arrays of all the datetimes on that day. */ function objectifyDates(dates: Date[]): ObjectifiedDates { - let result: ObjectifiedDates = { index: [], dates }; + const result: ObjectifiedDates = { index: [], dates }; for (let i = 0; i < dates.length; i++) { - let date = dates[i]; - let year = date.getFullYear(); - let century = Math.floor(year / 100); - let month = date.getMonth(); - let day = date.getDate(); - let hour = date.getHours(); + const date = dates[i]; + const year = date.getFullYear(); + const century = Math.floor(year / 100); + const month = date.getMonth(); + const day = date.getDate(); + const hour = date.getHours(); // ObjectifiedDates if (!result[century]) { diff --git a/lib/ModelMixins/GeojsonMixin.ts b/lib/ModelMixins/GeojsonMixin.ts index 5eacc8fee0c..7e739e2f500 100644 --- a/lib/ModelMixins/GeojsonMixin.ts +++ b/lib/ModelMixins/GeojsonMixin.ts @@ -649,8 +649,8 @@ function GeoJsonMixin>(Base: T) { }); if (matchedStyles !== undefined) { - for (let matched of matchedStyles) { - for (let trait of Object.keys(matched.style.traits)) { + for (const matched of matchedStyles) { + for (const trait of Object.keys(matched.style.traits)) { featureProperties[trait] = // @ts-ignore - TS can't tell that `trait` is of the correct index type for style matched.style[trait] ?? featureProperties[trait]; @@ -684,7 +684,7 @@ function GeoJsonMixin>(Base: T) { const dataSource = new CustomDataSource(this.name || "Table"); dataSource.entities.suspendEvents(); - let features: Entity[] = createLongitudeLatitudeFeaturePerRow( + const features: Entity[] = createLongitudeLatitudeFeaturePerRow( style, longitudes, latitudes @@ -1616,7 +1616,7 @@ function filterValue( prop: string, func: (obj: any, prop: string) => void ) { - for (let p in obj) { + for (const p in obj) { if (obj.hasOwnProperty(p) === false) { continue; } else if (p === prop) { @@ -1834,7 +1834,7 @@ function isPolygonOnTerrain(polygon: PolygonGraphics, now: JulianDate) { return isClamped || (!hasPerPositionHeight && !hasPolygonHeight); } -export function getColor(color: String | string | Color): Color { +export function getColor(color: string | string | Color): Color { if (typeof color === "string" || color instanceof String) { return Color.fromCssColorString(color.toString()) ?? Color.GRAY; } else { diff --git a/lib/ModelMixins/TableMixin.ts b/lib/ModelMixins/TableMixin.ts index 2f44c562e12..9a2619dd6ca 100644 --- a/lib/ModelMixins/TableMixin.ts +++ b/lib/ModelMixins/TableMixin.ts @@ -185,7 +185,9 @@ function TableMixin>(Base: T) { if (activeStyle === undefined) { return this.defaultTableStyle; } - let ret = this.tableStyles.find((style) => style.id === this.activeStyle); + const ret = this.tableStyles.find( + (style) => style.id === this.activeStyle + ); if (ret === undefined) { return this.defaultTableStyle; } @@ -214,7 +216,7 @@ function TableMixin>(Base: T) { protected async _exportData(): Promise { if (isDefined(this.dataColumnMajor)) { // I am assuming all columns have the same length -> so use first column - let csvString = this.dataColumnMajor[0] + const csvString = this.dataColumnMajor[0] .map((row, rowIndex) => this.dataColumnMajor!.map((col) => col[rowIndex]).join(",") ) diff --git a/lib/Models/CameraView.ts b/lib/Models/CameraView.ts index c8c87706777..80655c47b29 100644 --- a/lib/Models/CameraView.ts +++ b/lib/Models/CameraView.ts @@ -240,7 +240,7 @@ export default class CameraView { } Cartesian3.normalize(rightENU, rightENU); - var upENU = Cartesian3.cross(rightENU, directionENU, scratchUp); + const upENU = Cartesian3.cross(rightENU, directionENU, scratchUp); Cartesian3.normalize(upENU, upENU); const targetCartesian = Ellipsoid.WGS84.cartographicToCartesian( diff --git a/lib/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJob.ts b/lib/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJob.ts index 07fadd9dcc6..e6c15f203f1 100644 --- a/lib/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJob.ts +++ b/lib/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJob.ts @@ -245,7 +245,7 @@ export default class YDYRCatalogFunctionJob extends CatalogFunctionJobMixin( undefined ); - let regionColumnSplit = DATASETS.find( + const regionColumnSplit = DATASETS.find( (d) => d.title === this.parameters?.["Output Geography"] )?.geographyName.split("_"); let regionColumn = ""; diff --git a/lib/Models/Catalog/CatalogGroups/OpenDataSoftCatalogGroup.ts b/lib/Models/Catalog/CatalogGroups/OpenDataSoftCatalogGroup.ts index 651a3dd0761..7a6ee43084e 100644 --- a/lib/Models/Catalog/CatalogGroups/OpenDataSoftCatalogGroup.ts +++ b/lib/Models/Catalog/CatalogGroups/OpenDataSoftCatalogGroup.ts @@ -39,7 +39,7 @@ export class OpenDataSoftCatalogStratum extends LoadableStratum( domain: catalogGroup.url }); - let datasets: ValidDataset[] = []; + const datasets: ValidDataset[] = []; let facets: ValidFacet[] | undefined; // If no facetFilters, try to get some facets diff --git a/lib/Models/Catalog/CatalogGroups/ThreddsCatalogGroup.ts b/lib/Models/Catalog/CatalogGroups/ThreddsCatalogGroup.ts index 9461235dfb7..2d161a48f5d 100644 --- a/lib/Models/Catalog/CatalogGroups/ThreddsCatalogGroup.ts +++ b/lib/Models/Catalog/CatalogGroups/ThreddsCatalogGroup.ts @@ -101,7 +101,7 @@ export class ThreddsStratum extends LoadableStratum(ThreddsCatalogGroupTraits) { @action async createMembers() { if (!isDefined(this._catalogGroup.url)) return; - let proxy = proxyCatalogItemBaseUrl( + const proxy = proxyCatalogItemBaseUrl( this._catalogGroup, this._catalogGroup.url ); diff --git a/lib/Models/Catalog/CatalogItems/ApiTableCatalogItem.ts b/lib/Models/Catalog/CatalogItems/ApiTableCatalogItem.ts index 0de57c9b073..1ff89b2e465 100644 --- a/lib/Models/Catalog/CatalogItems/ApiTableCatalogItem.ts +++ b/lib/Models/Catalog/CatalogItems/ApiTableCatalogItem.ts @@ -131,7 +131,7 @@ export class ApiTableCatalogItem extends AutoRefreshingMixin( row["value"] = value; // add the id to the row's data row[this.idKey!] = id; if (columnMajorData.has(id)) { - let currentRow = columnMajorData.get(id); + const currentRow = columnMajorData.get(id); columnMajorData.set(id, { currentRow, ...value }); } else { columnMajorData.set(id, row); diff --git a/lib/Models/Catalog/CatalogItems/CartoMapV3CatalogItem.ts b/lib/Models/Catalog/CatalogItems/CartoMapV3CatalogItem.ts index e71af758eb6..be5c139ba92 100644 --- a/lib/Models/Catalog/CatalogItems/CartoMapV3CatalogItem.ts +++ b/lib/Models/Catalog/CatalogItems/CartoMapV3CatalogItem.ts @@ -109,7 +109,7 @@ export default class CartoMapV3CatalogItem extends GeoJsonMixin( q: this.cartoQuery, geo_column: this.cartoGeoColumn }) - ).throwIfError(); + )?.throwIfError(); } // If cartoTableName is defined - use Table API (https://api-docs.carto.com/#6a05d4d7-c6a1-4635-a8de-c91fa5e77fda) else if (this.cartoTableName) { @@ -124,7 +124,7 @@ export default class CartoMapV3CatalogItem extends GeoJsonMixin( response = ( await callCartoApi(url.toString(), this.accessToken) - ).throwIfError(); + )?.throwIfError(); } else { throw new TerriaError({ title: "Invalid Carto V3 config", @@ -176,7 +176,7 @@ export default class CartoMapV3CatalogItem extends GeoJsonMixin( // Download all geoJson files const geojsonResponses = await Promise.all( this.geoJsonUrls.map(async (url) => { - jsonData = (await callCartoApi(url, this.accessToken)).throwIfError(); + jsonData = (await callCartoApi(url, this.accessToken))?.throwIfError(); if (jsonData === undefined) { throw new TerriaError({ @@ -289,7 +289,9 @@ async function callCartoApi(url: string, auth?: string, body?: JsonObject) { ) ); } - } catch {} + } catch { + return; + } } return Result.error(e); } diff --git a/lib/Models/Catalog/CatalogItems/GpxCatalogItem.ts b/lib/Models/Catalog/CatalogItems/GpxCatalogItem.ts index a4c0e3b95c1..77e4b039781 100644 --- a/lib/Models/Catalog/CatalogItems/GpxCatalogItem.ts +++ b/lib/Models/Catalog/CatalogItems/GpxCatalogItem.ts @@ -43,7 +43,7 @@ class GpxCatalogItem extends GeoJsonMixin(CreateModel(GpxCatalogItemTraits)) { } private loadGpxText(text: string) { - var dom = new DOMParser().parseFromString(text, "text/xml"); + const dom = new DOMParser().parseFromString(text, "text/xml"); return toGeoJSON.gpx(dom); } diff --git a/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts b/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts index 6d21833590f..d9245fe91d3 100644 --- a/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts +++ b/lib/Models/Catalog/CatalogItems/MapboxVectorTileCatalogItem.ts @@ -158,7 +158,7 @@ class MapboxVectorTileCatalogItem extends MappableMixin( * - `parsedJsonStyle` */ get paintRules(): PaintRule[] { - let rules: PaintRule[] = []; + const rules: PaintRule[] = []; if (this.layer) { if (this.fillColor) { diff --git a/lib/Models/Catalog/CatalogItems/SenapsLocationsCatalogItem.ts b/lib/Models/Catalog/CatalogItems/SenapsLocationsCatalogItem.ts index f2673a11e84..12e9f53d274 100644 --- a/lib/Models/Catalog/CatalogItems/SenapsLocationsCatalogItem.ts +++ b/lib/Models/Catalog/CatalogItems/SenapsLocationsCatalogItem.ts @@ -95,7 +95,7 @@ export class SenapsLocationsStratum extends LoadableStratum( const locations = locationsResponse._embedded.locations; const streamPromises = []; - for (var i = 0; i < locations.length; i++) { + for (let i = 0; i < locations.length; i++) { const location = locations[i]; const locationId = location.id; const streamUrl = proxyCatalogItemUrl( @@ -286,7 +286,7 @@ class SenapsLocationsCatalogItem extends MappableMixin( message: i18next.t("models.senaps.missingSenapsBaseUrl") }); } - var uri = new URI(`${this.url}/locations`); + const uri = new URI(`${this.url}/locations`); if (this.locationIdFilter !== undefined) { uri.setSearch("id", this.locationIdFilter); } @@ -302,7 +302,7 @@ class SenapsLocationsCatalogItem extends MappableMixin( message: i18next.t("models.senaps.missingSenapsBaseUrl") }); } - var uri = new URI(`${this.url}/streams`); + const uri = new URI(`${this.url}/streams`); if (this.streamIdFilter !== undefined) { uri.setSearch("id", this.streamIdFilter); } diff --git a/lib/Models/Catalog/CatalogReferences/MagdaReference.ts b/lib/Models/Catalog/CatalogReferences/MagdaReference.ts index 4330c04276a..6c8c797917a 100644 --- a/lib/Models/Catalog/CatalogReferences/MagdaReference.ts +++ b/lib/Models/Catalog/CatalogReferences/MagdaReference.ts @@ -237,7 +237,7 @@ export default class MagdaReference extends AccessControlMixin( ) { if (record && override && isJsonObject(override.aspects)) { if (isJsonObject(record.aspects)) { - for (let key in override.aspects) + for (const key in override.aspects) record.aspects[key] = override.aspects[key]; } else { record.aspects = override.aspects; diff --git a/lib/Models/Catalog/Ckan/CkanCatalogGroup.ts b/lib/Models/Catalog/Ckan/CkanCatalogGroup.ts index eade52374a4..ba04862c518 100644 --- a/lib/Models/Catalog/Ckan/CkanCatalogGroup.ts +++ b/lib/Models/Catalog/Ckan/CkanCatalogGroup.ts @@ -117,14 +117,14 @@ export class CkanServerStratum extends LoadableStratum(CkanCatalogGroupTraits) { static async load( catalogGroup: CkanCatalogGroup ): Promise { - var terria = catalogGroup.terria; + const terria = catalogGroup.terria; let ckanServerResponse: CkanServerResponse | undefined = undefined; // Each item in the array causes an independent request to the CKAN, and the results are concatenated - for (var i = 0; i < catalogGroup.filterQuery.length; ++i) { + for (let i = 0; i < catalogGroup.filterQuery.length; ++i) { const filterQuery = catalogGroup.filterQuery[i]; - var uri = new URI(catalogGroup.url) + const uri = new URI(catalogGroup.url) .segment("api/3/action/package_search") .addQuery({ start: 0, rows: 1000, sort: "metadata_created asc" }); @@ -242,7 +242,7 @@ export class CkanServerStratum extends LoadableStratum(CkanCatalogGroupTraits) { dataset: CkanDataset, groupId: string ) { - let group: CatalogGroup | undefined = + const group: CatalogGroup | undefined = this._catalogGroup.terria.getModelById(CatalogGroup, groupId); if (group !== undefined) { group.add(CommonStrata.definition, catalogItem); @@ -293,7 +293,7 @@ export class CkanServerStratum extends LoadableStratum(CkanCatalogGroupTraits) { let filteredResources: CkanResourceWithFormat[] = []; // Track format IDS which multiple resources // As if they do, we will need to make sure that CkanItemReference uses resource name (instead of dataset name) - let formatsWithMultipleResources = new Set(); + const formatsWithMultipleResources = new Set(); if (this._catalogGroup.useSingleResource) { filteredResources = supportedResources[0] ? [supportedResources[0]] : []; @@ -352,7 +352,7 @@ export class CkanServerStratum extends LoadableStratum(CkanCatalogGroupTraits) { this._catalogGroup ); - for (var i = 0; i < filteredResources.length; ++i) { + for (let i = 0; i < filteredResources.length; ++i) { const { resource, format } = filteredResources[i]; const itemId = this.getItemId(ckanDataset, resource); diff --git a/lib/Models/Catalog/Ckan/CkanItemReference.ts b/lib/Models/Catalog/Ckan/CkanItemReference.ts index 388de0beff3..04b0fdf34ba 100644 --- a/lib/Models/Catalog/Ckan/CkanItemReference.ts +++ b/lib/Models/Catalog/Ckan/CkanItemReference.ts @@ -143,8 +143,8 @@ export class CkanDatasetStratum extends LoadableStratum( } } if (this.ckanDataset.geo_coverage !== undefined) { - var bboxString = this.ckanDataset.geo_coverage; - var parts = bboxString.split(","); + const bboxString = this.ckanDataset.geo_coverage; + const parts = bboxString.split(","); if (parts.length === 4) { return createStratumInstance(RectangleTraits, { west: parseInt(parts[0]), @@ -158,7 +158,7 @@ export class CkanDatasetStratum extends LoadableStratum( isDefined(this.ckanDataset.spatial) && this.ckanDataset.spatial !== "" ) { - var gj = JSON.parse(this.ckanDataset.spatial); + const gj = JSON.parse(this.ckanDataset.spatial); if (gj.type === "Polygon" && gj.coordinates[0].length === 5) { return createStratumInstance(RectangleTraits, { west: gj.coordinates[0][0][0], @@ -439,7 +439,7 @@ export interface PreparedSupportedFormat } async function loadCkanDataset(ckanItem: CkanItemReference) { - var uri = new URI(ckanItem.url) + const uri = new URI(ckanItem.url) .segment("api/3/action/package_show") .addQuery({ id: ckanItem.datasetId }); @@ -451,7 +451,7 @@ async function loadCkanDataset(ckanItem: CkanItemReference) { } async function loadCkanResource(ckanItem: CkanItemReference) { - var uri = new URI(ckanItem.url) + const uri = new URI(ckanItem.url) .segment("api/3/action/resource_show") .addQuery({ id: ckanItem.resourceId }); @@ -520,7 +520,7 @@ export function isResourceInSupportedFormats( formats: PreparedSupportedFormat[] ): PreparedSupportedFormat | undefined { if (resource === undefined) return undefined; - let matches: PreparedSupportedFormat[] = []; + const matches: PreparedSupportedFormat[] = []; for (let i = 0; i < formats.length; ++i) { const format = formats[i]; if (resourceIsSupported(resource, format)) return format; diff --git a/lib/Models/Catalog/Esri/ArcGisCatalogGroup.ts b/lib/Models/Catalog/Esri/ArcGisCatalogGroup.ts index b987db9fbf3..3b1aa01588b 100644 --- a/lib/Models/Catalog/Esri/ArcGisCatalogGroup.ts +++ b/lib/Models/Catalog/Esri/ArcGisCatalogGroup.ts @@ -68,8 +68,8 @@ class ArcGisServerStratum extends LoadableStratum(ArcGisCatalogGroupTraits) { static async load( catalogGroup: ArcGisCatalogGroup ): Promise { - var terria = catalogGroup.terria; - var uri = new URI(catalogGroup.url).addQuery("f", "json"); + const terria = catalogGroup.terria; + const uri = new URI(catalogGroup.url).addQuery("f", "json"); return loadJson(proxyCatalogItemUrl(catalogGroup, uri.toString())) .then((arcgisServer: ArcGisServer) => { // Is this really a ArcGisServer REST response? @@ -169,7 +169,7 @@ class ArcGisServerStratum extends LoadableStratum(ArcGisCatalogGroupTraits) { model.setTrait(CommonStrata.definition, "name", replaceUnderscores(folder)); - var uri = new URI(this._catalogGroup.url).segment(folder); + const uri = new URI(this._catalogGroup.url).segment(folder); model.setTrait(CommonStrata.definition, "url", uri.toString()); } @@ -230,7 +230,7 @@ class ArcGisServerStratum extends LoadableStratum(ArcGisCatalogGroupTraits) { replaceUnderscores(localName) ); - var uri = new URI(this._catalogGroup.url) + const uri = new URI(this._catalogGroup.url) .segment(localName) .segment(service.type); model.setTrait(CommonStrata.definition, "url", uri.toString()); @@ -314,7 +314,7 @@ function removePathFromName(basePath: string, name: string) { return name; } - var index = name.indexOf(basePath); + const index = name.indexOf(basePath); if (index === 0) { return name.substring(basePath.length + 1); } else { @@ -323,7 +323,7 @@ function removePathFromName(basePath: string, name: string) { } function getBasePath(catalogGroup: ArcGisCatalogGroup) { - var match = /rest\/services\/(.*)/i.exec(catalogGroup.url || ""); + const match = /rest\/services\/(.*)/i.exec(catalogGroup.url || ""); if (match && match.length > 1) { return match[1]; } else { diff --git a/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogGroup.ts b/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogGroup.ts index d9b7a59bbd2..5fff7242dea 100644 --- a/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogGroup.ts +++ b/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogGroup.ts @@ -120,8 +120,8 @@ export class FeatureServerStratum extends LoadableStratum( static async load( catalogGroup: ArcGisFeatureServerCatalogGroup | ArcGisCatalogGroup ): Promise { - var terria = catalogGroup.terria; - var uri = new URI(catalogGroup.url).addQuery("f", "json"); + const terria = catalogGroup.terria; + const uri = new URI(catalogGroup.url).addQuery("f", "json"); return loadJson(proxyCatalogItemUrl(catalogGroup, uri.toString())) .then((featureServer: FeatureServer) => { @@ -208,7 +208,7 @@ export class FeatureServerStratum extends LoadableStratum( replaceUnderscores(layer.name) ); - var uri = new URI(this._catalogGroup.url).segment(layer.id + ""); // Convert layer id to string as segment(0) means sthg different. + const uri = new URI(this._catalogGroup.url).segment(layer.id + ""); // Convert layer id to string as segment(0) means sthg different. model.setTrait(CommonStrata.definition, "url", uri.toString()); } } diff --git a/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogItem.ts b/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogItem.ts index f0d90021fdb..e5c8d5cf707 100644 --- a/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogItem.ts +++ b/lib/Models/Catalog/Esri/ArcGisFeatureServerCatalogItem.ts @@ -514,7 +514,7 @@ export default class ArcGisFeatureServerCatalogItem extends GeoJsonMixin( // until we run out of features or hit the limit const featuresPerRequest = this.featuresPerRequest; const maxFeatures = this.maxFeatures; - let combinedEsriLayerJson = await getEsriLayerJson(0); + const combinedEsriLayerJson = await getEsriLayerJson(0); const mapObjectIds = (features: any) => features.map( diff --git a/lib/Models/Catalog/Esri/ArcGisMapServerCatalogItem.ts b/lib/Models/Catalog/Esri/ArcGisMapServerCatalogItem.ts index 7b5d03fbc92..86b87b82f86 100644 --- a/lib/Models/Catalog/Esri/ArcGisMapServerCatalogItem.ts +++ b/lib/Models/Catalog/Esri/ArcGisMapServerCatalogItem.ts @@ -621,10 +621,10 @@ async function getJson(item: ArcGisMapServerCatalogItem, uri: any) { /* Given a comma-separated string of layer names, returns the layer objects corresponding to them. */ function findLayers(layers: Layer[], names: string | undefined) { function findLayer(layers: Layer[], id: string) { - var idLowerCase = id.toLowerCase(); - var foundByName; - for (var i = 0; i < layers.length; ++i) { - var layer = layers[i]; + const idLowerCase = id.toLowerCase(); + let foundByName; + for (let i = 0; i < layers.length; ++i) { + const layer = layers[i]; if (layer.id.toString() === id) { return layer; } else if ( @@ -703,7 +703,7 @@ function cleanAndProxyUrl( function cleanUrl(url: string) { // Strip off the search portion of the URL - var uri = new URI(url); + const uri = new URI(url); uri.search(""); return uri.toString(); } diff --git a/lib/Models/Catalog/Esri/ArcGisPortalCatalogGroup.ts b/lib/Models/Catalog/Esri/ArcGisPortalCatalogGroup.ts index d856be689c3..f8b3eaf669a 100644 --- a/lib/Models/Catalog/Esri/ArcGisPortalCatalogGroup.ts +++ b/lib/Models/Catalog/Esri/ArcGisPortalCatalogGroup.ts @@ -60,7 +60,7 @@ export class ArcGisPortalStratum extends LoadableStratum( static async load( catalogGroup: ArcGisPortalCatalogGroup ): Promise { - var terria = catalogGroup.terria; + const terria = catalogGroup.terria; let portalGroupsServerResponse: | ArcGisPortalGroupSearchResponse @@ -244,7 +244,7 @@ export class ArcGisPortalStratum extends LoadableStratum( private getGroups(): CatalogGroup[] { if (this._catalogGroup.groupBy === "none") return []; - let groups: CatalogGroup[] = [ + const groups: CatalogGroup[] = [ ...createUngroupedGroup(this), ...createGroupsByPortalGroups(this) ]; @@ -287,7 +287,7 @@ export class ArcGisPortalStratum extends LoadableStratum( dataset: ArcGisItem, groupId: string ) { - let group: CatalogGroup | undefined = + const group: CatalogGroup | undefined = this._catalogGroup.terria.getModelById(CatalogGroup, groupId); if (group !== undefined) { group.add(CommonStrata.definition, catalogItem); diff --git a/lib/Models/Catalog/Esri/ArcGisPortalItemReference.ts b/lib/Models/Catalog/Esri/ArcGisPortalItemReference.ts index 136659b9dd0..0232f355aa9 100644 --- a/lib/Models/Catalog/Esri/ArcGisPortalItemReference.ts +++ b/lib/Models/Catalog/Esri/ArcGisPortalItemReference.ts @@ -384,7 +384,7 @@ interface PreparedSupportedFormat { } async function loadPortalItem(portalItem: ArcGisPortalItemReference) { - var uri = new URI(portalItem._portalRootUrl) + const uri = new URI(portalItem._portalRootUrl) .segment(`/sharing/rest/content/items/${portalItem.itemId}`) .addQuery({ f: "json" }); @@ -400,7 +400,7 @@ async function loadPortalItem(portalItem: ArcGisPortalItemReference) { async function loadAdditionalPortalInfo(portalItem: ArcGisPortalItemReference) { if (portalItem._arcgisItem === undefined) return undefined; const baseUrl = portalItem._portalRootUrl; - var uri = new URI(baseUrl) + const uri = new URI(baseUrl) .segment(`/sharing/rest/content/items/${portalItem._arcgisItem.id}/data`) .addQuery({ f: "json" }); diff --git a/lib/Models/Catalog/Gltf/AssImpCatalogItem.ts b/lib/Models/Catalog/Gltf/AssImpCatalogItem.ts index 674aa99a986..af0c838c905 100644 --- a/lib/Models/Catalog/Gltf/AssImpCatalogItem.ts +++ b/lib/Models/Catalog/Gltf/AssImpCatalogItem.ts @@ -159,7 +159,7 @@ export default class AssImpCatalogItem const ajs = await assimpjs(); // Create assimpjs FileList object, and add the files - let fileList = new ajs.FileList(); + const fileList = new ajs.FileList(); for (let i = 0; i < fileArrayBuffers.length; i++) { fileList.AddFile( fileArrayBuffers[i].name, @@ -168,7 +168,7 @@ export default class AssImpCatalogItem } // Convert files to GlTf 2 - let result = ajs.ConvertFileList(fileList, "gltf2"); + const result = ajs.ConvertFileList(fileList, "gltf2"); const fileCount = result.FileCount(); if (!result.IsSuccess() || fileCount == 0) { diff --git a/lib/Models/Catalog/Gtfs/GtfsCatalogItem.ts b/lib/Models/Catalog/Gtfs/GtfsCatalogItem.ts index 27883c4e04c..a9dc4efac10 100644 --- a/lib/Models/Catalog/Gtfs/GtfsCatalogItem.ts +++ b/lib/Models/Catalog/Gtfs/GtfsCatalogItem.ts @@ -137,14 +137,14 @@ export default class GtfsCatalogItem extends UrlMixin( // Although technically the timestamp property is optional, if none is // present we'll show the record. const vehicleMap = new Map(); - for (var i = 0; i < feedEntities.length; ++i) { + for (let i = 0; i < feedEntities.length; ++i) { const entity: FeedEntity = feedEntities[i]; const item: VehicleData = this.convertFeedEntityToBillboardData(entity); if (item && item.position && item.featureInfo) { const vehicleInfo = item.featureInfo.get("entity").vehicle.vehicle; if (vehicleMap.has(vehicleInfo.id) && vehicleInfo.timestamp) { - let existingRecord = vehicleMap.get(vehicleInfo.id); + const existingRecord = vehicleMap.get(vehicleInfo.id); if (existingRecord.timestamp < vehicleInfo.timestamp) { vehicleMap.set(vehicleInfo.id, item); } @@ -163,7 +163,7 @@ export default class GtfsCatalogItem extends UrlMixin( // Convert the GTFS protobuf into a more useful shape const vehicleData: VehicleData[] = this.convertManyFeedEntitiesToBillboardData(this.gtfsFeedEntities); - for (let data of vehicleData) { + for (const data of vehicleData) { if (data.sourceId === undefined) { continue; } @@ -254,7 +254,7 @@ export default class GtfsCatalogItem extends UrlMixin( if (data.featureInfo !== undefined && data.featureInfo !== null) { entity.properties = new PropertyBag(); - for (let key of data.featureInfo.keys()) { + for (const key of data.featureInfo.keys()) { entity.properties.addProperty(key, data.featureInfo.get(key)); } } @@ -419,7 +419,7 @@ export default class GtfsCatalogItem extends UrlMixin( } let position = undefined; let orientation = undefined; - let featureInfo: Map = new Map(); + const featureInfo: Map = new Map(); if ( entity.vehicle !== null && entity.vehicle !== undefined && @@ -452,7 +452,7 @@ export default class GtfsCatalogItem extends UrlMixin( } // Add the values that the feature info template gets populated with - for (let field of GtfsCatalogItem.FEATURE_INFO_TEMPLATE_FIELDS) { + for (const field of GtfsCatalogItem.FEATURE_INFO_TEMPLATE_FIELDS) { featureInfo.set(field, prettyPrintGtfsEntityField(field, entity)); } featureInfo.set("entity", entity); diff --git a/lib/Models/Catalog/Ows/CswCatalogGroup.ts b/lib/Models/Catalog/Ows/CswCatalogGroup.ts index 7365e171267..43193737b2b 100644 --- a/lib/Models/Catalog/Ows/CswCatalogGroup.ts +++ b/lib/Models/Catalog/Ows/CswCatalogGroup.ts @@ -60,7 +60,7 @@ export interface BriefRecord { type?: string; } -export type CswURI = String & { +export type CswURI = string & { scheme?: string; protocol?: string; description?: string; @@ -656,7 +656,7 @@ function findGroup( metadataGroups: MetadataGroup[], record: Record ): MetadataGroup | undefined { - for (var i = 0; i < metadataGroups.length; i++) { + for (let i = 0; i < metadataGroups.length; i++) { const group = metadataGroups[i]; if (group.field) { const fields = filterOutUndefined(toArray(record[group.field]) ?? []); @@ -678,7 +678,7 @@ function findGroup( function matchValue(value: string, recordValue: string, regex?: boolean) { if (isDefined(regex) && regex) { // regular expression so parse it and check string against it - var regExp = new RegExp(value); + const regExp = new RegExp(value); return regExp.test(recordValue); } else { return value === recordValue; diff --git a/lib/Models/Catalog/Ows/SensorObservationServiceCatalogItem.ts b/lib/Models/Catalog/Ows/SensorObservationServiceCatalogItem.ts index 02a3620d00e..8ef51604afd 100644 --- a/lib/Models/Catalog/Ows/SensorObservationServiceCatalogItem.ts +++ b/lib/Models/Catalog/Ows/SensorObservationServiceCatalogItem.ts @@ -469,8 +469,8 @@ export default class SensorObservationServiceCatalogItem extends TableMixin( if (!points) return; if (!Array.isArray(points)) points = [points]; - var measurements = points.map((point) => point.MeasurementTVP); // TVP = Time value pairs, I think. - var featureIdentifier = + const measurements = points.map((point) => point.MeasurementTVP); // TVP = Time value pairs, I think. + const featureIdentifier = observation.featureOfInterest["xlink:href"] || ""; datesCol.push( ...measurements.map((measurement) => @@ -660,9 +660,9 @@ async function loadSoapBody( return; } - var json = xml2json(responseXml); + const json = xml2json(responseXml); if (json.Exception) { - var errorMessage = i18next.t( + let errorMessage = i18next.t( "models.sensorObservationService.unknownError" ); if (json.Exception.ExceptionText) { @@ -750,7 +750,7 @@ function addDurationToIso8601( */ function convertObjectToNameValueArray(parameters: any): NameValue[] { return Object.keys(parameters).reduce((result, key) => { - var values = parameters[key]; + let values = parameters[key]; if (!Array.isArray(values)) { values = [values]; } diff --git a/lib/Models/Catalog/Ows/WebFeatureServiceCapabilities.ts b/lib/Models/Catalog/Ows/WebFeatureServiceCapabilities.ts index 785f13a9f63..c5e8e2ef968 100644 --- a/lib/Models/Catalog/Ows/WebFeatureServiceCapabilities.ts +++ b/lib/Models/Catalog/Ows/WebFeatureServiceCapabilities.ts @@ -24,7 +24,7 @@ export interface FeatureType { export function getRectangleFromLayer( layer: FeatureType ): StratumFromTraits | undefined { - var bbox = layer.WGS84BoundingBox; + const bbox = layer.WGS84BoundingBox; if (bbox) { return { west: bbox.westBoundLongitude, @@ -124,7 +124,7 @@ function getFeatureTypes(json: any): FeatureType[] { } function getOutputTypes(json: any): string[] | undefined { - let outputTypes = json.OperationsMetadata?.Operation?.find( + const outputTypes = json.OperationsMetadata?.Operation?.find( (op: any) => op.name === "GetFeature" )?.Parameter?.find((p: any) => p.name === "outputFormat")?.Value; @@ -147,7 +147,7 @@ interface SrsNamesForLayer { * TODO: For catalog items that specify which layer we are interested in, why build the array describing the srsNames for all the other layers too? */ function getSrsNames(json: any): SrsNamesForLayer[] | undefined { - let layers = json.FeatureTypeList?.FeatureType; + const layers = json.FeatureTypeList?.FeatureType; let srsNamesByLayer: SrsNamesForLayer[] = []; if (Array.isArray(layers)) { srsNamesByLayer = layers.map(buildSrsNameObject); @@ -162,7 +162,7 @@ function getSrsNames(json: any): SrsNamesForLayer[] | undefined { * @param layer */ function buildSrsNameObject(layer: any): SrsNamesForLayer { - let srsNames: string[] = []; + const srsNames: string[] = []; if (isJsonString(layer.DefaultSRS)) srsNames.push(layer.DefaultSRS); if (Array.isArray(layer.OtherSRS)) diff --git a/lib/Models/Catalog/Ows/WebFeatureServiceCatalogItem.ts b/lib/Models/Catalog/Ows/WebFeatureServiceCatalogItem.ts index f9e3f8b0308..166371fa49e 100644 --- a/lib/Models/Catalog/Ows/WebFeatureServiceCatalogItem.ts +++ b/lib/Models/Catalog/Ows/WebFeatureServiceCatalogItem.ts @@ -431,7 +431,9 @@ class WebFeatureServiceCatalogItem extends GetCapabilitiesMixin( let errorMessage: string | undefined; try { errorMessage = xml2json(getFeatureResponse).Exception?.ExceptionText; - } catch {} + } catch { + /* ignore error */ + } const originalError = isDefined(errorMessage) ? new TerriaError({ @@ -454,7 +456,7 @@ class WebFeatureServiceCatalogItem extends GetCapabilitiesMixin( }); } - let geojsonData = + const geojsonData = this.outputFormat === "JSON" ? JSON.parse(getFeatureResponse) : gmlToGeoJson(getFeatureResponse); diff --git a/lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts b/lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts new file mode 100644 index 00000000000..0c73ce55567 --- /dev/null +++ b/lib/Models/Catalog/Ows/WebFeatureServiceSearchProvider.ts @@ -0,0 +1,220 @@ +import i18next from "i18next"; +import { runInAction } from "mobx"; +import URI from "urijs"; +import zoomRectangleFromPoint from "../../../Map/Vector/zoomRectangleFromPoint"; +import xml2json from "../../../ThirdParty/xml2json"; +import SearchProvider from "../../SearchProviders/SearchProvider"; +import SearchProviderResults from "../../SearchProviders/SearchProviderResults"; +import SearchResult from "../../SearchProviders/SearchResult"; +import Terria from "../../Terria"; +import defaultValue from "terriajs-cesium/Source/Core/defaultValue"; +import Resource from "terriajs-cesium/Source/Core/Resource"; + +export interface WebFeatureServiceSearchProviderOptions { + /** Base url for the service */ + wfsServiceUrl: string; + /** Which property to look for the search text in */ + searchPropertyName: string; + /** Type of the properties to search */ + searchPropertyTypeName: string; + /** Convert a WFS feature to a search result */ + featureToSearchResultFunction: (feature: any) => SearchResult; + terria: Terria; + /** How long it takes to zoom in when a search result is clicked */ + flightDurationSeconds?: number; + /** Apply a function to search text before it gets passed to the service. Useful for changing case */ + transformSearchText?: (searchText: string) => string; + /** Return true if a feature should be included in search results */ + searchResultFilterFunction?: (feature: any) => boolean; + /** Return a score that gets used to sort results (in descending order) */ + searchResultScoreFunction?: (feature: any, searchText: string) => number; + /** name of the search provider */ + name: string; +} + +export default class WebFeatureServiceSearchProvider extends SearchProvider { + private _wfsServiceUrl: uri.URI; + private _searchPropertyName: string; + private _searchPropertyTypeName: string; + private _featureToSearchResultFunction: (feature: any) => SearchResult; + flightDurationSeconds: number; + readonly terria: Terria; + private _transformSearchText: ((searchText: string) => string) | undefined; + private _searchResultFilterFunction: ((feature: any) => boolean) | undefined; + private _searchResultScoreFunction: + | ((feature: any, searchText: string) => number) + | undefined; + cancelRequest?: () => void; + private _waitingForResults: boolean = false; + + constructor(options: WebFeatureServiceSearchProviderOptions) { + super(); + this._wfsServiceUrl = new URI(options.wfsServiceUrl); + this._searchPropertyName = options.searchPropertyName; + this._searchPropertyTypeName = options.searchPropertyTypeName; + this._featureToSearchResultFunction = options.featureToSearchResultFunction; + this.terria = options.terria; + this.flightDurationSeconds = defaultValue( + options.flightDurationSeconds, + 1.5 + ); + this._transformSearchText = options.transformSearchText; + this._searchResultFilterFunction = options.searchResultFilterFunction; + this._searchResultScoreFunction = options.searchResultScoreFunction; + this.name = options.name; + } + + getXml(): Promise { + const resource = new Resource({ url: this._wfsServiceUrl.toString() }); + this._waitingForResults = true; + const xmlPromise = resource.fetchXML()!; + this.cancelRequest = resource.request.cancelFunction; + return xmlPromise.finally(() => { + this._waitingForResults = false; + }); + } + + protected doSearch( + searchText: string, + results: SearchProviderResults + ): Promise { + results.results.length = 0; + results.message = undefined; + + if (this._waitingForResults) { + // There's been a new search! Cancel the previous one. + if (this.cancelRequest !== undefined) { + this.cancelRequest(); + this.cancelRequest = undefined; + } + this._waitingForResults = false; + } + + const originalSearchText = searchText; + + searchText = searchText.trim(); + if (this._transformSearchText !== undefined) { + searchText = this._transformSearchText(searchText); + } + if (searchText.length < 2) { + return Promise.resolve(); + } + + // Support for matchCase="false" is patchy, but we try anyway + const filter = ` + ${this._searchPropertyName} + *${searchText}*`; + + this._wfsServiceUrl.setSearch({ + service: "WFS", + request: "GetFeature", + typeName: this._searchPropertyTypeName, + version: "1.1.0", + srsName: "urn:ogc:def:crs:EPSG::4326", // srsName must be formatted like this for correct lat/long order >:( + filter: filter + }); + + return this.getXml() + .then((xml: any) => { + const json: any = xml2json(xml); + let features: any[]; + if (json === undefined) { + results.message = i18next.t("viewModels.searchErrorOccurred"); + return; + } + + if (json.member !== undefined) { + features = json.member; + } else if (json.featureMember !== undefined) { + features = json.featureMember; + } else { + results.message = i18next.t("viewModels.searchNoPlaceNames"); + return; + } + + // if there's only one feature, make it an array + if (!Array.isArray(features)) { + features = [features]; + } + + const resultSet = new Set(); + + runInAction(() => { + if (this._searchResultFilterFunction !== undefined) { + features = features.filter(this._searchResultFilterFunction); + } + + if (features.length === 0) { + results.message = i18next.t("viewModels.searchNoPlaceNames"); + return; + } + + if (this._searchResultScoreFunction !== undefined) { + features = features.sort( + (featureA, featureB) => + this._searchResultScoreFunction!(featureB, originalSearchText) - + this._searchResultScoreFunction!(featureA, originalSearchText) + ); + } + + let searchResults = features + .map(this._featureToSearchResultFunction) + .map((result) => { + result.clickAction = createZoomToFunction(this, result.location); + return result; + }); + + // If we don't have a scoring function, sort the search results now + // We can't do this earlier because we don't know what the schema of the unprocessed feature looks like + if (this._searchResultScoreFunction === undefined) { + // Put shorter results first + // They have a larger percentage of letters that the user actually typed in them + searchResults = searchResults.sort( + (featureA, featureB) => + featureA.name.length - featureB.name.length + ); + } + + // Remove results that have the same name and are close to each other + searchResults = searchResults.filter((result) => { + const hash = `${result.name},${result.location?.latitude.toFixed( + 1 + )},${result.location?.longitude.toFixed(1)}`; + if (resultSet.has(hash)) { + return false; + } + resultSet.add(hash); + return true; + }); + + // append new results to all results + results.results.push(...searchResults); + }); + }) + .catch((e) => { + if (results.isCanceled) { + // A new search has superseded this one, so ignore the result. + return; + } + results.message = i18next.t("viewModels.searchErrorOccurred"); + }); + } +} + +function createZoomToFunction( + model: WebFeatureServiceSearchProvider, + location: any +) { + // Server does not return information of a bounding box, just a location. + // bboxSize is used to expand a point + const bboxSize = 0.2; + const rectangle = zoomRectangleFromPoint( + location.latitude, + location.longitude, + bboxSize + ); + + return function () { + model.terria.currentViewer.zoomTo(rectangle, model.flightDurationSeconds); + }; +} diff --git a/lib/Models/Catalog/Ows/WebMapServiceCapabilities.ts b/lib/Models/Catalog/Ows/WebMapServiceCapabilities.ts index bffbafb677a..12bb1ea326f 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCapabilities.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCapabilities.ts @@ -132,7 +132,7 @@ type Mutable = { -readonly [P in keyof T]: T[P] }; export function getRectangleFromLayer( layer: CapabilitiesLayer ): StratumFromTraits | undefined { - var egbb = layer.EX_GeographicBoundingBox; // required in WMS 1.3.0 + const egbb = layer.EX_GeographicBoundingBox; // required in WMS 1.3.0 if (egbb) { return { west: egbb.westBoundLongitude, @@ -141,7 +141,7 @@ export function getRectangleFromLayer( north: egbb.northBoundLatitude }; } else { - var llbb = layer.LatLonBoundingBox; // required in WMS 1.0.0 through 1.1.1 + const llbb = layer.LatLonBoundingBox; // required in WMS 1.0.0 through 1.1.1 if (llbb) { return { west: llbb.minx, diff --git a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts index 4426c8d540b..ed847705c89 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCapabilitiesStratum.ts @@ -376,11 +376,11 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( result.push({ layerName: layerName, styles: styles.map((style) => { - var wmsLegendUrl = isReadOnlyArray(style.LegendURL) + const wmsLegendUrl = isReadOnlyArray(style.LegendURL) ? style.LegendURL[0] : style.LegendURL; - var legendUri, legendMimeType; + let legendUri, legendMimeType; if ( wmsLegendUrl && wmsLegendUrl.OnlineResource && @@ -760,7 +760,7 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( get discreteTimes(): { time: string; tag: string | undefined }[] | undefined { const result = []; - for (let layer of this.capabilitiesLayers.values()) { + for (const layer of this.capabilitiesLayers.values()) { if (!layer) { continue; } @@ -943,7 +943,7 @@ export default class WebMapServiceCapabilitiesStratum extends LoadableStratum( // This is used to flag subsets (dimensions) which have multiple values // Each element in this array represents the **actual** value used for a subset which has multiple values - let duplicateSubsetValues: StratumFromTraits[] = []; + const duplicateSubsetValues: StratumFromTraits[] = []; // Get dimensionSubsets const dimensionSubsets: { key: string; value: string }[] = []; diff --git a/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts b/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts index 58517a153ae..5355642dc71 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCatalogGroup.ts @@ -388,7 +388,7 @@ export default class WebMapServiceCatalogGroup extends GetCapabilitiesMixin( } protected async forceLoadMembers(): Promise { - let getCapabilitiesStratum = ( + const getCapabilitiesStratum = ( this.strata.get(GetCapabilitiesMixin.getCapabilitiesStratumName) ); if (getCapabilitiesStratum !== undefined) { diff --git a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts index 44ae738148d..54f2b0ad886 100644 --- a/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts +++ b/lib/Models/Catalog/Ows/WebMapServiceCatalogItem.ts @@ -653,7 +653,7 @@ class WebMapServiceCatalogItem // Try to set selectedId to value stored in `styles` trait for this `layerIndex` // The `styles` parameter is CSV, a style for each layer - let selectedId = this.styles?.split(",")?.[layerIndex]; + const selectedId = this.styles?.split(",")?.[layerIndex]; return { name, diff --git a/lib/Models/Catalog/Ows/WebMapTileServiceCatalogItem.ts b/lib/Models/Catalog/Ows/WebMapTileServiceCatalogItem.ts index 57cb6735239..ac9933b981d 100644 --- a/lib/Models/Catalog/Ows/WebMapTileServiceCatalogItem.ts +++ b/lib/Models/Catalog/Ows/WebMapTileServiceCatalogItem.ts @@ -262,7 +262,7 @@ class GetCapabilitiesStratum extends LoadableStratum( @computed get capabilitiesLayer(): Readonly { - let result = this.catalogItem.layer + const result = this.catalogItem.layer ? this.capabilities.findLayer(this.catalogItem.layer) : undefined; return result; @@ -287,11 +287,10 @@ class GetCapabilitiesStratum extends LoadableStratum( result.push({ layerName: layer?.Identifier, styles: styles.map((style: CapabilitiesStyle) => { - let wmtsLegendUrl: WmtsCapabilitiesLegend | undefined = isReadOnlyArray( - style.LegendURL - ) - ? style.LegendURL[0] - : style.LegendURL; + const wmtsLegendUrl: WmtsCapabilitiesLegend | undefined = + isReadOnlyArray(style.LegendURL) + ? style.LegendURL[0] + : style.LegendURL; let legendUri, legendMimeType; if (wmtsLegendUrl && wmtsLegendUrl["xlink:href"]) { legendUri = new URI(decodeURIComponent(wmtsLegendUrl["xlink:href"])); @@ -352,9 +351,9 @@ class GetCapabilitiesStratum extends LoadableStratum( continue; } - var levelZeroTopLeftCorner = levelZeroMatrix.TopLeftCorner.split(" "); - var startX = parseFloat(levelZeroTopLeftCorner[0]); - var startY = parseFloat(levelZeroTopLeftCorner[1]); + const levelZeroTopLeftCorner = levelZeroMatrix.TopLeftCorner.split(" "); + const startX = parseFloat(levelZeroTopLeftCorner[0]); + const startY = parseFloat(levelZeroTopLeftCorner[1]); const rectangleInMeters = standardTilingScheme.rectangleToNativeRectangle( standardTilingScheme.rectangle ); diff --git a/lib/Models/Catalog/Ows/WebProcessingServiceCatalogFunction.ts b/lib/Models/Catalog/Ows/WebProcessingServiceCatalogFunction.ts index d11037c162b..69ea0ca6a0a 100644 --- a/lib/Models/Catalog/Ows/WebProcessingServiceCatalogFunction.ts +++ b/lib/Models/Catalog/Ows/WebProcessingServiceCatalogFunction.ts @@ -272,7 +272,7 @@ export default class WebProcessingServiceCatalogFunction extends XmlRequestMixin protected async createJob(id: string) { const job = new WebProcessingServiceCatalogFunctionJob(id, this.terria); - let dataInputs = filterOutUndefined( + const dataInputs = filterOutUndefined( await Promise.all( this.functionParameters .filter((p) => isDefined(p.value) && p.value !== null) @@ -332,7 +332,7 @@ export default class WebProcessingServiceCatalogFunction extends XmlRequestMixin } async convertParameterToInput(parameter: FunctionParameter) { - let converter = parameterTypeToConverter(parameter); + const converter = parameterTypeToConverter(parameter); const result = converter?.parameterToInput(parameter); if (!isDefined(result)) { @@ -514,8 +514,8 @@ const RectangleConverter = { ) { return undefined; } - var code = Reproject.crsStringToCode(input.BoundingBoxData.Default.CRS); - var usedCrs = input.BoundingBoxData.Default.CRS; + let code = Reproject.crsStringToCode(input.BoundingBoxData.Default.CRS); + let usedCrs = input.BoundingBoxData.Default.CRS; // Find out if Terria's CRS is supported. if ( code !== Reproject.TERRIA_CRS && @@ -656,7 +656,7 @@ function simpleGeoJsonDataConverter(schemaType: string, klass: any) { return undefined; } - var schema = input.ComplexData.Default.Format.Schema; + const schema = input.ComplexData.Default.Format.Schema; if (schema.indexOf("http://geojson.org/geojson-spec.html#") !== 0) { return undefined; } diff --git a/lib/Models/Catalog/SdmxJson/SdmxJsonDataflowStratum.ts b/lib/Models/Catalog/SdmxJson/SdmxJsonDataflowStratum.ts index c55283ec3ce..553ed212f69 100644 --- a/lib/Models/Catalog/SdmxJson/SdmxJsonDataflowStratum.ts +++ b/lib/Models/Catalog/SdmxJson/SdmxJsonDataflowStratum.ts @@ -78,7 +78,7 @@ export class SdmxJsonDataflowStratum extends LoadableStratum( catalogItem: SdmxJsonCatalogItem ): Promise { // Load dataflow (+ all related references) - let dataflowStructure: SdmxJsonStructureMessage = + const dataflowStructure: SdmxJsonStructureMessage = await loadSdmxJsonStructure( proxyCatalogItemUrl( catalogItem, @@ -153,7 +153,7 @@ export class SdmxJsonDataflowStratum extends LoadableStratum( this.sdmxJsonDataflow?.dataflow.annotations ?.filter((a) => a.type === "EXT_RESOURCE" && a.text) .map((annotation) => { - let text = annotation.texts?.[i18next.language] ?? annotation.text!; + const text = annotation.texts?.[i18next.language] ?? annotation.text!; const title = text.includes("|") ? text.split("|")[0] : undefined; const url = text.includes("|") ? text.split("|")[1] : text; return createStratumInstance(MetadataUrlTraits, { title, url }); @@ -292,7 +292,7 @@ export class SdmxJsonDataflowStratum extends LoadableStratum( let options: StratumFromTraits[] = []; // Get codes by merging allowedOptionIds with codelist - let filteredCodesList = + const filteredCodesList = (allowedOptionIds.size > 0 ? codelist?.codes?.filter((code) => allowedOptionIds.has(code.id!) @@ -399,7 +399,7 @@ export class SdmxJsonDataflowStratum extends LoadableStratum( ?.filter((override) => override.type === "unit-measure" && override.id) .map((override) => { // Find dimension/attribute id with concept or codelist override - let dimOrAttr = + const dimOrAttr = this.getAttributionWithConceptOrCodelist(override.id!) ?? this.getDimensionWithConceptOrCodelist(override.id!); @@ -424,7 +424,7 @@ export class SdmxJsonDataflowStratum extends LoadableStratum( ); // Try to get option label if it exists - let frequency = + const frequency = frequencyDim?.options.find((o) => o.id === frequencyDim.selectedId) ?.name ?? frequencyDim?.id; @@ -459,7 +459,7 @@ export class SdmxJsonDataflowStratum extends LoadableStratum( ) .map((override) => { // Find dimension/attribute id with concept or codelist - let dimOrAttr = + const dimOrAttr = this.getAttributionWithConceptOrCodelist(override.id!) ?? this.getDimensionWithConceptOrCodelist(override.id!); @@ -823,7 +823,7 @@ export class SdmxJsonDataflowStratum extends LoadableStratum( const conceptId = conceptUrn?.descendantIds?.[0]; if (!isDefined(conceptId)) return; - let resolvedConceptScheme = + const resolvedConceptScheme = typeof conceptSchemeId === "string" ? this.getConceptScheme(conceptSchemeId) : conceptSchemeId; diff --git a/lib/Models/Catalog/SdmxJson/SdmxJsonServerStratum.ts b/lib/Models/Catalog/SdmxJson/SdmxJsonServerStratum.ts index 4c66d76e1c2..60c71080ad4 100644 --- a/lib/Models/Catalog/SdmxJson/SdmxJsonServerStratum.ts +++ b/lib/Models/Catalog/SdmxJson/SdmxJsonServerStratum.ts @@ -47,7 +47,7 @@ export class SdmxServerStratum extends LoadableStratum(SdmxCatalogGroupTraits) { catalogGroup: SdmxCatalogGroup ): Promise { // Load agency schemes (may be undefined) - let agencySchemes = ( + const agencySchemes = ( await loadSdmxJsonStructure( proxyCatalogItemUrl(catalogGroup, `${catalogGroup.url}/agencyscheme/`), true @@ -55,7 +55,7 @@ export class SdmxServerStratum extends LoadableStratum(SdmxCatalogGroupTraits) { )?.data?.agencySchemes; // Load category schemes (may be undefined) - let categorySchemeResponse = await loadSdmxJsonStructure( + const categorySchemeResponse = await loadSdmxJsonStructure( proxyCatalogItemUrl( catalogGroup, `${catalogGroup.url}/categoryscheme?references=parentsandsiblings` @@ -376,7 +376,7 @@ export class SdmxServerStratum extends LoadableStratum(SdmxCatalogGroupTraits) { id?: string ) { if (!isDefined(id)) return; - let resolvedCategoryScheme = + const resolvedCategoryScheme = typeof categoryScheme === "string" ? this.getCategoryScheme(categoryScheme) : categoryScheme; diff --git a/lib/Models/Catalog/registerCatalogMembers.ts b/lib/Models/Catalog/registerCatalogMembers.ts index f6ffa854679..02a2040d9bd 100644 --- a/lib/Models/Catalog/registerCatalogMembers.ts +++ b/lib/Models/Catalog/registerCatalogMembers.ts @@ -397,7 +397,7 @@ function matchesUrl(regex: RegExp) { } export function matchesExtension(extension: string) { - var regex = new RegExp("\\." + extension + "$", "i"); + const regex = new RegExp("\\." + extension + "$", "i"); return function (url: string) { return Boolean(url.match(regex)); }; diff --git a/lib/Models/Cesium.ts b/lib/Models/Cesium.ts index f54115c838b..27d46fa3f1f 100644 --- a/lib/Models/Cesium.ts +++ b/lib/Models/Cesium.ts @@ -100,16 +100,16 @@ type CreditDisplayElement = { }; // Intermediary -var cartesian3Scratch = new Cartesian3(); -var enuToFixedScratch = new Matrix4(); -var southwestScratch = new Cartesian3(); -var southeastScratch = new Cartesian3(); -var northeastScratch = new Cartesian3(); -var northwestScratch = new Cartesian3(); -var southwestCartographicScratch = new Cartographic(); -var southeastCartographicScratch = new Cartographic(); -var northeastCartographicScratch = new Cartographic(); -var northwestCartographicScratch = new Cartographic(); +const cartesian3Scratch = new Cartesian3(); +const enuToFixedScratch = new Matrix4(); +const southwestScratch = new Cartesian3(); +const southeastScratch = new Cartesian3(); +const northeastScratch = new Cartesian3(); +const northwestScratch = new Cartesian3(); +const southwestCartographicScratch = new Cartographic(); +const southeastCartographicScratch = new Cartographic(); +const northeastCartographicScratch = new Cartographic(); +const northwestCartographicScratch = new Cartographic(); export default class Cesium extends GlobeOrMap { readonly type = "Cesium"; @@ -668,7 +668,7 @@ export default class Cesium extends GlobeOrMap { } // 2. Add new data sources - for (let ds of availableDataSources) { + for (const ds of availableDataSources) { if (!dataSources.contains(ds)) { await dataSources.add(ds); } @@ -986,7 +986,7 @@ export default class Cesium extends GlobeOrMap { * @returns Camera */ private cloneCamera(camera: Camera): Camera { - let result = new Camera(this.scene); + const result = new Camera(this.scene); Cartesian3.clone(camera.position, result.position); Cartesian3.clone(camera.direction, result.direction); Cartesian3.clone(camera.up, result.up); @@ -1350,7 +1350,7 @@ export default class Cesium extends GlobeOrMap { } if (hasUrl(imageryProvider) && providerCoords[imageryProvider.url]) { - var coords = providerCoords[imageryProvider.url]; + const coords = providerCoords[imageryProvider.url]; promises.push( imageryProvider .pickFeatures( @@ -1729,7 +1729,7 @@ export default class Cesium extends GlobeOrMap { } } -var boundingSphereScratch = new BoundingSphere(); +const boundingSphereScratch = new BoundingSphere(); function zoomToDataSource( cesium: Cesium, @@ -1744,11 +1744,11 @@ function zoomToDataSource( return false; } - var entities = target.entities.values; + const entities = target.entities.values; - var boundingSpheres = []; - for (var i = 0, len = entities.length; i < len; i++) { - var state = BoundingSphereState.PENDING; + const boundingSpheres = []; + for (let i = 0, len = entities.length; i < len; i++) { + let state = BoundingSphereState.PENDING; try { // TODO: missing Cesium type info state = (dataSourceDisplay).getBoundingSphere( @@ -1769,7 +1769,7 @@ function zoomToDataSource( // Test if boundingSpheres is empty to avoid zooming to nowhere if (boundingSpheres.length > 0 && _lastZoomTarget === target) { - var boundingSphere = + const boundingSphere = BoundingSphere.fromBoundingSpheres(boundingSpheres); flyToPromise = flyToBoundingSpherePromise( cesium.scene.camera, diff --git a/lib/Models/Definition/createCombinedModel.ts b/lib/Models/Definition/createCombinedModel.ts index cb94f03c75b..a7082fa817b 100644 --- a/lib/Models/Definition/createCombinedModel.ts +++ b/lib/Models/Definition/createCombinedModel.ts @@ -132,7 +132,7 @@ class CombinedStrata implements Map> { const result = new Map>(); // Add the strata fro the top - for (let key of this.top.strata.keys()) { + for (const key of this.top.strata.keys()) { const topStratum = this.top.strata.get(key); const bottomStratum = this.bottom.strata.get(key); @@ -159,7 +159,7 @@ class CombinedStrata implements Map> { } // Add any strata that are only in the bottom - for (let key of this.bottom.strata.keys()) { + for (const key of this.bottom.strata.keys()) { if (this.top.strata.has(key)) { continue; } diff --git a/lib/Models/Definition/upsertModelFromJson.ts b/lib/Models/Definition/upsertModelFromJson.ts index c8c051b5083..9ef4198f575 100644 --- a/lib/Models/Definition/upsertModelFromJson.ts +++ b/lib/Models/Definition/upsertModelFromJson.ts @@ -62,7 +62,7 @@ export default function upsertModelFromJson( ); } - let id = (parentId || "") + "/" + localId; + const id = (parentId || "") + "/" + localId; let idIncrement = 1; uniqueId = id; model = terria.getModelById(BaseModel, uniqueId); diff --git a/lib/Models/FunctionParameters/PointParameter.ts b/lib/Models/FunctionParameters/PointParameter.ts index 4fba82e677f..6f7f3eba1fb 100644 --- a/lib/Models/FunctionParameters/PointParameter.ts +++ b/lib/Models/FunctionParameters/PointParameter.ts @@ -30,7 +30,7 @@ export default class PointParameter * Get feature as geojson for display on map. */ static getGeoJsonFeature(value: CartographicPoint): Feature { - var coordinates = [ + const coordinates = [ CesiumMath.toDegrees(value.longitude), CesiumMath.toDegrees(value.latitude), value.height diff --git a/lib/Models/ItemSearchProviders/IndexedItemSearchProvider.ts b/lib/Models/ItemSearchProviders/IndexedItemSearchProvider.ts index e79c6a63ad5..f2840cfa569 100644 --- a/lib/Models/ItemSearchProviders/IndexedItemSearchProvider.ts +++ b/lib/Models/ItemSearchProviders/IndexedItemSearchProvider.ts @@ -238,7 +238,7 @@ export default class IndexedItemSearchProvider extends ItemSearchProvider { // The record can have a bunch of arbitrary properties and a few known // properties. We use the latitude, longitude, height & radius for // constructing a zoom target for the search result. - let { latitude, longitude, height, ...properties } = record; + const { latitude, longitude, height, ...properties } = record; const _latitude = parseFloat(latitude); const _longitude = parseFloat(longitude); const _featureHeight = parseFloat(height); diff --git a/lib/Models/Leaflet.ts b/lib/Models/Leaflet.ts index 84af5588269..99e7572f6b4 100644 --- a/lib/Models/Leaflet.ts +++ b/lib/Models/Leaflet.ts @@ -256,7 +256,7 @@ export default class Leaflet extends GlobeOrMap { */ private _initProgressEvent() { const onTileLoadChange = () => { - var tilesLoadingCount = 0; + let tilesLoadingCount = 0; this.map.eachLayer(function (layerOrGridlayer) { // _tiles is protected but our knockout-loading-logic accesses it here anyway @@ -477,7 +477,7 @@ export default class Leaflet extends GlobeOrMap { } // 2. Add new data sources - for (let ds of availableDataSources) { + for (const ds of availableDataSources) { if (!dataSources.contains(ds) && ds.show) { await dataSources.add(ds); } diff --git a/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts b/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts index 2072841b206..ededdb55867 100644 --- a/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts +++ b/lib/Models/SearchProviders/AustralianGazetteerSearchProvider.ts @@ -160,7 +160,7 @@ const searchResultScoreFunction = function ( feature = feature.Gazetteer_of_Australia; // Taken from original GazetteerSearchProviderViewModel in v7 - var featureTypes = [ + const featureTypes = [ "CONT", "STAT", "URBN", diff --git a/lib/Models/Terria.ts b/lib/Models/Terria.ts index 24773609c63..e2f77c7f7ad 100644 --- a/lib/Models/Terria.ts +++ b/lib/Models/Terria.ts @@ -846,7 +846,7 @@ export default class Terria { type: Class, id: string ): T | undefined { - let model = this.getModelById(type, id); + const model = this.getModelById(type, id); if (model) { return model; } else { @@ -2008,7 +2008,7 @@ export default class Terria { @action async loadPickedFeatures(pickedFeatures: JsonObject): Promise { let vectorFeatures: TerriaFeature[] = []; - let featureIndex: Record = {}; + const featureIndex: Record = {}; if (Array.isArray(pickedFeatures.entities)) { // Build index of terria features by a hash of their properties. @@ -2109,7 +2109,7 @@ export default class Terria { // SecurityError can arise if 3rd party cookies are blocked in Chrome and we're served in an iFrame return null; } - var v = window.localStorage.getItem(this.appName + "." + key); + const v = window.localStorage.getItem(this.appName + "." + key); if (v === "true") { return true; } else if (v === "false") { diff --git a/lib/Models/TimelineStack.ts b/lib/Models/TimelineStack.ts index 8c8ac14897e..03b9f39742c 100644 --- a/lib/Models/TimelineStack.ts +++ b/lib/Models/TimelineStack.ts @@ -142,7 +142,7 @@ export default class TimelineStack { */ @action addToTop(item: TimeVarying) { - var currentIndex = this.items.indexOf(item); + const currentIndex = this.items.indexOf(item); this.items.unshift(item); if (currentIndex > -1) { this.items.splice(currentIndex, 1); @@ -157,7 +157,7 @@ export default class TimelineStack { */ @action remove(item: TimeVarying) { - var index = this.items.indexOf(item); + const index = this.items.indexOf(item); this.items.splice(index, 1); } @@ -177,7 +177,7 @@ export default class TimelineStack { */ @action promoteToTop(item: TimeVarying) { - var currentIndex = this.items.indexOf(item); + const currentIndex = this.items.indexOf(item); if (currentIndex > -1) { this.addToTop(item); } diff --git a/lib/Models/UserDrawing.ts b/lib/Models/UserDrawing.ts index a4b59302941..12b992f2205 100644 --- a/lib/Models/UserDrawing.ts +++ b/lib/Models/UserDrawing.ts @@ -170,13 +170,13 @@ export default class UserDrawing extends MappableMixin( * SVG element for point drawn when user clicks. * http://stackoverflow.com/questions/24869733/how-to-draw-custom-dynamic-billboards-in-cesium-js */ - var svgDataDeclare = "data:image/svg+xml,"; - var svgPrefix = + const svgDataDeclare = "data:image/svg+xml,"; + const svgPrefix = ''; - var svgCircle = + const svgCircle = ' '; - var svgSuffix = ""; - var svgString = svgPrefix + svgCircle + svgSuffix; + const svgSuffix = ""; + const svgString = svgPrefix + svgCircle + svgSuffix; // create the cesium entity return svgDataDeclare + svgString; @@ -324,7 +324,7 @@ export default class UserDrawing extends MappableMixin( * Add new point to list of pointEntities */ private addPointToPointEntities(name: string, position: Cartesian3) { - var pointEntity = new Entity({ + const pointEntity = new Entity({ name: name, position: new ConstantPositionProperty(position), billboard: { @@ -562,7 +562,7 @@ export default class UserDrawing extends MappableMixin( ? this.messageHeader() : this.messageHeader) + "
"; - let innerMessage = isDefined(this.onMakeDialogMessage) + const innerMessage = isDefined(this.onMakeDialogMessage) ? this.onMakeDialogMessage() : ""; @@ -602,7 +602,7 @@ export default class UserDrawing extends MappableMixin( getPointsForShape() { if (isDefined(this.pointEntities.entities)) { const pos = []; - for (var i = 0; i < this.pointEntities.entities.values.length; i++) { + for (let i = 0; i < this.pointEntities.entities.values.length; i++) { const obj = this.pointEntities.entities.values[i]; if (isDefined(obj.position)) { const position = obj.position.getValue( diff --git a/lib/ReactViews/.eslintrc b/lib/ReactViews/.eslintrc deleted file mode 100644 index ff88a651c6c..00000000000 --- a/lib/ReactViews/.eslintrc +++ /dev/null @@ -1,40 +0,0 @@ -{ - "parser": "@babel/eslint-parser", - "parserOptions": { - "requireConfigFile": false, - "ecmaVersion": 2018, - "ecmaFeatures": { - "jsx": true, - "modules": true, - "legacyDecorators": true - } - }, - "env": { - "browser": true, - "commonjs": true - }, - "rules": { - "react/no-danger": 0, - "no-return-assign": 0, - "valid-jsdoc": 0, - "require-jsdoc": 0, - "consistent-return": 0, - "no-else-return": 0, - "no-multi-spaces": 0, - "no-multi-str": 1, - "vars-on-top": 1, - "handle-callback-err": 1, - "brace-style": [1, "1tbs", { "allowSingleLine": true }], - "camelcase": [1, { "properties": "always" }], - "indent": [0, 4, { "SwitchCase": 1 }], - "key-spacing": [1, { "beforeColon": false, "afterColon": true }], - "no-mixed-spaces-and-tabs": 1, - "no-multiple-empty-lines": [1, { "max": 1 }], - "one-var": [1, "never"], - "space-before-blocks": [1, "always"], - "space-in-parens": [1, "never"], - "spaced-comment": [1, "always"], - "no-var": 1, - "react/no-string-refs": 0 - } -} diff --git a/lib/ReactViews/Custom/Chart/ChartExpandAndDownloadButtons.tsx b/lib/ReactViews/Custom/Chart/ChartExpandAndDownloadButtons.tsx index fb25511c762..4887152dad4 100644 --- a/lib/ReactViews/Custom/Chart/ChartExpandAndDownloadButtons.tsx +++ b/lib/ReactViews/Custom/Chart/ChartExpandAndDownloadButtons.tsx @@ -73,7 +73,9 @@ class ChartExpandAndDownloadButtons extends React.Component { try { terria.addModel(itemToExpand); - } catch {} + } catch { + return; + } (await workbench.add(itemToExpand)).raiseError(terria, undefined, true); }); } @@ -104,7 +106,7 @@ class ChartExpandAndDownloadButtons extends React.Component { } // The downloads and download names default to the sources and source names if not defined. - let downloads: string[] = filterOutUndefined( + const downloads: string[] = filterOutUndefined( this.props.downloads || this.sourceItems.map((item) => hasTraits(item, UrlTraits, "url") ? item.url : undefined diff --git a/lib/ReactViews/Custom/Chart/chart.scss.d.ts b/lib/ReactViews/Custom/Chart/chart.scss.d.ts index cde02af0553..83209f11eb0 100644 --- a/lib/ReactViews/Custom/Chart/chart.scss.d.ts +++ b/lib/ReactViews/Custom/Chart/chart.scss.d.ts @@ -3,5 +3,5 @@ interface CssExports { 'noData': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Custom/Chart/legends.scss.d.ts b/lib/ReactViews/Custom/Chart/legends.scss.d.ts index 30cfe1352bb..67f62cde1fb 100644 --- a/lib/ReactViews/Custom/Chart/legends.scss.d.ts +++ b/lib/ReactViews/Custom/Chart/legends.scss.d.ts @@ -3,5 +3,5 @@ interface CssExports { 'legends': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Custom/Collapsible/Collapsible.tsx b/lib/ReactViews/Custom/Collapsible/Collapsible.tsx index a17541724de..62a566d19ac 100644 --- a/lib/ReactViews/Custom/Collapsible/Collapsible.tsx +++ b/lib/ReactViews/Custom/Collapsible/Collapsible.tsx @@ -76,7 +76,7 @@ const Collapsible: React.FC = observer((props) => { }; return ( - + <> = observer((props) => { ) : null} - + ); }); diff --git a/lib/ReactViews/Custom/Collapsible/collapsible.scss.d.ts b/lib/ReactViews/Custom/Collapsible/collapsible.scss.d.ts index 20e770d0baa..b3650847911 100644 --- a/lib/ReactViews/Custom/Collapsible/collapsible.scss.d.ts +++ b/lib/ReactViews/Custom/Collapsible/collapsible.scss.d.ts @@ -10,5 +10,5 @@ interface CssExports { 'isOpen': string; 'root': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Custom/FeedbackLinkCustomComponent.tsx b/lib/ReactViews/Custom/FeedbackLinkCustomComponent.tsx index 5653945cbe6..87cb70e52aa 100644 --- a/lib/ReactViews/Custom/FeedbackLinkCustomComponent.tsx +++ b/lib/ReactViews/Custom/FeedbackLinkCustomComponent.tsx @@ -80,7 +80,7 @@ export default class FeedbackLinkCustomComponent extends CustomComponent { viewState={context.viewState} emailMessage={node.attribs?.["email-message"]} feedbackMessage={node.attribs?.["feedback-message"]} - > + /> ); } } diff --git a/lib/ReactViews/Custom/SOSChartCustomComponent.ts b/lib/ReactViews/Custom/SOSChartCustomComponent.ts index a488419e3ea..22d5de61051 100644 --- a/lib/ReactViews/Custom/SOSChartCustomComponent.ts +++ b/lib/ReactViews/Custom/SOSChartCustomComponent.ts @@ -16,7 +16,7 @@ export default class SOSChartCustomComponent extends ChartCustomComponent ) )} diff --git a/lib/ReactViews/DataCatalog/data-catalog-item.scss.d.ts b/lib/ReactViews/DataCatalog/data-catalog-item.scss.d.ts index 9f6a43c3ef6..c9b7d1676ef 100644 --- a/lib/ReactViews/DataCatalog/data-catalog-item.scss.d.ts +++ b/lib/ReactViews/DataCatalog/data-catalog-item.scss.d.ts @@ -15,5 +15,5 @@ interface CssExports { 'btnTrash': string; 'root': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx index 10d48f53479..ff1a7d59d88 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx +++ b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/AddData.jsx @@ -229,7 +229,7 @@ const AddData = createReactClass({ options={this.state.localDataTypes} selected={this.state.localDataType} selectOption={this.selectLocalOption} - matchWidth={true} + matchWidth theme={dropdownTheme} /> {this.state.localDataType?.description @@ -263,7 +263,7 @@ const AddData = createReactClass({ options={this.state.remoteDataTypes} selected={this.state.remoteDataType} selectOption={this.selectRemoteOption} - matchWidth={true} + matchWidth theme={dropdownTheme} /> {this.state.remoteDataType?.description diff --git a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx index cec82e8d5b6..98a5fdfcb18 100644 --- a/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx +++ b/lib/ReactViews/ExplorerWindow/Tabs/MyDataTab/MyDataTab.jsx @@ -184,7 +184,7 @@ class MyDataTab extends React.Component { items={ this.props.terria.catalog.userAddedDataGroup.memberModels } - removable={true} + removable viewState={this.props.viewState} terria={this.props.terria} /> diff --git a/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx b/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx index 29f4b8847e9..e4f44d30eaf 100644 --- a/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx +++ b/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx @@ -293,7 +293,7 @@ export class FeatureInfoSection extends React.Component { const feature = this.props.feature; const currentTime = this.currentTimeIfAvailable ?? JulianDate.now(); - let description: string | undefined = + const description: string | undefined = feature.description?.getValue(currentTime); if (isDefined(description)) return description; diff --git a/lib/ReactViews/Feedback/FeedbackForm.tsx b/lib/ReactViews/Feedback/FeedbackForm.tsx index 675f47de837..2f7427ce256 100644 --- a/lib/ReactViews/Feedback/FeedbackForm.tsx +++ b/lib/ReactViews/Feedback/FeedbackForm.tsx @@ -219,7 +219,7 @@ class FeedbackForm extends React.Component { value={this.state.name} onChange={this.updateName} autoComplete="off" - > + /> { value={this.state.email} onChange={this.updateEmail} autoComplete="off" - > + /> = (props: TextAreaProps) => { } }} invalidValue={!valueIsValid} - > + /> ); }; diff --git a/lib/ReactViews/Generic/TooltipWrapper.tsx b/lib/ReactViews/Generic/TooltipWrapper.tsx index fde7b90f406..c882d05ca81 100644 --- a/lib/ReactViews/Generic/TooltipWrapper.tsx +++ b/lib/ReactViews/Generic/TooltipWrapper.tsx @@ -86,7 +86,7 @@ class TooltipWrapperRaw extends React.Component { const rootElement = this.rootRef.current; // Why .firstChild? Because we can't attach a ref to a render prop unless whatever's passed in passes the ref through to its first dom element - const launcherElement = rootElement!?.firstChild!; + const launcherElement = rootElement?.firstChild!; if (!launcherElement || !tooltipTextElement) { return; } diff --git a/lib/ReactViews/Generic/prompt.scss.d.ts b/lib/ReactViews/Generic/prompt.scss.d.ts index b04fb50e79f..f65f751b6a9 100644 --- a/lib/ReactViews/Generic/prompt.scss.d.ts +++ b/lib/ReactViews/Generic/prompt.scss.d.ts @@ -5,5 +5,5 @@ interface CssExports { 'isVisible': string; 'prompt': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Guidance/guidance-dot.scss.d.ts b/lib/ReactViews/Guidance/guidance-dot.scss.d.ts index 6c48ae21245..42956813c2c 100644 --- a/lib/ReactViews/Guidance/guidance-dot.scss.d.ts +++ b/lib/ReactViews/Guidance/guidance-dot.scss.d.ts @@ -10,5 +10,5 @@ interface CssExports { 'innerClone': string; 'oval': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Guidance/guidance.scss.d.ts b/lib/ReactViews/Guidance/guidance.scss.d.ts index 3557d226c19..8e914018e72 100644 --- a/lib/ReactViews/Guidance/guidance.scss.d.ts +++ b/lib/ReactViews/Guidance/guidance.scss.d.ts @@ -8,5 +8,5 @@ interface CssExports { 'indicatorEnabled': string; 'indicatorWrapper': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/HelpScreens/help-panel.scss.d.ts b/lib/ReactViews/HelpScreens/help-panel.scss.d.ts index 829bc3dd237..65aa42050be 100644 --- a/lib/ReactViews/HelpScreens/help-panel.scss.d.ts +++ b/lib/ReactViews/HelpScreens/help-panel.scss.d.ts @@ -30,5 +30,5 @@ interface CssExports { 'viewer-selector': string; 'viewerSelector': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Map/BottomBar/Credits/DataAttribution/DataAttributionModal.tsx b/lib/ReactViews/Map/BottomBar/Credits/DataAttribution/DataAttributionModal.tsx index b5b1856e5ca..d7b597dc0c7 100644 --- a/lib/ReactViews/Map/BottomBar/Credits/DataAttribution/DataAttributionModal.tsx +++ b/lib/ReactViews/Map/BottomBar/Credits/DataAttribution/DataAttributionModal.tsx @@ -71,7 +71,7 @@ export const DataAttributionModal: FC = observer( aria-hidden="true" pseudoBg css={{ top: 0, left: 0, zIndex: 99989 }} - > + /> diff --git a/lib/ReactViews/Map/BottomBar/Credits/MapCredits.tsx b/lib/ReactViews/Map/BottomBar/Credits/MapCredits.tsx index 05a8a30a118..5800d416b4b 100644 --- a/lib/ReactViews/Map/BottomBar/Credits/MapCredits.tsx +++ b/lib/ReactViews/Map/BottomBar/Credits/MapCredits.tsx @@ -52,7 +52,7 @@ export const MapCredits: FC = observer( {!hideTerriaLogo ? : null} - + {currentViewer.attributions && currentViewer.attributions.length > 0 ? ( diff --git a/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx b/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx index 2fbc49aacdf..ce939e8a7c6 100644 --- a/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx +++ b/lib/ReactViews/Map/BottomLeftBar/BottomLeftBar.tsx @@ -73,7 +73,7 @@ const BottomLeftBar: FC = observer(() => { } onClick={() => viewState.runStories()} primary={!isNotificationActive} @@ -89,7 +89,7 @@ const BottomLeftBar: FC = observer(() => { height="18px" style={{ paddingLeft: "8px" }} src="build/TerriaJS/images/google_on_non_white_hdpi.png" - > + /> )} {/* On screen data attributions. At the moment, this supports only Cesium viewer. Needed for Google Photorealistic 3D Tiles diff --git a/lib/ReactViews/Map/MapNavigation/CollapsedNavigation.tsx b/lib/ReactViews/Map/MapNavigation/CollapsedNavigation.tsx index 57b1ef8c648..81fb527bf89 100644 --- a/lib/ReactViews/Map/MapNavigation/CollapsedNavigation.tsx +++ b/lib/ReactViews/Map/MapNavigation/CollapsedNavigation.tsx @@ -85,7 +85,7 @@ const CollapsedNavigationPanel: React.FC = observer( const viewState = useViewState(); const theme = useTheme(); const { t, i18n } = useTranslation(); - let items = props.items; + const items = props.items; return ( { role="presentation" aria-hidden="true" pseudoBg - > - + /> + ); }); diff --git a/lib/ReactViews/Map/MapNavigation/Items/Compass/GyroscopeGuidance.jsx b/lib/ReactViews/Map/MapNavigation/Items/Compass/GyroscopeGuidance.jsx index b1ff9cf0ff5..ecc8a55b637 100644 --- a/lib/ReactViews/Map/MapNavigation/Items/Compass/GyroscopeGuidance.jsx +++ b/lib/ReactViews/Map/MapNavigation/Items/Compass/GyroscopeGuidance.jsx @@ -134,72 +134,70 @@ export function GyroscopeGuidance(props) { const controlsMapIcon = useRef(); const { t } = useTranslation(); return ( - <> +
+ } + onClick={() => setControlPanelOpen(!controlPanelOpen)} + inverted + css={` + svg { + margin: 0px; + width: 25px; + height: 25px; + } + `} + />
e.preventDefault()} css={` position: relative; `} > - } - onClick={() => setControlPanelOpen(!controlPanelOpen)} - inverted - css={` - svg { - margin: 0px; - width: 25px; - height: 25px; + p.theme.textBlack}; } `} - /> -
e.preventDefault()} - css={` - position: relative; - `} + refForCaret={controlsMapIcon} + isOpen={controlPanelOpen} + onOpenChanged={() => controlPanelOpen} + // onDismissed={() => setControlPanelOpen(false)} + btnTitle={t("compass.guidanceBtnTitle")} + btnText={t("compass.guidanceBtnText")} + viewState={props.viewState} + smallScreen={props.viewState.useSmallScreenInterface} > - p.theme.textBlack}; - } - `} - refForCaret={controlsMapIcon} - isOpen={controlPanelOpen} - onOpenChanged={() => controlPanelOpen} - // onDismissed={() => setControlPanelOpen(false)} - btnTitle={t("compass.guidanceBtnTitle")} - btnText={t("compass.guidanceBtnText")} - viewState={props.viewState} - smallScreen={props.viewState.useSmallScreenInterface} - > - { - setControlPanelOpen(false); - props.onClose(); - props.viewState.terria.setLocalProperty( - COMPASS_LOCAL_PROPERTY_KEY, - true - ); - }} - /> - -
+ { + setControlPanelOpen(false); + props.onClose(); + props.viewState.terria.setLocalProperty( + COMPASS_LOCAL_PROPERTY_KEY, + true + ); + }} + /> +
- +
); } diff --git a/lib/ReactViews/Map/MapNavigation/MapNavigation.tsx b/lib/ReactViews/Map/MapNavigation/MapNavigation.tsx index 5f6ef8cf951..23131e3b538 100644 --- a/lib/ReactViews/Map/MapNavigation/MapNavigation.tsx +++ b/lib/ReactViews/Map/MapNavigation/MapNavigation.tsx @@ -167,15 +167,15 @@ class MapNavigationBase extends React.Component { if (this.computeSizes.length !== this.model.visibleItems.length) { this.computeSizes(); } - let itemsToShow = this.model.visibleItems.filter((item) => + const itemsToShow = this.model.visibleItems.filter((item) => filterViewerAndScreenSize(item, this.viewState) ); // items we have to show in the navigation bar - let pinnedItems = this.model.pinnedItems.filter((item) => + const pinnedItems = this.model.pinnedItems.filter((item) => filterViewerAndScreenSize(item, this.viewState) ); // items that are possible to be collapsed - let possibleToCollapse = itemsToShow + const possibleToCollapse = itemsToShow .filter( (item) => !pinnedItems.some((pinnedItem) => pinnedItem.id === item.id) ) diff --git a/lib/ReactViews/Map/Panels/LangPanel/LangPanel.tsx b/lib/ReactViews/Map/Panels/LangPanel/LangPanel.tsx index 2ad49bc15b8..5b69f3a15bf 100644 --- a/lib/ReactViews/Map/Panels/LangPanel/LangPanel.tsx +++ b/lib/ReactViews/Map/Panels/LangPanel/LangPanel.tsx @@ -15,7 +15,7 @@ type Props = { smallScreen: boolean; }; -export default (props: Props) => { +const LangPanel = (props: Props) => { const { t, i18n } = useTranslation(); if (!props.terria.configParameters.languageConfiguration?.languages) { @@ -61,3 +61,4 @@ export default (props: Props) => { ); }; +export default LangPanel; diff --git a/lib/ReactViews/Map/Panels/SharePanel/Print/PrintDatasets.tsx b/lib/ReactViews/Map/Panels/SharePanel/Print/PrintDatasets.tsx index 6ff0f4952c7..4121f66d51e 100644 --- a/lib/ReactViews/Map/Panels/SharePanel/Print/PrintDatasets.tsx +++ b/lib/ReactViews/Map/Panels/SharePanel/Print/PrintDatasets.tsx @@ -13,7 +13,7 @@ const PrintDatasets = (props: Props) => { {props.items.map((item, index) => (
{getName(item)} - +
))} diff --git a/lib/ReactViews/Map/Panels/SharePanel/Print/PrintView.tsx b/lib/ReactViews/Map/Panels/SharePanel/Print/PrintView.tsx index 8f283732101..03323501baf 100644 --- a/lib/ReactViews/Map/Panels/SharePanel/Print/PrintView.tsx +++ b/lib/ReactViews/Map/Panels/SharePanel/Print/PrintView.tsx @@ -186,7 +186,7 @@ const PrintView = (props: Props) => { scale={getScale( viewState.terria.currentViewer.getContainer() )} - isPrintMode={true} + isPrintMode /> ) : ( diff --git a/lib/ReactViews/Map/Panels/SharePanel/Print/PrintWorkbench.tsx b/lib/ReactViews/Map/Panels/SharePanel/Print/PrintWorkbench.tsx index dab2e9cc5c6..df693035052 100644 --- a/lib/ReactViews/Map/Panels/SharePanel/Print/PrintWorkbench.tsx +++ b/lib/ReactViews/Map/Panels/SharePanel/Print/PrintWorkbench.tsx @@ -45,7 +45,7 @@ const renderLegend = (catalogItem: BaseModel) => {
Time: {catalogItem.currentTime}
)} {CatalogMemberMixin.isMixedInto(catalogItem) && ( - + )} ); diff --git a/lib/ReactViews/Map/Panels/SharePanel/SharePanel.tsx b/lib/ReactViews/Map/Panels/SharePanel/SharePanel.tsx index 3328cf2e3a4..a70f16cf43f 100644 --- a/lib/ReactViews/Map/Panels/SharePanel/SharePanel.tsx +++ b/lib/ReactViews/Map/Panels/SharePanel/SharePanel.tsx @@ -78,7 +78,7 @@ class SharePanel extends React.Component { { = ({ shouldShortenDefault(terria) ); - const [_, update] = useState<{}>(); + const [_, update] = useState(); const shareUrlRef = useCallbackRef(null, () => update({})); const includeStoryInShareOnChange = useCallback(() => { diff --git a/lib/ReactViews/Map/Panels/SharePanel/ShareUrl/ShareUrl.tsx b/lib/ReactViews/Map/Panels/SharePanel/ShareUrl/ShareUrl.tsx index 766047564b4..3979f1cdb65 100644 --- a/lib/ReactViews/Map/Panels/SharePanel/ShareUrl/ShareUrl.tsx +++ b/lib/ReactViews/Map/Panels/SharePanel/ShareUrl/ShareUrl.tsx @@ -42,107 +42,105 @@ export interface IShareUrlRef { export const ShareUrl = forwardRef< IShareUrlRef, PropsWithChildren ->( - ( - { - terria, - viewState, - includeStories, - shouldShorten, - children, - theme, - inputTheme, - rounded, - callback - }, - forwardRef - ) => { - const { t } = useTranslation(); +>(function ShareUrl( + { + terria, + viewState, + includeStories, + shouldShorten, + children, + theme, + inputTheme, + rounded, + callback + }, + forwardRef +) { + const { t } = useTranslation(); - const [shareUrl, setShareUrl] = useState(""); - const [shorteningInProgress, setShorteningInProgress] = useState(false); - const [placeholder, setPlaceholder] = useState(); + const [shareUrl, setShareUrl] = useState(""); + const [shorteningInProgress, setShorteningInProgress] = useState(false); + const [placeholder, setPlaceholder] = useState(); - useImperativeHandle( - forwardRef, - () => ({ - url: shareUrl, - shorteningInProgress: shorteningInProgress - }), - [forwardRef, shareUrl, shorteningInProgress] - ); + useImperativeHandle( + forwardRef, + () => ({ + url: shareUrl, + shorteningInProgress: shorteningInProgress + }), + [forwardRef, shareUrl, shorteningInProgress] + ); - useEffect(() => { - if (shouldShorten) { - setPlaceholder(t("share.shortLinkShortening")); - setShorteningInProgress(true); - buildShortShareLink(terria, viewState, { + useEffect(() => { + if (shouldShorten) { + setPlaceholder(t("share.shortLinkShortening")); + setShorteningInProgress(true); + buildShortShareLink(terria, viewState, { + includeStories + }) + .then((shareUrl) => setShareUrl(shareUrl)) + .catch(() => { + setShareUrl( + buildShareLink(terria, viewState, { + includeStories + }) + ); + }) + .finally(() => setShorteningInProgress(false)); + } else { + setShareUrl( + buildShareLink(terria, viewState, { includeStories }) - .then((shareUrl) => setShareUrl(shareUrl)) - .catch(() => { - setShareUrl( - buildShareLink(terria, viewState, { - includeStories - }) - ); - }) - .finally(() => setShorteningInProgress(false)); - } else { - setShareUrl( - buildShareLink(terria, viewState, { - includeStories - }) - ); - } - }, [terria, viewState, shouldShorten, includeStories]); + ); + } + }, [terria, viewState, shouldShorten, includeStories]); - return ( - <> - - {t("clipboard.shareExplanation")} - - - e.currentTarget.select()} - css={` - ${rounded ? `border-radius: 32px 0 0 32px;` : ""} - `} - id="share-url" - /> - } - id="share-url" - rounded={rounded} - onCopy={(text) => - terria.analytics?.logEvent( - Category.share, - ShareAction.storyCopy, - text - ) - } - /> - {children} - - {})} - /> - - ); - } -); + return ( + <> + + {t("clipboard.shareExplanation")} + + + e.currentTarget.select()} + css={` + ${rounded ? `border-radius: 32px 0 0 32px;` : ""} + `} + id="share-url" + /> + } + id="share-url" + rounded={rounded} + onCopy={(text) => + terria.analytics?.logEvent( + Category.share, + ShareAction.storyCopy, + text + ) + } + /> + {children} + + {})} + /> + + ); +}); const Explanation = styled(TextSpan)` opacity: 0.8; diff --git a/lib/ReactViews/Map/Panels/ToolsPanel/CountDatasets.tsx b/lib/ReactViews/Map/Panels/ToolsPanel/CountDatasets.tsx index b446d8530d2..0bd4fccf3d8 100644 --- a/lib/ReactViews/Map/Panels/ToolsPanel/CountDatasets.tsx +++ b/lib/ReactViews/Map/Panels/ToolsPanel/CountDatasets.tsx @@ -82,7 +82,7 @@ const CountDatasets: React.FC = observer((props) => { path.push(member.name!); const loadPromise = member.loadMembers(); - let countPromise = member.isLoading + const countPromise = member.isLoading ? loadPromise .then((result) => result.throwIfError()) .then( @@ -116,7 +116,7 @@ const CountDatasets: React.FC = observer((props) => { const promise = counter(member, childStats, path).then(function () { stats.groups += childStats.groups + 1; stats.members += childStats.members; - stats.messages.push.apply(stats.messages, childStats.messages); + stats.messages.push(...childStats.messages); stats.subTotals.push(childStats); }); return promise; diff --git a/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx b/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx index 634f6ac8165..af3f33d340a 100644 --- a/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx +++ b/lib/ReactViews/Map/Panels/ToolsPanel/ToolsPanel.jsx @@ -38,6 +38,7 @@ const ToolsPanel = observer(() => { )}
+ {/* eslint-disable-next-line react/no-danger */}
diff --git a/lib/ReactViews/Map/TerriaViewerWrapper/Splitter/dragHook.ts b/lib/ReactViews/Map/TerriaViewerWrapper/Splitter/dragHook.ts index 2014e467f34..7552a336fc3 100644 --- a/lib/ReactViews/Map/TerriaViewerWrapper/Splitter/dragHook.ts +++ b/lib/ReactViews/Map/TerriaViewerWrapper/Splitter/dragHook.ts @@ -23,7 +23,9 @@ try { window.addEventListener("test", callback, options); window.removeEventListener("test", callback, options); -} catch (err) {} +} catch (err) { + /* ignore error */ +} const notPassive = passiveSupported ? { passive: false } : false; diff --git a/lib/ReactViews/Mobile/MobileHeader.jsx b/lib/ReactViews/Mobile/MobileHeader.jsx index 9600c11404c..e27e36f4468 100644 --- a/lib/ReactViews/Mobile/MobileHeader.jsx +++ b/lib/ReactViews/Mobile/MobileHeader.jsx @@ -143,7 +143,7 @@ class MobileHeader extends React.Component { )} alwaysShowClear={true} onClear={this.closeLocationSearch.bind(this)} - autoFocus={true} + autoFocus /> )} {searchState.showMobileCatalogSearch && ( @@ -153,7 +153,7 @@ class MobileHeader extends React.Component { onDoSearch={this.searchCatalog.bind(this)} placeholder={t("search.searchCatalogue")} onClear={this.closeCatalogSearch.bind(this)} - autoFocus={true} + autoFocus /> )}
; diff --git a/lib/ReactViews/Mobile/MobileMenuItem.tsx b/lib/ReactViews/Mobile/MobileMenuItem.tsx index 3b62acaf56d..5435466c207 100644 --- a/lib/ReactViews/Mobile/MobileMenuItem.tsx +++ b/lib/ReactViews/Mobile/MobileMenuItem.tsx @@ -10,7 +10,7 @@ type Props = { icon: { id: keyof typeof GLYPHS }; }; -export default (props: Props) => ( +const MobileMenuItem = (props: Props) => (
{props.href ? ( ( )}
); +export default MobileMenuItem; diff --git a/lib/ReactViews/Notification/Notification.tsx b/lib/ReactViews/Notification/Notification.tsx index 4b7e597d1bc..5ef6b16f9eb 100644 --- a/lib/ReactViews/Notification/Notification.tsx +++ b/lib/ReactViews/Notification/Notification.tsx @@ -16,7 +16,7 @@ const Notification = observer(() => { notificationState === undefined || notification === undefined ) { - return <>; + return null; } const close = () => { diff --git a/lib/ReactViews/Notification/shareConvertNotification.tsx b/lib/ReactViews/Notification/shareConvertNotification.tsx index 84907702966..a79b8b87fbe 100644 --- a/lib/ReactViews/Notification/shareConvertNotification.tsx +++ b/lib/ReactViews/Notification/shareConvertNotification.tsx @@ -9,9 +9,10 @@ import Text, { TextSpan } from "../../Styled/Text"; import { RawButton } from "../../Styled/Button"; import Spacing from "../../Styled/Spacing"; -export const shareConvertNotification = - (messages: import("catalog-converter").ShareResult["messages"]) => - (viewState: ViewState) => { +export const shareConvertNotification = ( + messages: import("catalog-converter").ShareResult["messages"] +) => + function shareConvertNotification(viewState: ViewState) { const messagesForPath: { [path: string]: string[] } = {}; messages?.forEach((message: any) => { let pathString = message.path?.join(": "); @@ -39,7 +40,7 @@ export const shareConvertNotification = }; return ( - + <> {parseCustomMarkdownToReact( i18next.t("share.convertNotificationMessage") @@ -77,27 +78,27 @@ export const shareConvertNotification = {rootMessages && ( - + <>
    - {rootMessages.map((message) => ( -
  • {message}
  • + {rootMessages.map((message, i) => ( +
  • {message}
  • ))}
-
+ )} {Object.entries(messagesForPath).map(([path, messages]) => ( - + <>
    - {messages.map((message) => ( -
  • {message}
  • + {messages.map((message, i) => ( +
  • {message}
  • ))}
-
+ ))}
-
+ ); }; diff --git a/lib/ReactViews/Notification/terriaErrorNotification.tsx b/lib/ReactViews/Notification/terriaErrorNotification.tsx index c529ac5c7e7..58cfe94249d 100644 --- a/lib/ReactViews/Notification/terriaErrorNotification.tsx +++ b/lib/ReactViews/Notification/terriaErrorNotification.tsx @@ -28,10 +28,7 @@ const ErrorsBox = (props: { key={idx} > {error instanceof TerriaError ? ( - + ) : ( // Show error.message (as well as error.stack) if error.stack is defined
@@ -72,14 +69,14 @@ const TerriaErrorBox = (props: { + /> ) : null} ); }; -export const terriaErrorNotification = - (error: TerriaError) => (viewState: ViewState) => { +export const terriaErrorNotification = (error: TerriaError) => + function TerriaErrorNotification(viewState: ViewState) { // Get "detailed" errors - these can be expanded if the user wants to see more "detail" let detailedErrors: (Error | TerriaError)[] | undefined; @@ -122,7 +119,7 @@ export const terriaErrorNotification = <> (error.showDetails = show)); }} > - + ) : null} - {!includesFeedbackLink ? ( - - ) : null} + {!includesFeedbackLink ? : null} ); }; diff --git a/lib/ReactViews/Preview/DataPreviewUrl.jsx b/lib/ReactViews/Preview/DataPreviewUrl.jsx index 420d29b98ec..fa536f9f213 100644 --- a/lib/ReactViews/Preview/DataPreviewUrl.jsx +++ b/lib/ReactViews/Preview/DataPreviewUrl.jsx @@ -5,6 +5,8 @@ import createReactClass from "create-react-class"; import PropTypes from "prop-types"; import Styles from "./data-preview.scss"; +import { Trans } from "react-i18next"; + /** * URL section of the preview. */ diff --git a/lib/ReactViews/Preview/Description.jsx b/lib/ReactViews/Preview/Description.jsx index eea167e3511..6e39b9401b6 100644 --- a/lib/ReactViews/Preview/Description.jsx +++ b/lib/ReactViews/Preview/Description.jsx @@ -117,7 +117,7 @@ class Description extends React.Component { `} > {metadataUrl.title && ( - + )} {!metadataUrl.title ? metadataUrl.url : null} @@ -230,7 +230,7 @@ class Description extends React.Component { `} > {dataUrl.title && ( - + )} {!dataUrl.title ? dataUrl.url : null} @@ -248,7 +248,7 @@ class Description extends React.Component {
)} - {!this.props.printView ? ( - - ) : null} + {!this.props.printView ? : null}
); } diff --git a/lib/ReactViews/Preview/WarningBox.tsx b/lib/ReactViews/Preview/WarningBox.tsx index 7134a906b52..0651547e719 100644 --- a/lib/ReactViews/Preview/WarningBox.tsx +++ b/lib/ReactViews/Preview/WarningBox.tsx @@ -48,7 +48,7 @@ const WarningBox: React.FC<{ )} {props.viewState && !includesFeedbackLink ? ( - + ) : null} {/* Add "show details" button if there are nested errors */} diff --git a/lib/ReactViews/RelatedMaps/RelatedMaps.tsx b/lib/ReactViews/RelatedMaps/RelatedMaps.tsx index 2194252874a..08e1c9f2382 100644 --- a/lib/ReactViews/RelatedMaps/RelatedMaps.tsx +++ b/lib/ReactViews/RelatedMaps/RelatedMaps.tsx @@ -51,7 +51,7 @@ class RelatedMaps extends React.Component { {this.props.relatedMaps.map((map, i) => ( - + { target="_blank" style={{ color: this.props.theme.colorPrimary }} href={map.url} + rel="noreferrer" > {map.title} diff --git a/lib/ReactViews/RelatedMaps/related-maps.scss.d.ts b/lib/ReactViews/RelatedMaps/related-maps.scss.d.ts index c57b7d8c80d..c11b8ba8d31 100644 --- a/lib/ReactViews/RelatedMaps/related-maps.scss.d.ts +++ b/lib/ReactViews/RelatedMaps/related-maps.scss.d.ts @@ -7,5 +7,5 @@ interface CssExports { 'link': string; 'section': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Search/LocationSearchResults.jsx b/lib/ReactViews/Search/LocationSearchResults.jsx new file mode 100644 index 00000000000..1b2ad13fc21 --- /dev/null +++ b/lib/ReactViews/Search/LocationSearchResults.jsx @@ -0,0 +1,179 @@ +/** + Initially this was written to support various location search providers in master, + however we only have a single location provider at the moment, and how we merge + them in the new design is yet to be resolved, see: + https://github.com/TerriaJS/nsw-digital-twin/issues/248#issuecomment-599919318 + */ + +import { observer } from "mobx-react"; +import React from "react"; +import styled from "styled-components"; +import PropTypes from "prop-types"; +import { withTranslation } from "react-i18next"; +import SearchHeader from "./SearchHeader"; +import SearchResult from "./SearchResult"; +import classNames from "classnames"; +import Styles from "./location-search-result.scss"; +import isDefined from "../../Core/isDefined"; + +import Icon, { StyledIcon } from "../../Styled/Icon"; +// import Box, { BoxSpan } from "../../Styled/Box"; +import { BoxSpan } from "../../Styled/Box"; +import Text, { TextSpan } from "../../Styled/Text"; + +import { RawButton } from "../../Styled/Button"; + +const RawButtonAndHighlight = styled(RawButton)` + ${(p) => ` + &:hover, &:focus { + background-color: ${p.theme.greyLighter}; + ${StyledIcon} { + fill-opacity: 1; + } + }`} +`; + +const MAX_RESULTS_BEFORE_TRUNCATING = 5; + +@observer +class LocationSearchResults extends React.Component { + static propTypes = { + viewState: PropTypes.object.isRequired, + isWaitingForSearchToStart: PropTypes.bool, + terria: PropTypes.object.isRequired, + search: PropTypes.object.isRequired, + onLocationClick: PropTypes.func.isRequired, + theme: PropTypes.string, + locationSearchText: PropTypes.string, + t: PropTypes.func.isRequired + }; + + static defaultProps = { + theme: "light" + }; + + constructor(props) { + super(props); + this.state = { + isOpen: true, + isExpanded: false + }; + } + + toggleIsOpen() { + this.setState({ + isOpen: !this.state.isOpen + }); + } + + toggleExpand() { + this.setState({ + isExpanded: !this.state.isExpanded + }); + } + + renderResultsFooter() { + const { t } = this.props; + if (this.state.isExpanded) { + return t("search.viewLess", { + name: this.props.search.searchProvider.name + }); + } + return t("search.viewMore", { + name: this.props.search.searchProvider.name + }); + } + + render() { + const search = this.props.search; + const { isOpen, isExpanded } = this.state; + const searchProvider = search.searchProvider; + const locationSearchBoundingBox = + this.props.terria.configParameters.locationSearchBoundingBox; + + const validResults = isDefined(locationSearchBoundingBox) + ? search.results.filter(function (r) { + return ( + r.location.longitude > locationSearchBoundingBox[0] && + r.location.longitude < locationSearchBoundingBox[2] && + r.location.latitude > locationSearchBoundingBox[1] && + r.location.latitude < locationSearchBoundingBox[3] + ); + }) + : search.results; + + const results = + validResults.length > MAX_RESULTS_BEFORE_TRUNCATING + ? isExpanded + ? validResults + : validResults.slice(0, MAX_RESULTS_BEFORE_TRUNCATING) + : validResults; + + return ( +
+ this.toggleIsOpen()} + > + + + {`${search.searchProvider.name} (${validResults?.length})`} + + + + + + +
    + {results.map((result, i) => ( + + ))} +
+ {isOpen && validResults.length > MAX_RESULTS_BEFORE_TRUNCATING && ( + + this.toggleExpand()}> + + {this.renderResultsFooter()} + + + + )} +
+
+ ); + } +} + +module.exports = withTranslation()(LocationSearchResults); diff --git a/lib/ReactViews/Search/search-result.scss.d.ts b/lib/ReactViews/Search/search-result.scss.d.ts new file mode 100644 index 00000000000..88e6820d991 --- /dev/null +++ b/lib/ReactViews/Search/search-result.scss.d.ts @@ -0,0 +1,17 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + 'arrow-icon': string; + 'arrowIcon': string; + 'btn': string; + 'btnLocationName': string; + 'btnWithBorderBottom': string; + 'dark': string; + 'icon': string; + 'light': string; + 'resultName': string; + 'search-result': string; + 'searchResult': string; +} +declare let cssExports: CssExports; +export = cssExports; diff --git a/lib/ReactViews/Search/sidebar-dataset-search-results.scss.d.ts b/lib/ReactViews/Search/sidebar-dataset-search-results.scss.d.ts index 9ca7bfdb2f5..bbd1ce8fc2f 100644 --- a/lib/ReactViews/Search/sidebar-dataset-search-results.scss.d.ts +++ b/lib/ReactViews/Search/sidebar-dataset-search-results.scss.d.ts @@ -11,5 +11,5 @@ interface CssExports { 'provider-result': string; 'providerResult': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/Search/sidebar-search.scss.d.ts b/lib/ReactViews/Search/sidebar-search.scss.d.ts index 2022920f078..1ec3fe36bd4 100644 --- a/lib/ReactViews/Search/sidebar-search.scss.d.ts +++ b/lib/ReactViews/Search/sidebar-search.scss.d.ts @@ -17,5 +17,5 @@ interface CssExports { 'results-content': string; 'resultsContent': string; } -declare var cssExports: CssExports; +declare let cssExports: CssExports; export = cssExports; diff --git a/lib/ReactViews/SelectableDimensions/Color.tsx b/lib/ReactViews/SelectableDimensions/Color.tsx index 41f4b38a834..9330d6d237b 100644 --- a/lib/ReactViews/SelectableDimensions/Color.tsx +++ b/lib/ReactViews/SelectableDimensions/Color.tsx @@ -47,7 +47,7 @@ export const SelectableDimensionColor: React.FC<{ borderRadius: "2px", background: dim.value ?? "#aaa" }} - >
+ /> ) : null} {/* Show "Add" button if value is undefined */} diff --git a/lib/ReactViews/SelectableDimensions/ColorSchemeOptionRenderer.tsx b/lib/ReactViews/SelectableDimensions/ColorSchemeOptionRenderer.tsx index 3f3a250422d..f07d1b14688 100644 --- a/lib/ReactViews/SelectableDimensions/ColorSchemeOptionRenderer.tsx +++ b/lib/ReactViews/SelectableDimensions/ColorSchemeOptionRenderer.tsx @@ -5,7 +5,7 @@ import React from "react"; import StandardCssColors from "../../Core/StandardCssColors"; import { OptionRenderer } from "../../Models/SelectableDimensions/SelectableDimensions"; -const Invalid: React.VFC<{}> = () => { +const Invalid: React.VFC = () => { const { t } = useTranslation(); return {t("selectableDimensions.invalid")}; }; diff --git a/lib/ReactViews/SelectableDimensions/MarkerOptionRenderer.tsx b/lib/ReactViews/SelectableDimensions/MarkerOptionRenderer.tsx index 2246786731b..fb6dc40ea08 100644 --- a/lib/ReactViews/SelectableDimensions/MarkerOptionRenderer.tsx +++ b/lib/ReactViews/SelectableDimensions/MarkerOptionRenderer.tsx @@ -9,7 +9,7 @@ export const MarkerOptionRenderer: OptionRenderer = (option) => ( height="20px" style={{ marginBottom: -5 }} src={getMakiIcon(option.value, "#000", 1, "#fff", 24, 24) ?? option.value} - >{" "} + />{" "} {option.value} ); diff --git a/lib/ReactViews/SelectableDimensions/Select.tsx b/lib/ReactViews/SelectableDimensions/Select.tsx index d27aabe9897..f5f4e7eb490 100644 --- a/lib/ReactViews/SelectableDimensions/Select.tsx +++ b/lib/ReactViews/SelectableDimensions/Select.tsx @@ -99,7 +99,7 @@ export const SelectableDimensionEnumMulti: React.FC<{ }> = observer(({ id, dim }) => { const theme = useTheme(); - let options = dim.options?.map((option) => ({ + const options = dim.options?.map((option) => ({ value: option.id, label: option.name ?? option.id })); @@ -137,7 +137,7 @@ export const SelectableDimensionEnumMulti: React.FC<{ primary: theme.colorPrimary } })} - isMulti={true} + isMulti /> ); }); diff --git a/lib/ReactViews/SidePanel/SidePanel.tsx b/lib/ReactViews/SidePanel/SidePanel.tsx index d020e0bdb01..d590a8b5968 100644 --- a/lib/ReactViews/SidePanel/SidePanel.tsx +++ b/lib/ReactViews/SidePanel/SidePanel.tsx @@ -103,7 +103,7 @@ type SidePanelButtonProps = { const SidePanelButton = React.forwardRef< HTMLButtonElement, SidePanelButtonProps ->((props, ref) => { +>(function SidePanelButton(props, ref) { const { btnText, ...rest } = props; return (