diff --git a/src/js/views/MetadataView.js b/src/js/views/MetadataView.js index 19d42fa7b1..2a25bee5c6 100644 --- a/src/js/views/MetadataView.js +++ b/src/js/views/MetadataView.js @@ -34,9 +34,7 @@ define([ "text!templates/editMetadata.html", "text!templates/dataDisplay.html", "text!templates/map.html", - "text!templates/annotation.html", "text!templates/metaTagsHighwirePress.html", - "uuid", "views/MetricView", ], ( $, @@ -44,7 +42,7 @@ define([ _, Backbone, gmaps, - fancybox, + _fancybox, Clipboard, DataPackage, DataONEObject, @@ -74,9 +72,7 @@ define([ EditMetadataTemplate, DataDisplayTemplate, MapTemplate, - AnnotationTemplate, metaTagsHighwirePressTemplate, - uuid, MetricView, ) => { "use strict"; @@ -98,8 +94,8 @@ define([ saveProvPending: false, model: new SolrResult(), - packageModels: new Array(), - entities: new Array(), + packageModels: [], + entities: [], dataPackage: null, dataPackageSynced: false, el: "#Content", @@ -156,9 +152,7 @@ define([ "click #save-metadata-prov": "saveProv", }, - initialize(options) { - if (options === undefined || !options) var options = {}; - + initialize(options = {}) { this.pid = options.pid || options.id || MetacatUI.appModel.get("pid") || null; @@ -175,10 +169,10 @@ define([ // this.showLoading("Loading..."); // Reset various properties of this view first - this.classMap = new Array(); - this.subviews = new Array(); + this.classMap = []; + this.subviews = []; this.model.set(this.model.defaults); - this.packageModels = new Array(); + this.packageModels = []; // get the pid to render if (!this.pid) this.pid = MetacatUI.appModel.get("pid"); @@ -186,13 +180,13 @@ define([ this.listenTo(MetacatUI.appUserModel, "change:loggedIn", this.render); // Listen to when the metadata has been rendered - this.once("metadataLoaded", function () { + this.once("metadataLoaded", () => { this.createAnnotationViews(); this.insertMarkdownViews(); }); // Listen to when the package table has been rendered - this.once("dataPackageRendered", function () { + this.once("dataPackageRendered", () => { const packageTableContainer = this.$("#data-package-container"); $(packageTableContainer).children(".loading").remove(); @@ -232,7 +226,7 @@ define([ return; } - this.listenToOnce(this.dataPackage, "complete", function () { + this.listenToOnce(this.dataPackage, "complete", () => { this.dataPackageSynced = true; this.trigger("changed:dataPackageSynced"); const dataPackageView = _.findWhere(this.subviews, { @@ -267,7 +261,7 @@ define([ ) { this.checkWritePermissions(); } else { - this.listenToOnce(this.dataPackage.packageModel, "sync", function () { + this.listenToOnce(this.dataPackage.packageModel, "sync", () => { this.checkWritePermissions(); }); } @@ -281,30 +275,28 @@ define([ * Retrieves information from the index about this object, given the id (passed from the URL) * When the object info is retrieved from the index, we set up models depending on the type of object this is */ - getModel(pid) { - // Get the pid and sid - if (typeof pid === "undefined" || !pid) var { pid } = this; - if (typeof this.seriesId !== "undefined" && this.seriesId) - var sid = this.seriesId; + getModel(id) { + const pid = id || this.pid; + const sid = this.seriesId || null; // Get the package ID this.model.set({ id: pid, seriesId: sid }); const { model } = this; - this.listenToOnce(model, "sync", function () { + this.listenToOnce(model, "sync", () => { if ( - this.model.get("formatType") == "METADATA" || + this.model.get("formatType") === "METADATA" || !this.model.get("formatType") ) { this.model = model; this.renderMetadata(); - } else if (this.model.get("formatType") == "DATA") { + } else if (this.model.get("formatType") === "DATA") { // Get the metadata pids that document this data object const isDocBy = this.model.get("isDocumentedBy"); // If there is only one metadata pid that documents this data object, then // get that metadata model for this view. - if (isDocBy && isDocBy.length == 1) { + if (isDocBy && isDocBy.length === 1) { this.navigateWithFragment(_.first(isDocBy), this.pid); return; @@ -314,6 +306,7 @@ define([ if (isDocBy && isDocBy.length > 1) { const view = this; + // eslint-disable-next-line import/no-dynamic-require require(["collections/Filters", "collections/SolrResults"], ( Filters, SolrResults, @@ -336,7 +329,7 @@ define([ }); // When the search results are returned, process those results - view.listenToOnce(searchResults, "sync", (searchResults) => { + view.listenToOnce(searchResults, "sync", () => { // Keep track of the latest version of the metadata doc(s) const latestVersions = []; @@ -376,11 +369,11 @@ define([ return; } this.noMetadata(this.model); - } else if (this.model.get("formatType") == "RESOURCE") { + } else if (this.model.get("formatType") === "RESOURCE") { const packageModel = new Package({ id: this.model.get("id") }); packageModel.on( "complete", - function () { + () => { const metadata = packageModel.getMetadata(); if (!metadata) { @@ -448,25 +441,20 @@ define([ ); // Check for a view service in this MetacatUI.appModel - if ( - MetacatUI.appModel.get("viewServiceUrl") !== undefined && - MetacatUI.appModel.get("viewServiceUrl") - ) - var endpoint = - MetacatUI.appModel.get("viewServiceUrl") + encodeURIComponent(pid); - - if (endpoint && typeof endpoint !== "undefined") { + const viewServiceUrl = MetacatUI.appModel.get("viewServiceUrl"); + if (viewServiceUrl) { + const endpoint = viewServiceUrl + encodeURIComponent(pid); const viewRef = this; const loadSettings = { url: endpoint, - success(response, status, xhr) { + success(response, status, _xhr) { try { // If the user has navigated away from the MetadataView, then don't render anything further - if (MetacatUI.appView.currentView != viewRef) return; + if (MetacatUI.appView.currentView !== viewRef) return; // Our fallback is to show the metadata details from the Solr index if ( - status == "error" || + status === "error" || !response || typeof response !== "string" ) @@ -482,7 +470,7 @@ define([ return; } // Mark this as a metadata doc with no stylesheet, or one that is at least different than usual EML and FGDC - if (response.indexOf('id="Metadata"') == -1) { + if (response.indexOf('id="Metadata"') === -1) { viewRef.$el.addClass("container no-stylesheet"); if (viewRef.model.get("indexed")) { @@ -522,15 +510,15 @@ define([ viewRef.insertCopiables(); } } catch (e) { - console.log( - "Error rendering metadata from the view service", - e, + MetacatUI.analytics?.trackException( + `Error rendering metadata from the view service. Fellback to index, ${e}, Response: ${response}`, + pid, + false, ); - console.log("Response from the view service: ", response); viewRef.renderMetadataFromIndex(); } }, - error(xhr, textStatus, errorThrown) { + error(_xhr, _textStatus, _errorThrown) { viewRef.renderMetadataFromIndex(); }, }; @@ -587,7 +575,6 @@ define([ else { // Find the DOM element with the citation const wells = this.$(".well"); - const viewRef = this; // Find the div.well with the citation. If we never find it, we don't insert the list of contents _.each(wells, (well) => { @@ -641,7 +628,7 @@ define([ `${MetacatUI.root}/data${ MetacatUI.appModel.get("page") > 0 ? `/page/${ - parseInt(MetacatUI.appModel.get("page")) + 1 + parseInt(MetacatUI.appModel.get("page"), 10) + 1 }` : "" }`, @@ -662,14 +649,14 @@ define([ ), ); - if (MetacatUI.uiRouter.lastRoute() == "data") { + if (MetacatUI.uiRouter.lastRoute() === "data") { $(breadcrumbs).prepend( $(document.createElement("a")) .attr( "href", `${MetacatUI.root}/data/page/${ MetacatUI.appModel.get("page") > 0 - ? parseInt(MetacatUI.appModel.get("page")) + 1 + ? parseInt(MetacatUI.appModel.get("page"), 10) + 1 : "" }`, ) @@ -695,21 +682,16 @@ define([ return; } - try { - // Check if a query string was in the URL and if so, try removing it in the identifier - if (this.model.get("id").match(/\?\S+\=\S+/g) && !this.findTries) { - const newID = this.model.get("id").replace(/\?\S+\=\S+/g, ""); - this.onClose(); - this.model.set("id", newID); - this.pid = newID; - this.findTries = 1; - this.render(); - return; - } - } catch (e) { - console.warn("Caught error while determining query string", e); + // Check if a query string was in the URL and if so, try removing it in the identifier + if (this.model.get("id").match(/\?\S+=\S+/g) && !this.findTries) { + const newID = this.model.get("id").replace(/\?\S+=\S+/g, ""); + this.onClose(); + this.model.set("id", newID); + this.pid = newID; + this.findTries = 1; + this.render(); + return; } - // Construct a message that shows this object doesn't exist const msg = `

Nothing was found.

` + @@ -751,9 +733,11 @@ define([ return; } + let msg = ""; + // If the user is logged in, the message will display that this dataset is private. if (MetacatUI.appUserModel.get("loggedIn")) { - var msg = + msg = '' + @@ -763,7 +747,7 @@ define([ } // If the user isn't logged in, display a log in link. else { - var msg = + msg = `` + @@ -781,6 +765,7 @@ define([ }, getPackageDetails(packageIDs) { + const view = this; let completePackages = 0; // This isn't a package, but just a lonely metadata doc... @@ -794,7 +779,7 @@ define([ } else { _.each( packageIDs, - function (thisPackageID, i) { + (thisPackageID, _i) => { // Create a model representing the data package const thisPackage = new Package({ id: thisPackageID }); @@ -806,28 +791,24 @@ define([ ); // When the package info is fully retrieved - this.listenToOnce( - thisPackage, - "complete", - function (thisPackage) { - // When all packages are fully retrieved - completePackages++; - if (completePackages >= packageIDs.length) { - const latestPackages = _.filter( - this.packageModels, - (m) => !_.contains(packageIDs, m.get("obsoletedBy")), - ); + view.listenToOnce(thisPackage, "complete", () => { + // When all packages are fully retrieved + completePackages += 1; + if (completePackages >= packageIDs.length) { + const latestPackages = _.filter( + view.packageModels, + (m) => !_.contains(packageIDs, m.get("obsoletedBy")), + ); - // Set those packages as the most recent package - this.packageModels = latestPackages; + // Set those packages as the most recent package + view.packageModels = latestPackages; - this.insertPackageDetails(latestPackages); - } - }, - ); + view.insertPackageDetails(latestPackages); + } + }); // Save the package in the view - this.packageModels.push(thisPackage); + view.packageModels.push(thisPackage); // Make sure we get archived content, too thisPackage.set("getArchivedMembers", true); @@ -871,73 +852,70 @@ define([ * Inserts an info icon next to the alternate identifier field, if it * exists. The icon will display a tooltip with the help text for the * field. - * @returns {jQuery} The jQuery object for the icon element. + * @returns {jQuery|null} The jQuery object for the icon element. * @since 2.26.0 */ renderAltIdentifierHelpText() { - try { - // Find the HTML element that contains the alternate identifier. - const altIdentifierLabel = this.$( - ".control-label:contains('Alternate Identifier')", - ); + // Find the HTML element that contains the alternate identifier. + const altIdentifierLabel = this.$( + ".control-label:contains('Alternate Identifier')", + ); - // It may not exist for all datasets. - if (!altIdentifierLabel.length) return; + // It may not exist for all datasets. + if (!altIdentifierLabel.length) return null; - const text = this.alternativeIdentifierHelpText; + const text = this.alternativeIdentifierHelpText; - if (!text) return; + if (!text) return null; - // Create the tooltip - const icon = $(document.createElement("i")) - .addClass("tooltip-this icon icon-info-sign") - .css("margin-left", "4px"); + // Create the tooltip + const icon = $(document.createElement("i")) + .addClass("tooltip-this icon icon-info-sign") + .css("margin-left", "4px"); - // Activate the jQuery tooltip plugin - icon.tooltip({ - title: text, - placement: "top", - container: "body", - }); + // Activate the jQuery tooltip plugin + icon.tooltip({ + title: text, + placement: "top", + container: "body", + }); - // Add the icon to the label. - altIdentifierLabel.append(icon); + // Add the icon to the label. + altIdentifierLabel.append(icon); - return icon; - } catch (e) { - console.log("Error adding help text to alternate identifier", e); - } + return icon; }, - /* - * Inserts a table with all the data package member information and sends the call to display annotations + /** + * Inserts a table with all the data package member information and sends + * the call to display annotations + * @param {Array} packageModels - An array of Package models + * @param {object} options - An object with options for rendering the package table + * @returns {MetadataView|null} Returns this view object */ - insertPackageDetails(packages, options) { - if (typeof options === "undefined") { - var options = {}; - } + insertPackageDetails(packageModels, options = {}) { // Don't insert the package details twice const view = this; const tableEls = this.$(view.tableContainer).children().not(".loading"); - if (tableEls.length > 0) return; + if (tableEls.length > 0) return view; // wait for the metadata to load const metadataEls = this.$(view.metadataContainer).children(); if (!metadataEls.length || metadataEls.first().is(".loading")) { - this.once("metadataLoaded", function () { + this.once("metadataLoaded", () => { view.insertPackageDetails(this.packageModels, options); }); - return; + return view; } - if (!packages) var packages = this.packageModels; + const packages = packageModels || this.packageModels; // Get the entity names from this page/metadata this.getEntityNames(packages); _.each( packages, - function (packageModel) { + function showDetails(packageModel) { // If the package model is not complete, don't do anything if (!packageModel.complete) return; @@ -959,34 +937,34 @@ define([ if ( !( !this.model.get("archived") && - packageModel.get("archived") == true + packageModel.get("archived") === true ) ) { - var title = packageModel.get("id") + const title = packageModel.get("id") ? `Package: ${packageModel.get( "id", )}` : ""; - options.title = `Files in this dataset ${title}`; - options.nested = true; - this.insertPackageTable(packageModel, options); + const tableOptions = { ...options, nested: true, title }; + this.insertPackageTable(packageModel, tableOptions); } - } else { + } else if ( // If this metadata is not archived, then don't display archived packages - if ( - !( - !this.model.get("archived") && - packageModel.get("archived") == true - ) - ) { - var title = packageModel.get("id") - ? `Package: ${packageModel.get( - "id", - )}` - : ""; - options.title = `Files in this dataset ${title}`; - this.insertPackageTable(packageModel, options); - } + !( + !this.model.get("archived") && + packageModel.get("archived") === true + ) + ) { + const title = packageModel.get("id") + ? `Package: ${packageModel.get( + "id", + )}` + : ""; + const tableOptions = { + ...options, + title: `Files in this dataset ${title}`, + }; + this.insertPackageTable(packageModel, tableOptions); } // Remove the extra download button returned from the XSLT since the package table will have all the download links @@ -1001,9 +979,12 @@ define([ members: [this.model], }); packageModel.complete = true; - options.title = "Files in this dataset"; - options.disablePackageDownloads = true; - this.insertPackageTable(packageModel, options); + const tableOptions = { + ...options, + title: "Files in this dataset", + disablePackageDownloads: true, + }; + this.insertPackageTable(packageModel, tableOptions); } // Insert the data details sections @@ -1034,13 +1015,14 @@ define([ } // Get the data package only if it is not the same as the previously fetched package - if (mostRecentPackage.get("id") != packages[0].get("id")) + if (mostRecentPackage.get("id") !== packages[0].get("id")) this.getDataPackage(mostRecentPackage.get("id")); } } catch (e) { - console.error( - "Could not get the data package (prov will not be displayed, possibly other info as well).", - e, + MetacatUI.analytics?.trackException( + "Could not get the data package and could not display provenance graphs.", + view.pid, + false, ); } @@ -1052,7 +1034,7 @@ define([ insertPackageTable(packageModel, options) { const view = this; - if (this.dataPackage == null || !this.dataPackageSynced) { + if (!this.dataPackage || !this.dataPackageSynced) { this.listenToOnce(this, "changed:dataPackageSynced", () => { view.insertPackageTable(packageModel, options); }); @@ -1067,16 +1049,16 @@ define([ this.dataPackage.mergeModels(packageModel.get("members")); } + let title = ""; + let disablePackageDownloads = false; + let nested = false; + if (options) { - var title = options.title || ""; - var disablePackageDownloads = - options.disablePackageDownloads || false; - var nested = + title = options.title || ""; + disablePackageDownloads = options.disablePackageDownloads || false; + nested = typeof options.nested === "undefined" ? false : options.nested; - } else - var title = "", - nested = false, - disablePackageDownloads = false; + } //* * Draw the package table **// const tableView = new DataPackageView({ @@ -1099,16 +1081,18 @@ define([ const numTables = $(tablesContainer).find( "table.download-contents", ).length; - if (numTables == 1) { - var tableContainer = $(document.createElement("div")).attr( + + let tableContainer = tablesContainer; + if (numTables === 1) { + tableContainer = $(document.createElement("div")).attr( "id", `additional-tables-for-${this.cid}`, ); tableContainer.hide(); $(tablesContainer).append(tableContainer); - } else if (numTables > 1) - var tableContainer = this.$(`#additional-tables-for-${this.cid}`); - else var tableContainer = tablesContainer; + } else if (numTables > 1) { + tableContainer = this.$(`#additional-tables-for-${this.cid}`); + } // Insert the package table HTML $(tableContainer).empty(); @@ -1143,7 +1127,7 @@ define([ const parentPackageMetadata = packageModel.get("parentPackageMetadata"); const view = this; - _.each(parentPackageMetadata, (m, i) => { + _.each(parentPackageMetadata, (m, _i) => { const title = m.get("title"); const icon = $(document.createElement("i")).addClass( "icon icon-on-left icon-level-up", @@ -1162,44 +1146,43 @@ define([ }, insertSpatialCoverageMap(customCoordinates) { - // Find the geographic region container. Older versions of Metacat (v2.4.3 and less) will not have it classified so look for the header text - if (!this.$(".geographicCoverage").length) { + let geoCoverEls = this.$(".geographicCoverage"); + let parseText = false; + let directions = ["north", "south", "east", "west"]; + let [n, s, e, w] = customCoordinates || []; + + // Find the geographic region container. Older versions of Metacat + // (v2.4.3 and less) will not have it classified so look for the header + // text + if (!geoCoverEls.length) { // For EML let title = this.$('h4:contains("Geographic Region")'); // For FGDC - if (title.length == 0) { + if (title.length === 0) { title = this.$('label:contains("Bounding Coordinates")'); } - var georegionEls = $(title).parent(); - var parseText = true; - var directions = new Array("North", "South", "East", "West"); - } else { - var georegionEls = this.$(".geographicCoverage"); - var directions = new Array("north", "south", "east", "west"); + geoCoverEls = $(title).parent(); + parseText = true; + directions = ["North", "South", "East", "West"]; } - for (let i = 0; i < georegionEls.length; i++) { - var georegion = georegionEls[i]; + for (let i = 0; i < geoCoverEls.length; i += 1) { + const georegion = geoCoverEls[i]; - if (typeof customCoordinates !== "undefined") { - // Extract the coordinates - var n = customCoordinates[0]; - var s = customCoordinates[1]; - var e = customCoordinates[2]; - var w = customCoordinates[3]; - } else { - var coordinates = new Array(); + if (!customCoordinates?.length) { + const coordinates = []; _.each(directions, (direction) => { // Parse text for older versions of Metacat (v2.4.3 and earlier) + let coordinate = ""; if (parseText) { const labelEl = $(georegion).find( `label:contains("${direction}")`, ); if (labelEl.length) { - var coordinate = $(labelEl).next().html(); + coordinate = $(labelEl).next().html(); if ( typeof coordinate !== "undefined" && coordinate.indexOf(" ") > -1 @@ -1210,7 +1193,7 @@ define([ ); } } else { - var coordinate = $(georegion) + coordinate = $(georegion) .find(`.${direction}BoundingCoordinate`) .attr("data-value"); } @@ -1220,10 +1203,7 @@ define([ }); // Extract the coordinates - var n = coordinates[0]; - var s = coordinates[1]; - var e = coordinates[2]; - var w = coordinates[3]; + [n, s, e, w] = coordinates; } // Create Google Map LatLng objects out of our coordinates @@ -1238,7 +1218,7 @@ define([ // If there isn't a center point found, don't draw the map. if (typeof latLngCEN === "undefined") { - return; + return false; } // Get the map path color @@ -1267,15 +1247,15 @@ define([ `&key=${MetacatUI.mapKey}'/>`; // Find the spot in the DOM to insert our map image - if (parseText) - var insertAfter = $(georegion) + let insertAfter = georegion; + if (parseText) { + insertAfter = $(georegion) .find('label:contains("West")') .parent() .parent().length ? $(georegion).find('label:contains("West")').parent().parent() : georegion; - // The last coordinate listed - else var insertAfter = georegion; + } // Get the URL to the interactive Google Maps instance const url = this.getGoogleMapsUrl(latLngCEN, bounds); @@ -1339,8 +1319,9 @@ define([ // useful /** - * - * @param lat + * Converts a latitude to radians. + * @param {number} lat - The latitude to convert. + * @returns {number} The latitude in radians. */ function latRad(lat) { const sin = Math.sin((lat * Math.PI) / 180); @@ -1349,10 +1330,12 @@ define([ } /** - * - * @param mapPx - * @param worldPx - * @param fraction + * Returns the zoom level that will display the given bounding box at + * the given dimensions. + * @param {number} mapPx - The dimensions of the map. + * @param {number} worldPx - The dimensions of the world. + * @param {number} fraction - The fraction of the world to display. + * @returns {number} The zoom level. */ function zoom(mapPx, worldPx, fraction) { return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2); @@ -1373,7 +1356,7 @@ define([ }, insertCitation() { - if (!this.model) return false; + if (!this.model) return; // Create a citation header element from the model attributes const header = new CitationHeaderView({ model: this.model }); this.$(this.citationContainer).html(header.render().el); @@ -1403,7 +1386,7 @@ define([ // Construct a URL to the profile of this repository const profileURL = - dataSource.identifier == MetacatUI.appModel.get("nodeId") + dataSource.identifier === MetacatUI.appModel.get("nodeId") ? `${MetacatUI.root}/profile` : `${MetacatUI.appModel.get("dataoneSearchUrl")}/portals/${ dataSource.shortIdentifier @@ -1448,18 +1431,18 @@ define([ }, animation: false, }) - .on("mouseenter", function () { - const _this = this; + .on("mouseenter", () => { + const el = this; $(this).popover("show"); $(".popover").on("mouseleave", () => { - $(_this).popover("hide"); + $(el).popover("hide"); }); }) - .on("mouseleave", function () { - const _this = this; + .on("mouseleave", () => { + const el = this; setTimeout(() => { if (!$(".popover:hover").length) { - $(_this).popover("hide"); + $(el).popover("hide"); } }, 300); }); @@ -1482,7 +1465,7 @@ define([ modelsToCheck.forEach((model, index) => { // If there is no resource map or no EML, // then the user does not need permission to edit it. - if (!model || model.get("notFound") == true) { + if (!model || model.get("notFound") === true) { authorization[index] = true; // If we already checked, and the user is authorized, // record that information in the authorzation array. @@ -1546,7 +1529,7 @@ define([ const modelsToCheck = [this.model, resourceMap]; const authorized = _.every(modelsToCheck, (model) => // If there is no EML or no resource map, the user doesn't need permission to edit it. - !model || model.get("notFound") == true + !model || model.get("notFound") === true ? true : model.get("isAuthorized_write") === true, ); @@ -1562,7 +1545,7 @@ define([ this.model.get("obsoletedBy").length > 0) || this.model.get("archived") ) { - return false; + return; } // Save the element that will contain the owner control buttons @@ -1600,47 +1583,43 @@ define([ } } - try { - // Determine if this metadata can be published. - // The Publish feature has to be enabled in the app. - // The model cannot already have a DOI - let canBePublished = - MetacatUI.appModel.get("enablePublishDOI") && !view.model.isDOI(); - - // If publishing is enabled, check if only certain users and groups can publish metadata - if (canBePublished) { - // Get the list of authorized publishers from the AppModel - const authorizedPublishers = MetacatUI.appModel.get( - "enablePublishDOIForSubjects", - ); - // If the logged-in user is one of the subjects in the list or is in a group that is - // in the list, then this metadata can be published. Otherwise, it cannot. + // Determine if this metadata can be published. + // The Publish feature has to be enabled in the app. + // The model cannot already have a DOI + let canBePublished = + MetacatUI.appModel.get("enablePublishDOI") && !view.model.isDOI(); + + // If publishing is enabled, check if only certain users and groups can publish metadata + if (canBePublished) { + // Get the list of authorized publishers from the AppModel + const authorizedPublishers = MetacatUI.appModel.get( + "enablePublishDOIForSubjects", + ); + // If the logged-in user is one of the subjects in the list or is in a group that is + // in the list, then this metadata can be published. Otherwise, it cannot. + if ( + Array.isArray(authorizedPublishers) && + authorizedPublishers.length + ) { if ( - Array.isArray(authorizedPublishers) && - authorizedPublishers.length + MetacatUI.appUserModel.hasIdentityOverlap(authorizedPublishers) ) { - if ( - MetacatUI.appUserModel.hasIdentityOverlap(authorizedPublishers) - ) { - canBePublished = true; - } else { - canBePublished = false; - } + canBePublished = true; + } else { + canBePublished = false; } } + } - // If this metadata can be published, then insert the Publish button template - if (canBePublished) { - // Insert a Publish button template - container.append( - view.doiTemplate({ - isAuthorized: true, - identifier: pid, - }), - ); - } - } catch (e) { - console.error("Cannot display the publish button: ", e); + // If this metadata can be published, then insert the Publish button template + if (canBePublished) { + // Insert a Publish button template + container.append( + view.doiTemplate({ + isAuthorized: true, + identifier: pid, + }), + ); } }, @@ -1691,19 +1670,16 @@ define([ insertControls() { // Convert the support mdq formatId list to a version // that JS regex likes (with special characters double - RegExp.escape = function (s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\\\$&"); - }; + RegExp.escape = (s) => s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\\\$&"); const mdqFormatIds = MetacatUI.appModel.get("mdqFormatIds"); // Check of the current formatId is supported by the current // metadata quality suite. If not, the 'Assessment Report' button // will not be displacyed in the metadata controls panel. const thisFormatId = this.model.get("formatId"); - const mdqFormatSupported = false; let formatFound = false; if (mdqFormatIds !== null) { - for (let ifmt = 0; ifmt < mdqFormatIds.length; ++ifmt) { + for (let ifmt = 0; ifmt < mdqFormatIds.length; ifmt += 1) { const currentFormatId = RegExp.escape(mdqFormatIds[ifmt]); const re = new RegExp(currentFormatId); formatFound = re.test(thisFormatId); @@ -1790,10 +1766,10 @@ define([ return; } - const pid_list = []; - pid_list.push(this.pid); + const pidList = []; + pidList.push(this.pid); const metricsModel = new MetricsModel({ - pid_list, + pid_list: pidList, type: "dataset", }); metricsModel.fetch(); @@ -1824,26 +1800,22 @@ define([ buttonToolbar.append(citationsMetricView.render().el); this.subviews.push(citationsMetricView); - try { - // Check if the registerCitation=true query string is set - if (window.location.search) { - if ( - window.location.search.indexOf("registerCitation=true") > -1 - ) { - // Open the modal for the citations - citationsMetricView.showMetricModal(); - - // Show the register citation form - if (citationsMetricView.modalView) { - citationsMetricView.modalView.on( - "renderComplete", - citationsMetricView.modalView.showCitationForm, - ); - } + // Check if the registerCitation=true query string is set + if (window.location.search) { + if ( + window.location.search.indexOf("registerCitation=true") > -1 + ) { + // Open the modal for the citations + citationsMetricView.showMetricModal(); + + // Show the register citation form + if (citationsMetricView.modalView) { + citationsMetricView.modalView.on( + "renderComplete", + citationsMetricView.modalView.showCitationForm, + ); } } - } catch (e) { - console.warn("Not able to show the register citation form ", e); } } @@ -1872,11 +1844,10 @@ define([ // Render the provenance trace using the redrawProvCharts function instead of the drawProvCharts function // just in case the prov charts have already been inserted. Redraw will make sure they are removed // before being re-inserted. - const { model } = this; - if (this.dataPackage.provenanceFlag == "complete") { + if (this.dataPackage.provenanceFlag === "complete") { this.redrawProvCharts(this.dataPackage); } else { - this.listenToOnce(this.dataPackage, "queryComplete", function () { + this.listenToOnce(this.dataPackage, "queryComplete", () => { this.redrawProvCharts(this.dataPackage); }); // parseProv triggers "queryComplete" @@ -1884,21 +1855,26 @@ define([ } }, - /* - * Renders ProvChartViews on the page to display provenance on a package level and on an individual object level. - * This function looks at four sources for the provenance - the package sources, the package derivations, member sources, and member derivations + /** + * Renders ProvChartViews on the page to display provenance on a package + * level and on an individual object level. This function looks at four + * sources for the provenance - the package sources, the package + * derivations, member sources, and member derivations + * @param {DataPackage} collection - The DataPackage collection to render provenance for */ - drawProvCharts(dataPackage) { + drawProvCharts(collection) { + const dataPackage = collection || this.dataPackage; + const view = this; // Set a listener to re-draw the prov charts when needed - this.stopListening(this.dataPackage, "redrawProvCharts"); + this.stopListening(dataPackage, "redrawProvCharts"); this.listenToOnce( - this.dataPackage, + dataPackage, "redrawProvCharts", this.redrawProvCharts, ); // Provenance has to be retrieved from the Package Model (getProvTrace()) before the charts can be drawn - if (dataPackage.provenanceFlag != "complete") return false; + if (dataPackage.provenanceFlag !== "complete") return; // If the user is authorized to edit the provenance for this package // then turn on editing, so that edit icons are displayed. @@ -1924,7 +1900,7 @@ define([ // Get the PackageModel for this DataPackage const packageModel = _.find( this.packageModels, - (packageModel) => packageModel.get("id") == dataPackage.id, + (model) => model.get("id") === dataPackage.id, ); // Merge the SolrResult models into the DataONEObject models @@ -1940,7 +1916,7 @@ define([ // If there are no models to merge in, get them from the index else { // Listen to the DataPackage fetch to complete and re-execute this function - this.listenToOnce(dataPackage, "complete", function () { + this.listenToOnce(dataPackage, "complete", () => { this.drawProvCharts(dataPackage); }); @@ -1960,8 +1936,6 @@ define([ return; } } - - var view = this; // Draw two flow charts to represent the sources and derivations at a package level const packageSources = dataPackage.sourcePackages; const packageDerivations = dataPackage.derivationPackages; @@ -1995,11 +1969,11 @@ define([ editModeOn ) { // Draw the provenance charts for each member of this package at an object level - _.each(dataPackage.toArray(), (member, i) => { + _.each(dataPackage.toArray(), (member, _i) => { // Don't draw prov charts for metadata objects. if ( - member.get("type").toLowerCase() == "metadata" || - member.get("formatType").toLowerCase() == "metadata" + member.get("type").toLowerCase() === "metadata" || + member.get("formatType").toLowerCase() === "metadata" ) { return; } @@ -2011,9 +1985,8 @@ define([ } // Retrieve the sources and derivations for this member - const memberSources = member.get("provSources") || new Array(); - const memberDerivations = - member.get("provDerivations") || new Array(); + const memberSources = member.get("provSources") || []; + const memberDerivations = member.get("provDerivations") || []; // Make the source chart for this member. // If edit is on, then either a 'blank' sources ProvChart will be displayed if there @@ -2061,10 +2034,9 @@ define([ if (this.$(".prov-chart").length > 10000) { const allNodes = this.$(".prov-chart .node"); let ids = []; - var view = this; let i = 1; - $(allNodes).each(function () { + $(allNodes).each(() => { ids.push($(this).attr("data-id")); }); ids = _.uniq(ids); @@ -2080,7 +2052,7 @@ define([ const className = `uniqueNode${i}`; // Add the unique class and up the iterator - if (matchingNodes.prop("tagName") != "polygon") + if (matchingNodes.prop("tagName") !== "polygon") $(matchingNodes).addClass(className); else $(matchingNodes).attr( @@ -2096,7 +2068,7 @@ define([ id, className, }); - i++; + i += 1; } }); } @@ -2117,12 +2089,12 @@ define([ } else { this.hideEditorControls(); - // Reset the edited flag for each package member - _.each(this.dataPackage.toArray(), (item) => { - item.selectedInEditor == false; + this.dataPackage.toArray().forEach((item) => { + // eslint-disable-next-line no-param-reassign + item.selectedInEditor = false; }); } - _.each(this.subviews, (thisView, i) => { + _.each(this.subviews, (thisView, _i) => { // Check if this is a ProvChartView if ( thisView.className && @@ -2138,7 +2110,7 @@ define([ this.subviews = _.filter( this.subviews, (item) => - item.className && item.className.indexOf("prov-chart") == -1, + item.className && item.className.indexOf("prov-chart") === -1, ); view.drawProvCharts(this.dataPackage); @@ -2149,7 +2121,7 @@ define([ */ saveSuccess(savedObject) { // We only want to perform these actions after the package saves - if (savedObject.type != "DataPackage") return; + if (savedObject.type !== "DataPackage") return; // Change the URL to the new id MetacatUI.uiRouter.navigate( @@ -2185,14 +2157,16 @@ define([ // then re-render it with the update resmap id. const view = this; const metadataId = this.packageModels[0].getMetadata().get("id"); - _.each(this.subviews, (thisView, i) => { + _.each(this.subviews, (thisView, _i) => { // Check if this is a ProvChartView if (thisView.type && thisView.type.indexOf("DataPackage") !== -1) { - if (thisView.currentlyViewing == metadataId) { + if (thisView.currentlyViewing === metadataId) { const packageId = view.dataPackage.packageModel.get("id"); const title = packageId ? `Package: ${packageId}` : ""; + + // eslint-disable-next-line no-param-reassign thisView.title = `Files in this dataset ${title}`; thisView.render(); } @@ -2241,7 +2215,6 @@ define([ // Only call this function once per save operation. if (this.saveProvPending) return; - const view = this; if (this.dataPackage.provEditsPending()) { this.saveProvPending = true; // If the Data Package failed saving, display an error message @@ -2294,16 +2267,16 @@ define([ const metadataModel = packageModel.getMetadata(); if (!metadataModel) return; - if (metadataModel.get("id") != viewRef.pid) { + if (metadataModel.get("id") !== viewRef.pid) { const requestSettings = { url: MetacatUI.appModel.get("viewServiceUrl") + encodeURIComponent(metadataModel.get("id")), - success(parsedMetadata, response, xhr) { - _.each(packageModel.get("members"), (solrResult, i) => { + success(parsedMetadata, _response, _xhr) { + _.each(packageModel.get("members"), (solrResult, _i) => { let entityName = ""; - if (solrResult.get("formatType") == "METADATA") + if (solrResult.get("formatType") === "METADATA") entityName = solrResult.get("title"); const container = viewRef.findEntityDetailsContainer( @@ -2338,14 +2311,14 @@ define([ return; } - _.each(packageModel.get("members"), (solrResult, i) => { + _.each(packageModel.get("members"), (solrResult, _i) => { let entityName = ""; if (solrResult.get("fileName")) entityName = solrResult.get("fileName"); - else if (solrResult.get("formatType") == "METADATA") + else if (solrResult.get("formatType") === "METADATA") entityName = solrResult.get("title"); - else if (solrResult.get("formatType") == "RESOURCE") return; + else if (solrResult.get("formatType") === "RESOURCE") return; else { const container = viewRef.findEntityDetailsContainer(solrResult); @@ -2383,13 +2356,17 @@ define([ }, /** - * Finds the element in the rendered metadata that describes the given data entity. - * @param {(DataONEObject|SolrResult|string)} model - Either a model that represents the data object or the identifier of the data object - * @param {Element} [el] - The DOM element to exclusivly search inside. - * @returns {Element} - The DOM element that describbbes the given data entity. + * Finds the element in the rendered metadata that describes the given + * data entity. + * @param {(DataONEObject|SolrResult|string)} model - Either a model that + * represents the data object or the identifier of the data object + * @param {Element} [containerEl] - The DOM element to exclusivly search + * inside. + * @returns {Element|null} - The DOM element that describes the given data + * entity or null if it cannot be found. */ - findEntityDetailsContainer(model, el) { - if (!el) var { el } = this; + findEntityDetailsContainer(model, containerEl) { + const el = containerEl || this.el; // Get the id and file name for this data object let id = ""; @@ -2398,8 +2375,7 @@ define([ // If a model is given, get the id and file name from the object if ( model && - (DataONEObject.prototype.isPrototypeOf(model) || - SolrResult.prototype.isPrototypeOf(model)) + (model instanceof DataONEObject || model instanceof SolrResult) ) { id = model.get("id"); fileName = model.get("fileName"); @@ -2410,7 +2386,7 @@ define([ } // Otherwise, there isn't enough info to find the element, so exit else { - return; + return null; } // If we already found it earlier, return it now @@ -2428,7 +2404,7 @@ define([ } // Are we looking for the main object that this MetadataView is displaying? - if (id == this.pid) { + if (id === this.pid) { if (this.$("#Metadata").length > 0) return this.$("#Metadata"); return this.el; } @@ -2487,12 +2463,15 @@ define([ // ----Find by file name rather than id----- if (!fileName) { // Get the name of the object first - for (var i = 0; i < this.packageModels.length; i++) { - var model = _.findWhere(this.packageModels[i].get("members"), { - id, - }); - if (model) { - fileName = model.get("fileName"); + for (let i = 0; i < this.packageModels.length; i += 1) { + const packageModel = _.findWhere( + this.packageModels[i].get("members"), + { + id, + }, + ); + if (packageModel) { + fileName = packageModel.get("fileName"); break; } } @@ -2506,12 +2485,12 @@ define([ ]; // Search through each possible location in the DOM where the file name might be - for (var i = 0; i < possibleLocations.length; i++) { + for (let i = 0; i < possibleLocations.length; i += 1) { // Get the elements in this view that match the possible location const matches = this.$(possibleLocations[i]); // If exactly one match is found - if (matches.length == 1) { + if (matches.length === 1) { // Get the entity details parent element container = $(matches).parents(".entitydetails").first(); // Set the object ID on the element for easier locating later @@ -2533,10 +2512,10 @@ define([ const members = this.packageModels[0].get("members"); const dataMembers = _.filter( members, - (m) => m.get("formatType") == "DATA", + (m) => m.get("formatType") === "DATA", ); - if (dataMembers.length == 1) { - if (this.$(".entitydetails").length == 1) { + if (dataMembers.length === 1) { + if (this.$(".entitydetails").length === 1) { this.$(".entitydetails").attr("data-id", id); // Store the PID on this element for moreInfo icons this.storeEntityPIDs(this.$(".entitydetails"), id); @@ -2549,7 +2528,8 @@ define([ }, /* - * Inserts new image elements into the DOM via the image template. Use for displaying images that are part of this metadata's resource map. + * Inserts new image elements into the DOM via the image template. Use for + * displaying images that are part of this metadata's resource map. */ insertDataDetails() { // If there is a metadataIndex subview, render from there. @@ -2565,30 +2545,28 @@ define([ const viewRef = this; - _.each(this.packageModels, (packageModel) => { - const dataDisplay = ""; + this.packageModels.forEach((packageModel) => { const images = []; - const other = []; const packageMembers = packageModel.get("members"); // Don't do this for large packages if (packageMembers.length > 150) return; //= === Loop over each visual object and create a dataDisplay template for it to attach to the DOM ==== - _.each(packageMembers, (solrResult, i) => { + _.each(packageMembers, (solrResult, _i) => { // Don't display any info about nested packages - if (solrResult.type == "Package") return; + if (solrResult.type === "Package") return; const objID = solrResult.get("id"); - if (objID == viewRef.pid) return; + if (objID === viewRef.pid) return; // Is this a visual object (image)? const type = - solrResult.type == "SolrResult" + solrResult.type === "SolrResult" ? solrResult.getType() : "Data set"; - if (type == "image") images.push(solrResult); + if (type === "image") images.push(solrResult); // Find the part of the HTML Metadata view that describes this data object const anchor = $(document.createElement("a")).attr( @@ -2605,7 +2583,7 @@ define([ // Insert the data display HTML and the anchor tag to mark this spot on the page if (container) { // Only show data displays for images hosted on the same origin - if (type == "image") { + if (type === "image") { // Create the data display HTML const dataDisplay = $.parseHTML( viewRef @@ -2628,7 +2606,7 @@ define([ const xhr = new XMLHttpRequest(); xhr.withCredentials = true; - xhr.onload = function () { + xhr.onload = () => { if (xhr.response) $(dataDisplay) .find("img") @@ -2657,8 +2635,9 @@ define([ } }); - //= === Initialize the fancybox images ===== - // We will be checking every half-second if all the HTML has been loaded into the DOM - once they are all loaded, we can initialize the lightbox functionality. + //= === Initialize the fancybox images ===== We will be checking every + // half-second if all the HTML has been loaded into the DOM - once + // they are all loaded, we can initialize the lightbox functionality. const numImages = images.length; // The shared lightbox options for both images const lightboxOptions = { @@ -2689,17 +2668,17 @@ define([ imgLightboxOptions.type = "image"; imgLightboxOptions.perload = 1; - const initializeImgLightboxes = function () { - numImgChecks++; + const initializeImgLightboxes = () => { + numImgChecks += 1; // Initialize what images have loaded so far after 5 seconds - if (numImgChecks == 10) { + if (numImgChecks === 10) { $(lightboxImgSelector).fancybox(imgLightboxOptions); } // When 15 seconds have passed, stop checking so we don't blow up the browser else if (numImgChecks > 30) { $(lightboxImgSelector).fancybox(imgLightboxOptions); - window.clearInterval(imgIntervalID); + window.clearInterval(viewRef.imgIntervalID); return; } @@ -2710,10 +2689,10 @@ define([ $(lightboxImgSelector).fancybox(imgLightboxOptions); // We're done - clear the interval - window.clearInterval(imgIntervalID); + window.clearInterval(viewRef.imgIntervalID); }; - var imgIntervalID = window.setInterval( + viewRef.imgIntervalID = window.setInterval( initializeImgLightboxes, 500, ); @@ -2722,8 +2701,6 @@ define([ }, replaceEcoGridLinks() { - const viewRef = this; - // Find the element in the DOM housing the ecogrid link $("a:contains('ecogrid://')").each((i, thisLink) => { // Get the link text @@ -2748,13 +2725,14 @@ define([ // target may not actually prevent click events, so double check const disabled = $(event.target).closest("a").attr("disabled"); if (disabled) { - return false; + return; } const publishServiceUrl = MetacatUI.appModel.get("publishServiceUrl"); const pid = $(event.target).closest("a").attr("pid"); + // eslint-disable-next-line no-restricted-globals, no-alert const ret = confirm( `Are you sure you want to publish ${pid} with a DOI?`, - ); + ); // TODO: We should use a custom modal here instead of the browser's confirm dialog if (ret) { // show the loading icon @@ -2769,7 +2747,7 @@ define([ xhrFields: { withCredentials: true, }, - success(data, textStatus, xhr) { + success(data, _textStatus, _xhr) { // the response should have new identifier in it identifier = $(data).find("d1\\:identifier, identifier").text(); @@ -2798,7 +2776,7 @@ define([ }, 3000); } }, - error(xhr, textStatus, errorThrown) { + error(xhr, _textStatus, _errorThrown) { // show the error message, but stay on the same page const msg = `Publish failed: ${$(xhr.responseText) .find("description") @@ -2856,7 +2834,7 @@ define([ // When the latest version is found, this.listenTo(this.model, "change:newestVersion", () => { // Make sure it has a newer version, and if so, - if (view.model.get("newestVersion") != view.model.get("id")) { + if (view.model.get("newestVersion") !== view.model.get("id")) { // Put a link to the newest version in the content view.$(".newer-version").replaceWith( view.versionTemplate({ @@ -2916,7 +2894,8 @@ define([ * When the "Metadata" button in the table is clicked while we are on the Metadata view, * we want to scroll to the anchor tag of this data object within the page instead of navigating * to the metadata page again, which refreshes the page and re-renders (more loading time) - * @param e + * @param {Event} e - The click event + * @returns {boolean} - Returns false if the click event should not be followed */ previewData(e) { // Don't go anywhere yet... @@ -2926,10 +2905,12 @@ define([ let link = $(e.target); if (!$(link).hasClass("preview")) link = $(link).parents("a.preview"); - if (link) { - var id = $(link).attr("data-id"); - if (typeof id === "undefined" || !id) return false; // This will make the app defualt to the child view previewData function - } else return false; + if (!link?.length) return false; + + const id = $(link).attr("data-id"); + + // This will make the app defualt to the child view previewData function + if (!id) return false; // If we are on the Metadata view, update the URL and scroll to the // anchor @@ -2972,18 +2953,18 @@ define([ * is not a metadata PID but is, instead, a data PID. getModel() does * the work of finding an appropriate metadata PID for the data PID and * this method handles re-routing to the correct URL. - * @param {string} metadata_pid - The new metadata PID - * @param {string} data_pid - Optional. A data PID that's part of the - * package metadata_pid exists within. + * @param {string} metadataPid - The new metadata PID + * @param {string} dataPid - Optional. A data PID that's part of the + * package metadataPid exists within. */ - navigateWithFragment(metadata_pid, data_pid) { - let next_route = `view/${encodeURIComponent(metadata_pid)}`; + navigateWithFragment(metadataPid, dataPid) { + let nextRoute = `view/${encodeURIComponent(metadataPid)}`; - if (typeof data_pid === "string" && data_pid.length > 0) { - next_route += `#${encodeURIComponent(data_pid)}`; + if (typeof dataPid === "string" && dataPid.length > 0) { + nextRoute += `#${encodeURIComponent(dataPid)}`; } - MetacatUI.uiRouter.navigate(next_route, { trigger: true }); + MetacatUI.uiRouter.navigate(nextRoute, { trigger: true }); }, closePopovers(e) { @@ -3012,13 +2993,13 @@ define([ id = $(e.target).parents("[data-id]").attr("data-id"); // If there is no id, return - if (typeof id === "undefined") return false; + if (typeof id === "undefined") return; // Highlight its node $(`.prov-chart .node[data-id='${id}']`).toggleClass("active"); // Highlight its metadata section - if (MetacatUI.appModel.get("pid") == id) + if (MetacatUI.appModel.get("pid") === id) this.$("#Metadata").toggleClass("active"); else { const entityDetails = this.findEntityDetailsContainer(id); @@ -3027,15 +3008,13 @@ define([ }, onClose() { - const viewRef = this; - this.stopListening(); _.each(this.subviews, (subview) => { if (subview.onClose) subview.onClose(); }); - this.packageModels = new Array(); + this.packageModels = []; this.model.set(this.model.defaults); this.pid = null; this.dataPackage = null; @@ -3056,6 +3035,7 @@ define([ * Generate a string appropriate to go into the author/creator portion of * a dataset citation from the value stored in the underlying model's * origin field. + * @returns {string} - A string of author names, formatted for citation */ getAuthorText() { const authors = this.model.get("origin"); @@ -3063,9 +3043,9 @@ define([ let authorText = ""; _.each(authors, (author) => { - count++; + count += 1; - if (count == 6) { + if (count === 6) { authorText += ", et al. "; return; } @@ -3078,7 +3058,7 @@ define([ authorText += ","; } - if (count == authors.length) { + if (count === authors.length) { authorText += " and"; } @@ -3097,6 +3077,7 @@ define([ * Generate a string appropriate to be used in the publisher portion of a * dataset citation. This method falls back to the node ID when the proper * node name cannot be fetched from the app's NodeModel instance. + * @returns {string} - A string of the publisher name, formatted for citation */ getPublisherText() { const datasource = this.model.get("datasource"); @@ -3111,6 +3092,7 @@ define([ /** * Generate a string appropriate to be used as the publication date in a * dataset citation. + * @returns {string} - A string of the publication date, formatted for citation */ getDatePublishedText() { // Dataset/datePublished @@ -3127,17 +3109,11 @@ define([ * * Note: `insertJSONLD` should be called to do the actual inserting into the * DOM. + * @returns {object} - JSON-LD object for the model bound to the view */ generateJSONLD() { const { model } = this; - // Determine the path (either #view or view, depending on router - // configuration) for use in the 'url' property - const { href } = document.location; - const route = href - .replace(`${document.location.origin}/`, "") - .split("/")[0]; - // First: Create a minimal Schema.org Dataset with just the fields we // know will come back from Solr (System Metadata fields). // Add the rest in conditional on whether they are present. @@ -3244,10 +3220,10 @@ define([ if (model.get("abstract")) { elJSON.description = model.get("abstract"); } else { - const datasets_url = `https://dataone.org/datasets/${encodeURIComponent( + const datasetsUrl = `https://dataone.org/datasets/${encodeURIComponent( model.get("id"), )}`; - elJSON.description = `No description is available. Visit ${datasets_url} for complete metadata about this dataset.`; + elJSON.description = `No description is available. Visit ${datasetsUrl} for complete metadata about this dataset.`; } // Dataset/keywords @@ -3288,6 +3264,7 @@ define([ * Tries to use the PropertyValue pattern when the identifier is a DOI * and falls back to a Text value otherwise * @param {string} identifier - The raw identifier + * @returns {object} - A Schema.org/PropertyValue object or a string */ generateSchemaOrgIdentifier(identifier) { if (!this.model.isDOI()) { @@ -3313,10 +3290,11 @@ define([ * * Either generates a GeoCoordinates (when the north and east coords are * the same) or a GeoShape otherwise. - * @param north - * @param east - * @param south - * @param west + * @param {number} north - North bounding coordinate + * @param {number} east - East bounding coordinate + * @param {number} south - South bounding coordinate + * @param {number} west - West bounding coordinate + * @returns {object} - A Schema.org/Place/geo object */ generateSchemaOrgGeo(north, east, south, west) { if (north === south) { @@ -3351,6 +3329,7 @@ define([ * @param {number} east - East bounding coordinate * @param {number} south - South bounding coordinate * @param {number} west - West bounding coordinate + * @returns {string} - A stringified GeoJSON object */ generateGeoJSONString(north, east, south, west) { if (north === south) { @@ -3363,8 +3342,8 @@ define([ * Generate a GeoJSON Point object * @param {number} north - North bounding coordinate * @param {number} east - East bounding coordinate - * - * Example: + * @returns {string} - A stringified GeoJSON Point object + * @example * { * "type": "Point", * "coordinates": [ @@ -3386,10 +3365,8 @@ define([ * @param {number} east - East bounding coordinate * @param {number} south - South bounding coordinate * @param {number} west - West bounding coordinate - * - * - * Example: - * + * @returns {string} - A stringified GeoJSON Polygon object + * @example * { * "type": "Polygon", * "coordinates": [[ @@ -3405,14 +3382,12 @@ define([ '{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[['; // Handle the case when the polygon wraps across the 180W/180E boundary - if (east < west) { - east = 360 - east; - } + const fixedEast = east < west ? 360 - east : east; const inner = `[${west},${south}],` + - `[${east},${south}],` + - `[${east},${north}],` + + `[${fixedEast},${south}],` + + `[${fixedEast},${north}],` + `[${west},${north}],` + `[${west},${south}]`; @@ -3423,11 +3398,9 @@ define([ /** * Create a canonical IRI for a DOI given a random DataONE identifier. - * @param {string} identifier: The identifier to (possibly) create the IRI - * for. - * @param identifier - * @returns {string|null} Returns null when matching the identifier to a DOI - * regex fails or a string when the match is successful + * @param {string} identifier The identifier to (possibly) create the IRI for. + * @returns {string|null} Returns null when matching the identifier to a + * DOI regex fails or a string when the match is successful * * Useful for describing resources identified by DOIs in linked open data * contexts or possibly also useful for comparing two DOIs for equality. @@ -3490,18 +3463,13 @@ define([ }, createAnnotationViews() { - try { - const viewRef = this; - - _.each($(".annotation"), (annoEl) => { - const newView = new AnnotationView({ - el: annoEl, - }); - viewRef.subviews.push(newView); + const viewRef = this; + _.each($(".annotation"), (annoEl) => { + const newView = new AnnotationView({ + el: annoEl, }); - } catch (e) { - console.error(e); - } + viewRef.subviews.push(newView); + }); }, insertMarkdownViews() { @@ -3525,7 +3493,7 @@ define([ storeEntityPIDs(entityEl, entityId) { let entityPID = entityId; // Get the entity ID if it is null or undefined - if (entityPID == null) entityPID = $(entityEl).data("id"); + if (entityPID === null) entityPID = $(entityEl).data("id"); // Perform clean up with the entity ID if (entityPID && typeof entityPID === "string") {