From 554eadc88d78e45d0267feb802eca378ace91751 Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Tue, 6 Aug 2024 19:06:27 -0700 Subject: [PATCH 1/6] Move bytesToSize to Utilities Ref #2484 --- src/js/common/Utilities.js | 33 ++++++++++++++++++ src/js/models/DataONEObject.js | 57 ++++++++++++------------------- src/js/models/PackageModel.js | 31 ----------------- src/js/models/SolrResult.js | 32 ----------------- src/js/views/DataPackageView.js | 4 ++- src/js/views/MetadataIndexView.js | 7 +++- src/js/views/PackageTableView.js | 6 ++-- src/js/views/StatsView.js | 35 +++---------------- 8 files changed, 71 insertions(+), 134 deletions(-) diff --git a/src/js/common/Utilities.js b/src/js/common/Utilities.js index 753674fb1..3e5798c0d 100644 --- a/src/js/common/Utilities.js +++ b/src/js/common/Utilities.js @@ -1,6 +1,11 @@ define([], () => { "use strict"; + const KIBIBYTE = 1024; + const MEBIBYTE = KIBIBYTE * 1024; + const GIBIBYTE = MEBIBYTE * 1024; + const TEBIBYTE = GIBIBYTE * 1024; + /** * @namespace Utilities * @description A generic utility object that contains functions used throughout MetacatUI to perform useful functions, @@ -156,6 +161,34 @@ define([], () => { } return 0; // No decimal places }, + + /** + * Convert number of bytes into human readable format + * + * @param integer bytes Number of bytes to convert + * @param integer precision Number of digits after the decimal separator + * @return string + */ + bytesToSize: function (bytes, precision = 0) { + if (typeof bytes === "undefined") return; + + if (bytes >= 0 && bytes < KIBIBYTE) { + return bytes + " B"; + } else if (bytes >= KIBIBYTE && bytes < MEBIBYTE) { + return (bytes / KIBIBYTE).toFixed(precision) + " KiB"; + } else if (bytes >= MEBIBYTE && bytes < GIBIBYTE) { + precision = 2; + return (bytes / MEBIBYTE).toFixed(precision) + " MiB"; + } else if (bytes >= GIBIBYTE && bytes < TEBIBYTE) { + precision = 2; + return (bytes / GIBIBYTE).toFixed(precision) + " GiB"; + } else if (bytes >= TEBIBYTE) { + precision = 2; + return (bytes / TEBIBYTE).toFixed(precision) + " TiB"; + } else { + return bytes + " B"; + } + }, }; return Utilities; diff --git a/src/js/models/DataONEObject.js b/src/js/models/DataONEObject.js index f9155fdb0..58b37eaf0 100644 --- a/src/js/models/DataONEObject.js +++ b/src/js/models/DataONEObject.js @@ -6,8 +6,19 @@ define([ "he", "collections/AccessPolicy", "collections/ObjectFormats", + "common/Utilities", "md5", -], function ($, _, Backbone, uuid, he, AccessPolicy, ObjectFormats, md5) { +], function ( + $, + _, + Backbone, + uuid, + he, + AccessPolicy, + ObjectFormats, + Utilities, + md5, +) { /** * @class DataONEObject * @classdesc A DataONEObject represents a DataONE object, such as a data file, @@ -124,8 +135,15 @@ define([ this.set("accessPolicy", this.createAccessPolicy()); - this.on("change:size", this.bytesToSize); - if (attrs.size) this.bytesToSize(); + var model = this; + this.on("change:size", function () { + var size = Utilities.bytesToSize(model.get("size")); + model.set("sizeStr", size); + }); + if (attrs.size) { + var size = Utilities.bytesToSize(model.get("size")); + model.set("sizeStr", size); + } // Cache an array of original attribute names to help in handleChange() if (this.type == "DataONEObject") @@ -1977,39 +1995,6 @@ define([ return xmlString; }, - /** - * Converts the number of bytes into a human readable format and - * updates the `sizeStr` attribute - * @returns: None - * - */ - bytesToSize: function () { - var kibibyte = 1024; - var mebibyte = kibibyte * 1024; - var gibibyte = mebibyte * 1024; - var tebibyte = gibibyte * 1024; - var precision = 0; - - var bytes = this.get("size"); - - if (bytes >= 0 && bytes < kibibyte) { - this.set("sizeStr", bytes + " B"); - } else if (bytes >= kibibyte && bytes < mebibyte) { - this.set("sizeStr", (bytes / kibibyte).toFixed(precision) + " KiB"); - } else if (bytes >= mebibyte && bytes < gibibyte) { - precision = 2; - this.set("sizeStr", (bytes / mebibyte).toFixed(precision) + " MiB"); - } else if (bytes >= gibibyte && bytes < tebibyte) { - precision = 2; - this.set("sizeStr", (bytes / gibibyte).toFixed(precision) + " GiB"); - } else if (bytes >= tebibyte) { - precision = 2; - this.set("sizeStr", (bytes / tebibyte).toFixed(precision) + " TiB"); - } else { - this.set("sizeStr", bytes + " B"); - } - }, - /** * This method will download this object while * sending the user's auth token in the request. diff --git a/src/js/models/PackageModel.js b/src/js/models/PackageModel.js index c17cd3bed..8c29e56e3 100644 --- a/src/js/models/PackageModel.js +++ b/src/js/models/PackageModel.js @@ -1816,37 +1816,6 @@ define([ this.set("totalSize", totalSize); return totalSize; }, - - /****************************/ - /** - * Convert number of bytes into human readable format - * - * @param integer bytes Number of bytes to convert - * @param integer precision Number of digits after the decimal separator - * @return string - */ - bytesToSize: function (bytes, precision) { - var kibibyte = 1024; - var mebibyte = kibibyte * 1024; - var gibibyte = mebibyte * 1024; - var tebibyte = gibibyte * 1024; - - if (typeof bytes === "undefined") var bytes = this.get("size"); - - if (bytes >= 0 && bytes < kibibyte) { - return bytes + " B"; - } else if (bytes >= kibibyte && bytes < mebibyte) { - return (bytes / kibibyte).toFixed(precision) + " KiB"; - } else if (bytes >= mebibyte && bytes < gibibyte) { - return (bytes / mebibyte).toFixed(precision) + " MiB"; - } else if (bytes >= gibibyte && bytes < tebibyte) { - return (bytes / gibibyte).toFixed(precision) + " GiB"; - } else if (bytes >= tebibyte) { - return (bytes / tebibyte).toFixed(precision) + " TiB"; - } else { - return bytes + " B"; - } - }, }, ); return PackageModel; diff --git a/src/js/models/SolrResult.js b/src/js/models/SolrResult.js index 3c55466d9..4bf2cb00a 100644 --- a/src/js/models/SolrResult.js +++ b/src/js/models/SolrResult.js @@ -859,38 +859,6 @@ define(["jquery", "underscore", "backbone"], function ($, _, Backbone) { //If nothing works so far, return an empty array return []; }, - - /****************************/ - - /** - * Convert number of bytes into human readable format - * - * @param integer bytes Number of bytes to convert - * @param integer precision Number of digits after the decimal separator - * @return string - */ - bytesToSize: function (bytes, precision) { - var kibibyte = 1024; - var mebibyte = kibibyte * 1024; - var gibibyte = mebibyte * 1024; - var tebibyte = gibibyte * 1024; - - if (typeof bytes === "undefined") var bytes = this.get("size"); - - if (bytes >= 0 && bytes < kibibyte) { - return bytes + " B"; - } else if (bytes >= kibibyte && bytes < mebibyte) { - return (bytes / kibibyte).toFixed(precision) + " KiB"; - } else if (bytes >= mebibyte && bytes < gibibyte) { - return (bytes / mebibyte).toFixed(precision) + " MiB"; - } else if (bytes >= gibibyte && bytes < tebibyte) { - return (bytes / gibibyte).toFixed(precision) + " GiB"; - } else if (bytes >= tebibyte) { - return (bytes / tebibyte).toFixed(precision) + " TiB"; - } else { - return bytes + " B"; - } - }, }, ); return SolrResult; diff --git a/src/js/views/DataPackageView.js b/src/js/views/DataPackageView.js index 5a7cd1c99..a7ba4c97d 100644 --- a/src/js/views/DataPackageView.js +++ b/src/js/views/DataPackageView.js @@ -4,6 +4,7 @@ "backbone", "localforage", "collections/DataPackage", + "common/Utilities", "models/DataONEObject", "models/PackageModel", "models/metadata/ScienceMetadata", @@ -20,6 +21,7 @@ Backbone, LocalForage, DataPackage, + Utilities, DataONEObject, PackageModel, ScienceMetadata, @@ -1036,7 +1038,7 @@ var view = this; _.each(members, function (m) { // Update the size to bytes format - m.set({ size: m.bytesToSize(m.get("size")) }); + m.set({ size: Utilities.bytesToSize(m.get("size")) }); // Add each item of this nested package to the package table view view.addOne(m, dataPackage); diff --git a/src/js/views/MetadataIndexView.js b/src/js/views/MetadataIndexView.js index 518ff79ce..b9192035b 100644 --- a/src/js/views/MetadataIndexView.js +++ b/src/js/views/MetadataIndexView.js @@ -3,6 +3,7 @@ define([ "underscore", "backbone", "gmaps", + "common/Utilities", "models/SolrResult", "views/DownloadButtonView", "text!templates/loading.html", @@ -14,6 +15,7 @@ define([ _, Backbone, gmaps, + Utilities, SolrResult, DownloadButtonView, LoadingTemplate, @@ -434,7 +436,10 @@ define([ _.each(pkg.get("members"), function (solrResult, i) { if (solrResult.get("formatType") != "DATA") return; - solrResult.set("formattedSize", solrResult.bytesToSize()); + solrResult.set( + "formattedSize", + Utilities.bytesToSize(solrResult.get("size")), + ); //Add a section for the data details, just like the other attribute sections var keys = [ diff --git a/src/js/views/PackageTableView.js b/src/js/views/PackageTableView.js index 96ce136b5..bbb934c23 100644 --- a/src/js/views/PackageTableView.js +++ b/src/js/views/PackageTableView.js @@ -2,10 +2,11 @@ define([ "jquery", "underscore", "backbone", + "common/Utilities", "models/PackageModel", "views/DownloadButtonView", "text!templates/downloadContents.html", -], function ($, _, Backbone, Package, DownloadButtonView, Template) { +], function ($, _, Backbone, Utilities, Package, DownloadButtonView, Template) { "use strict"; var PackageTable = Backbone.View.extend({ @@ -401,7 +402,8 @@ define([ //File size cell var sizeCell = $(document.createElement("td")).addClass("size"); - var size = memberModel.bytesToSize(); + var size = Utilities.bytesToSize(memberModel.get("size")); + memberModel.set("sizeStr", size); $(sizeCell).text(size); $(tr).append(sizeCell); diff --git a/src/js/views/StatsView.js b/src/js/views/StatsView.js index e655ca2a4..fb394eaab 100644 --- a/src/js/views/StatsView.js +++ b/src/js/views/StatsView.js @@ -8,6 +8,7 @@ define([ "DonutChart", "CircleBadge", "collections/Citations", + "common/Utilities", "models/MetricsModel", "models/Stats", "MetricsChart", @@ -27,6 +28,7 @@ define([ DonutChart, CircleBadge, Citations, + Utilities, MetricsModel, StatsModel, MetricsChart, @@ -923,12 +925,13 @@ define([ displayTotalSize: function () { var className = "quick-stats-count"; var count = ""; + var view = this; if (!this.model.get("totalSize")) { count = "0 bytes"; className += " no-activity"; } else { - count = this.bytesToSize(this.model.get("totalSize")); + count = Utilities.bytesToSize(view.model.get("totalSize")); } var countEl = $(document.createElement("p")) @@ -1103,36 +1106,6 @@ define([ } }, - /** - * Convert number of bytes into human readable format - * - * @param integer bytes Number of bytes to convert - * @param integer precision Number of digits after the decimal separator - * @return string - */ - bytesToSize: function (bytes, precision) { - var kibibyte = 1024; - var mebibyte = kibibyte * 1024; - var gibibyte = mebibyte * 1024; - var tebibyte = gibibyte * 1024; - - if (typeof bytes === "undefined") var bytes = this.get("size"); - - if (bytes >= 0 && bytes < kibibyte) { - return bytes + " B"; - } else if (bytes >= kibibyte && bytes < mebibyte) { - return (bytes / kibibyte).toFixed(precision) + " KiB"; - } else if (bytes >= mebibyte && bytes < gibibyte) { - return (bytes / mebibyte).toFixed(precision) + " MiB"; - } else if (bytes >= gibibyte && bytes < tebibyte) { - return (bytes / gibibyte).toFixed(precision) + " GiB"; - } else if (bytes >= tebibyte) { - return (bytes / tebibyte).toFixed(precision) + " TiB"; - } else { - return bytes + " B"; - } - }, - renderUsageMetricsError: function () { var message = "

This might take some time. Check back in 24 hours to see these results.

"; From c690d7e63ce03ede42689f26df5bd4f5bc8851bb Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Thu, 29 Aug 2024 14:02:05 -0700 Subject: [PATCH 2/6] Fix eslint issues Ref #2483 --- src/js/views/DataCatalogView.js | 1279 +++++++++--------- src/js/views/DataPackageView.js | 397 +++--- src/js/views/DownloadButtonView.js | 92 +- src/js/views/MetadataIndexView.js | 321 +++-- src/js/views/MetadataView.js | 1985 +++++++++++++--------------- 5 files changed, 1975 insertions(+), 2099 deletions(-) diff --git a/src/js/views/DataCatalogView.js b/src/js/views/DataCatalogView.js index 9f5c7c078..00b15bab1 100644 --- a/src/js/views/DataCatalogView.js +++ b/src/js/views/DataCatalogView.js @@ -16,7 +16,7 @@ define([ "text!templates/loading.html", "gmaps", "nGeohash", -], function ( +], ( $, _, Backbone, @@ -34,18 +34,18 @@ define([ LoadingTemplate, gmaps, nGeohash, -) { +) => { "use strict"; /** * @class DataCatalogView * @classcategory Views - * @extends Backbone.View - * @constructor + * @augments Backbone.View + * @class * @deprecated * @description This view is deprecated and will eventually be removed in a future version (likely 3.0.0) */ - var DataCatalogView = Backbone.View.extend( + const DataCatalogView = Backbone.View.extend( /** @lends DataCatalogView.prototype */ { el: "#Content", @@ -55,7 +55,7 @@ define([ /** * If true, the view height will be adjusted to fit the height of the window * If false, the view height will be fixed via CSS - * @type {Boolean} + * @type {boolean} */ fixedHeight: false, @@ -156,13 +156,13 @@ define([ "mouseover .prevent-popover-runoff": "preventPopoverRunoff", }, - initialize: function (options) { - var view = this; + initialize(options) { + const view = this; // Get all the options and apply them to this view if (options) { - var optionKeys = Object.keys(options); - _.each(optionKeys, function (key, i) { + const optionKeys = Object.keys(options); + _.each(optionKeys, (key, i) => { view[key] = options[key]; }); } @@ -171,13 +171,13 @@ define([ // Render the main view and/or re-render subviews. Don't call .html() here // so we don't lose state, rather use .setElement(). Delegate rendering // and event handling to sub views - render: function () { + render() { // Use the global models if there are no other models specified at time of render if ( MetacatUI.appModel.get("searchHistory").length > 0 && (!this.searchModel || Object.keys(this.searchModel).length == 0) ) { - var lastSearchModels = _.last( + const lastSearchModels = _.last( MetacatUI.appModel.get("searchHistory"), ); @@ -243,12 +243,12 @@ define([ } // Populate the search template with some model attributes - var loadingHTML = this.loadingTemplate({ + const loadingHTML = this.loadingTemplate({ msg: "Retrieving member nodes...", }); - var templateVars = { - gmaps: gmaps, + const templateVars = { + gmaps, mode: MetacatUI.appModel.get("searchMode"), useMapBounds: this.searchModel.get("useGeohash"), username: MetacatUI.appUserModel.get("username"), @@ -263,16 +263,16 @@ define([ dataSourceTitle: MetacatUI.theme == "dataone" ? "Member Node" : "Data source", }; - var cel = this.template( + const cel = this.template( _.extend(this.searchModel.toJSON(), templateVars), ); this.$el.html(cel); - //Hide the filters that are disabled in the AppModel settings + // Hide the filters that are disabled in the AppModel settings _.each( this.$(".filter-contain[data-category]"), - function (filterEl) { + (filterEl) => { if ( !_.contains( MetacatUI.appModel.get("defaultSearchFilters"), @@ -296,18 +296,18 @@ define([ this.renderMap(); // Initialize the tooltips - var tooltips = $(".tooltip-this"); + const tooltips = $(".tooltip-this"); // Find the tooltips that are on filter labels - add a slight delay to those - var groupedTooltips = _.groupBy(tooltips, function (t) { - return ( + const groupedTooltips = _.groupBy( + tooltips, + (t) => ($(t).prop("tagName") == "LABEL" || $(t).parent().prop("tagName") == "LABEL") && - $(t).parents(".filter-container").length > 0 - ); - }); - var forFilterLabel = true, - forOtherElements = false; + $(t).parents(".filter-container").length > 0, + ); + const forFilterLabel = true; + const forOtherElements = false; $(groupedTooltips[forFilterLabel]).tooltip({ delay: { @@ -328,7 +328,7 @@ define([ this.toggleFilterCollapse(); // Iterate through each search model text attribute and show UI filter for each - var categories = [ + const categories = [ "all", "attribute", "creator", @@ -339,26 +339,26 @@ define([ "annotation", "isPrivate", ]; - var thisTerm = null; + let thisTerm = null; - for (var i = 0; i < categories.length; i++) { + for (let i = 0; i < categories.length; i++) { thisTerm = this.searchModel.get(categories[i]); if (thisTerm === undefined || thisTerm === null) break; - for (var x = 0; x < thisTerm.length; x++) { + for (let x = 0; x < thisTerm.length; x++) { this.showFilter(categories[i], thisTerm[x]); } } // List the Member Node filters - var view = this; + const view = this; _.each( _.contains( MetacatUI.appModel.get("defaultSearchFilters"), "dataSource", ), - function (source, i) { + (source, i) => { view.showFilter("dataSource", source); }, ); @@ -414,21 +414,21 @@ define([ /** * addAnnotationFilter - Add the annotation filter to the view */ - addAnnotationFilter: function () { + addAnnotationFilter() { if (MetacatUI.appModel.get("bioportalAPIKey")) { - var view = this; - var popoverTriggerSelector = + const view = this; + const popoverTriggerSelector = "[data-category='annotation'] .expand-collapse-control"; if (!this.$el.find(popoverTriggerSelector)) { return; } - var annotationFilter = new AnnotationFilter({ - popoverTriggerSelector: popoverTriggerSelector, + const annotationFilter = new AnnotationFilter({ + popoverTriggerSelector, }); this.$el.find(popoverTriggerSelector).append(annotationFilter.el); annotationFilter.render(); annotationFilter.off("annotationSelected"); - annotationFilter.on("annotationSelected", function (event, item) { + annotationFilter.on("annotationSelected", (event, item) => { $("#annotation_input").val(item.value); view.updateTextFilters(event, item); }); @@ -436,10 +436,10 @@ define([ }, // Linked Data Object for appending the jsonld into the browser DOM - getLinkedData: function () { + getLinkedData() { // Find the MN info from the CN Node list - var members = MetacatUI.nodeModel.get("members"); - for (var i = 0; i < members.length; i++) { + const members = MetacatUI.nodeModel.get("members"); + for (let i = 0; i < members.length; i++) { if ( members[i].identifier == MetacatUI.nodeModel.get("currentMemberNode") @@ -449,7 +449,7 @@ define([ } // JSON Linked Data Object - let elJSON = { + const elJSON = { "@context": { "@vocab": "http://schema.org/", }, @@ -458,7 +458,7 @@ define([ if (nodeModelObject) { // "keywords": "", // "provider": "", - let conditionalData = { + const conditionalData = { description: nodeModelObject.description, identifier: nodeModelObject.identifier, image: nodeModelObject.logo, @@ -471,22 +471,21 @@ define([ // Check if the jsonld already exists from the previous data view // If not create a new script tag and append otherwise replace the text for the script if (!document.getElementById("jsonld")) { - var el = document.createElement("script"); + const el = document.createElement("script"); el.type = "application/ld+json"; el.id = "jsonld"; el.text = JSON.stringify(elJSON); document.querySelector("head").appendChild(el); } else { - var script = document.getElementById("jsonld"); + const script = document.getElementById("jsonld"); script.text = JSON.stringify(elJSON); } - return; }, /* * Sets the height on elements in the main content area to fill up the entire area minus header and footer */ - setAutoHeight: function () { + setAutoHeight() { // If we are in list mode, don't determine the height of any elements because we are not "full screen" if ( MetacatUI.appModel.get("searchMode") == "list" || @@ -498,14 +497,14 @@ define([ // Get the heights of the header, navbar, and footer var otherHeight = 0; - $(".auto-height-member").each(function (i, el) { + $(".auto-height-member").each((i, el) => { if ($(el).css("display") != "none") { otherHeight += $(el).outerHeight(true); } }); // Get the remaining height left based on the window size - var remainingHeight = $(window).outerHeight(true) - otherHeight; + let remainingHeight = $(window).outerHeight(true) - otherHeight; if (remainingHeight < 0) remainingHeight = $(window).outerHeight(true) || 300; else if (remainingHeight <= 120) @@ -522,12 +521,12 @@ define([ var otherHeight = 0; $("#map-container.auto-height") .children() - .each(function (i, el) { + .each((i, el) => { if ($(el).attr("id") != "map-canvas") { otherHeight += $(el).outerHeight(true); } }); - var newMapHeight = remainingHeight - otherHeight; + const newMapHeight = remainingHeight - otherHeight; if (newMapHeight > 100) { $("#map-canvas").height(remainingHeight - otherHeight); } @@ -544,9 +543,9 @@ define([ * PERFORMING SEARCH * ================================================================================================== */ - triggerSearch: function () { + triggerSearch() { // Set the sort order - var sortOrder = $("#sortOrder").val(); + const sortOrder = $("#sortOrder").val(); if (sortOrder) { this.searchModel.set("sortOrder", sortOrder); } @@ -556,7 +555,7 @@ define([ if (!this.isSubView) { // make sure the browser knows where we are - var route = Backbone.history.fragment; + const route = Backbone.history.fragment; if (route.indexOf("data") < 0) { MetacatUI.uiRouter.navigate("data", { trigger: false, @@ -571,7 +570,7 @@ define([ return false; }, - triggerOnEnter: function (e) { + triggerOnEnter(e) { if (e.keyCode != 13) return; // Update the filters @@ -582,15 +581,15 @@ define([ * getResults gets all the current search filters from the searchModel, creates a Solr query, and runs that query. * @param {number} page - The page of search results to get results for */ - getResults: function (page) { + getResults(page) { // Set the sort order based on user choice - var sortOrder = this.searchModel.get("sortOrder"); + const sortOrder = this.searchModel.get("sortOrder"); if (sortOrder) { this.searchResults.setSort(sortOrder); } // Specify which fields to retrieve - var fields = ""; + let fields = ""; fields += "id,"; fields += "seriesId,"; fields += "title,"; @@ -621,13 +620,12 @@ define([ this.searchResults.setfields(fields); // Get the query - var query = this.searchModel.getQuery(); + const query = this.searchModel.getQuery(); // Specify which facets to retrieve if (gmaps && this.map) { // If we have Google Maps enabled - var geohashLevel = - "geohash_" + this.mapModel.determineGeohashLevel(this.map.zoom); + const geohashLevel = `geohash_${this.mapModel.determineGeohashLevel(this.map.zoom)}`; this.searchResults.facet.push(geohashLevel); } @@ -659,18 +657,18 @@ define([ * After the search results have been returned, * check if any of them are derived data or have derivations */ - checkForProv: function () { - var maps = [], - hasSources = [], - hasDerivations = [], - mainSearchResults = this.searchResults; + checkForProv() { + let maps = []; + let hasSources = []; + let hasDerivations = []; + const mainSearchResults = this.searchResults; // Get a list of all the resource map IDs from the SolrResults collection maps = this.searchResults.pluck("resourceMap"); maps = _.compact(_.flatten(maps)); // Create a new Search model with a search that finds all members of these packages/resource maps - var provSearchModel = new SearchModel({ + const provSearchModel = new SearchModel({ formatType: [ { value: "DATA", @@ -683,30 +681,25 @@ define([ }); // Create a new Solr Results model to store the results of this supplemental query - var provSearchResults = new SearchResults(null, { + const provSearchResults = new SearchResults(null, { query: provSearchModel.getQuery(), searchLogs: false, usePOST: true, rows: 150, - fields: provSearchModel.getProvFlList() + ",id,resourceMap", + fields: `${provSearchModel.getProvFlList()},id,resourceMap`, }); // Trigger a search on that Solr Results model - this.listenTo(provSearchResults, "reset", function (results) { + this.listenTo(provSearchResults, "reset", (results) => { if (results.models.length == 0) return; // See if any of the results have a value for a prov field - results.forEach(function (result) { + results.forEach((result) => { if (!result.getSources().length || !result.getDerivations()) return; - _.each(result.get("resourceMap"), function (rMapID) { + _.each(result.get("resourceMap"), (rMapID) => { if (_.contains(maps, rMapID)) { - var match = mainSearchResults.filter( - function (mainSearchResult) { - return _.contains( - mainSearchResult.get("resourceMap"), - rMapID, - ); - }, + const match = mainSearchResults.filter((mainSearchResult) => + _.contains(mainSearchResult.get("resourceMap"), rMapID), ); if (match && match.length && result.getSources().length > 0) hasSources.push(match[0].get("id")); @@ -722,16 +715,16 @@ define([ // If they do, find their corresponding result row here and add // the prov icon (or just change the class to active) - _.each(hasSources, function (metadataID) { - var metadataDoc = mainSearchResults.findWhere({ + _.each(hasSources, (metadataID) => { + const metadataDoc = mainSearchResults.findWhere({ id: metadataID, }); if (metadataDoc) { metadataDoc.set("prov_hasSources", true); } }); - _.each(hasDerivations, function (metadataID) { - var metadataDoc = mainSearchResults.findWhere({ + _.each(hasDerivations, (metadataID) => { + const metadataDoc = mainSearchResults.findWhere({ id: metadataID, }); if (metadataDoc) { @@ -742,7 +735,7 @@ define([ provSearchResults.toPage(0); }, - cacheSearch: function () { + cacheSearch() { MetacatUI.appModel.get("searchHistory").push({ search: this.searchModel.clone(), map: this.mapModel ? this.mapModel.clone() : null, @@ -755,15 +748,15 @@ define([ * FILTERS * ================================================================================================== */ - updateCheckboxFilter: function (e, category, value) { + updateCheckboxFilter(e, category, value) { if (!this.filters) return; - var checkbox = e.target; - var checked = $(checkbox).prop("checked"); + const checkbox = e.target; + const checked = $(checkbox).prop("checked"); - if (typeof category == "undefined") + if (typeof category === "undefined") var category = $(checkbox).attr("data-category"); - if (typeof value == "undefined") var value = $(checkbox).attr("value"); + if (typeof value === "undefined") var value = $(checkbox).attr("value"); // If the user just unchecked the box, then remove this filter if (!checked) { @@ -772,28 +765,28 @@ define([ } // If the user just checked the box, then add this filter else { - var currentValue = this.searchModel.get(category); + const currentValue = this.searchModel.get(category); // Get the description - var desc = + let desc = $(checkbox).attr("data-description") || $(checkbox).attr("title"); - if (typeof desc == "undefined" || !desc) desc = ""; + if (typeof desc === "undefined" || !desc) desc = ""; // Get the label - var labl = $(checkbox).attr("data-label"); - if (typeof labl == "undefined" || !labl) labl = ""; + let labl = $(checkbox).attr("data-label"); + if (typeof labl === "undefined" || !labl) labl = ""; // Make the filter object - var filter = { + const filter = { description: desc, label: labl, - value: value, + value, }; // If this filter category is an array, add this value to the array if (Array.isArray(currentValue)) { currentValue.push(filter); this.searchModel.set(category, currentValue); - this.searchModel.trigger("change:" + category); + this.searchModel.trigger(`change:${category}`); } else { // If it isn't an array, then just update the model with a simple value this.searchModel.set(category, filter); @@ -813,13 +806,13 @@ define([ this.triggerSearch(); }, - updateBooleanFilters: function (e) { + updateBooleanFilters(e) { if (!this.filters) return; // Get the category - var checkbox = e.target; - var category = $(checkbox).attr("data-category"); - var currentValue = this.searchModel.get(category); + const checkbox = e.target; + const category = $(checkbox).attr("data-category"); + const currentValue = this.searchModel.get(category); // If this filter is not enabled, exit this function if ( @@ -828,15 +821,16 @@ define([ return false; } - //The year filter is handled in a different way + // The year filter is handled in a different way if (category == "pubYear" || category == "dataYear") return; // If the checkbox has a value, then update as a string value not boolean - var value = $(checkbox).attr("value"); + let value = $(checkbox).attr("value"); if (value) { this.updateCheckboxFilter(e, category, value); return; - } else value = $(checkbox).prop("checked"); + } + value = $(checkbox).prop("checked"); this.searchModel.set(category, value); @@ -859,42 +853,42 @@ define([ this.triggerSearch(); // Track this event - MetacatUI.analytics?.trackEvent("search", "filter, " + category, value); + MetacatUI.analytics?.trackEvent("search", `filter, ${category}`, value); }, // Update the UI year slider and input values // Also update the model - updateYearRange: function (e) { + updateYearRange(e) { if (!this.filters) return; - var viewRef = this, - userAction = !(typeof e === "undefined"), - model = this.searchModel, - pubYearChecked = $("#publish_year").prop("checked"), - dataYearChecked = $("#data_year").prop("checked"); + const viewRef = this; + const userAction = !(typeof e === "undefined"); + const model = this.searchModel; + const pubYearChecked = $("#publish_year").prop("checked"); + const dataYearChecked = $("#data_year").prop("checked"); // If the year range slider has not been created yet if (!userAction && !$("#year-range").hasClass("ui-slider")) { var defaultMin = - typeof this.searchModel.defaults == "function" - ? this.searchModel.defaults().yearMin - : 1800, - defaultMax = - typeof this.searchModel.defaults == "function" - ? this.searchModel.defaults().yearMax - : new Date().getUTCFullYear(); - - //jQueryUI slider + typeof this.searchModel.defaults === "function" + ? this.searchModel.defaults().yearMin + : 1800; + var defaultMax = + typeof this.searchModel.defaults === "function" + ? this.searchModel.defaults().yearMax + : new Date().getUTCFullYear(); + + // jQueryUI slider $("#year-range").slider({ range: true, disabled: false, - min: defaultMin, //sets the minimum on the UI slider on initialization - max: defaultMax, //sets the maximum on the UI slider on initialization + min: defaultMin, // sets the minimum on the UI slider on initialization + max: defaultMax, // sets the maximum on the UI slider on initialization values: [ this.searchModel.get("yearMin"), this.searchModel.get("yearMax"), - ], //where the left and right slider handles are - stop: function (event, ui) { + ], // where the left and right slider handles are + stop(event, ui) { // When the slider is changed, update the input values $("#min_year").val(ui.values[0]); $("#max_year").val(ui.values[1]); @@ -921,7 +915,7 @@ define([ $("#publish_year").attr("data-category"), true, false, - ui.values[0] + " to " + ui.values[1], + `${ui.values[0]} to ${ui.values[1]}`, { replace: true, }, @@ -932,7 +926,7 @@ define([ $("#data_year").attr("data-category"), true, false, - ui.values[0] + " to " + ui.values[1], + `${ui.values[0]} to ${ui.values[1]}`, { replace: true, }, @@ -959,7 +953,7 @@ define([ }); return; } - var year = new Date( + const year = new Date( this.statsModel.get("firstBeginDate"), ).getUTCFullYear(); if (typeof year !== "undefined") { @@ -981,7 +975,7 @@ define([ "pubYear", true, false, - $("#min_year").val() + " to " + $("#max_year").val(), + `${$("#min_year").val()} to ${$("#max_year").val()}`, { replace: true, }, @@ -992,7 +986,7 @@ define([ "dataYear", true, false, - $("#min_year").val() + " to " + $("#max_year").val(), + `${$("#min_year").val()} to ${$("#max_year").val()}`, { replace: true, }, @@ -1011,7 +1005,7 @@ define([ }); return; } - var year = new Date( + const year = new Date( this.statsModel.get("lastEndDate"), ).getUTCFullYear(); if (typeof year !== "undefined") { @@ -1033,7 +1027,7 @@ define([ "pubYear", true, false, - $("#min_year").val() + " to " + $("#max_year").val(), + `${$("#min_year").val()} to ${$("#max_year").val()}`, { replace: true, }, @@ -1044,7 +1038,7 @@ define([ "dataYear", true, false, - $("#min_year").val() + " to " + $("#max_year").val(), + `${$("#min_year").val()} to ${$("#max_year").val()}`, { replace: true, }, @@ -1096,8 +1090,8 @@ define([ } // If either of the year inputs have changed or if just one of the year types were unchecked else { - var minVal = $("#min_year").val(); - var maxVal = $("#max_year").val(); + const minVal = $("#min_year").val(); + const maxVal = $("#max_year").val(); // Update the search model to match what is in the text inputs this.searchModel.set("yearMin", minVal); @@ -1118,7 +1112,7 @@ define([ $("#data_year").attr("data-category"), true, true, - minVal + " to " + maxVal, + `${minVal} to ${maxVal}`, { replace: true, }, @@ -1128,7 +1122,7 @@ define([ MetacatUI.analytics?.trackEvent( "search", "filter, Data Year", - minVal + " to " + maxVal, + `${minVal} to ${maxVal}`, ); } else { // Add the filter elements @@ -1137,7 +1131,7 @@ define([ $("#publish_year").attr("data-category"), true, true, - minVal + " to " + maxVal, + `${minVal} to ${maxVal}`, { replace: true, }, @@ -1147,7 +1141,7 @@ define([ MetacatUI.analytics?.trackEvent( "search", "filter, Publication Year", - minVal + " to " + maxVal, + `${minVal} to ${maxVal}`, ); } else { this.hideFilter($("#publish_year").attr("data-category"), true); @@ -1158,7 +1152,7 @@ define([ $("#data_year").attr("data-category"), true, true, - minVal + " to " + maxVal, + `${minVal} to ${maxVal}`, { replace: true, }, @@ -1168,7 +1162,7 @@ define([ MetacatUI.analytics?.trackEvent( "search", "filter, Data Year", - minVal + " to " + maxVal, + `${minVal} to ${maxVal}`, ); } else { this.hideFilter($("#data_year").attr("data-category"), true); @@ -1184,15 +1178,15 @@ define([ } }, - updateTextFilters: function (e, item) { + updateTextFilters(e, item) { if (!this.filters) return; // Get the search/filter category - var category = $(e.target).attr("data-category"); + let category = $(e.target).attr("data-category"); // Try the parent elements if not found if (!category) { - var parents = $(e.target) + const parents = $(e.target) .parents() .each(function () { category = $(this).attr("data-category"); @@ -1207,12 +1201,12 @@ define([ } // Get the input element - var input = this.$el.find("#" + category + "_input"); + const input = this.$el.find(`#${category}_input`); // Get the value of the associated input - var term = !item || !item.value ? input.val() : item.value; - var label = !item || !item.filterLabel ? null : item.filterLabel; - var filterDesc = !item || !item.desc ? null : item.desc; + const term = !item || !item.value ? input.val() : item.value; + const label = !item || !item.filterLabel ? null : item.filterLabel; + const filterDesc = !item || !item.desc ? null : item.desc; // Check that something was actually entered if (term == "" || term == " ") { @@ -1228,20 +1222,18 @@ define([ } // Get the current searchModel array for this category - var filtersArray = _.clone(this.searchModel.get(category)); + const filtersArray = _.clone(this.searchModel.get(category)); - if (typeof filtersArray == "undefined") { + if (typeof filtersArray === "undefined") { console.error( - "The filter category '" + - category + - "' does not exist in the Search model. Not sending this search term.", + `The filter category '${category}' does not exist in the Search model. Not sending this search term.`, ); return false; } // Check if this entry is a duplicate - var duplicate = (function () { - for (var i = 0; i < filtersArray.length; i++) { + const duplicate = (function () { + for (let i = 0; i < filtersArray.length; i++) { if (filtersArray[i].value === term) { return true; } @@ -1250,14 +1242,14 @@ define([ if (duplicate) { // Display a quick message - if ($("#duplicate-" + category + "-alert").length <= 0) { - $("#current-" + category + "-filters").prepend( + if ($(`#duplicate-${category}-alert`).length <= 0) { + $(`#current-${category}-filters`).prepend( "
" + "You are already using that filter" + "
", ); - $("#duplicate-" + category + "-alert") + $(`#duplicate-${category}-alert`) .delay(2000) .fadeOut(500, function () { this.remove(); @@ -1268,10 +1260,10 @@ define([ } // Add the new entry to the array of current filters - var filter = { + const filter = { value: term, filterLabel: label, - label: label, + label, description: filterDesc, }; filtersArray.push(filter); @@ -1292,19 +1284,19 @@ define([ this.triggerSearch(); // Track this event - MetacatUI.analytics?.trackEvent("search", "filter, " + category, term); + MetacatUI.analytics?.trackEvent("search", `filter, ${category}`, term); }, // Removes a specific filter term from the searchModel - removeFilter: function (e) { + removeFilter(e) { // Get the parent element that stores the filter term - var filterNode = $(e.target).parent(); + const filterNode = $(e.target).parent(); // Find this filter's category and value - var category = - filterNode.attr("data-category") || - filterNode.parent().attr("data-category"), - value = $(filterNode).attr("data-term"); + const category = + filterNode.attr("data-category") || + filterNode.parent().attr("data-category"); + const value = $(filterNode).attr("data-term"); // Remove this filter from the searchModel this.searchModel.removeFromModel(category, value); @@ -1313,25 +1305,26 @@ define([ this.hideFilter(category, value); // If there is an associated checkbox with this filter, uncheck it - var assocCheckbox, - checkboxes = this.$( - "input[type='checkbox'][data-category='" + category + "']", - ); + let assocCheckbox; + const checkboxes = this.$( + `input[type='checkbox'][data-category='${category}']`, + ); - //If there are more than one checkboxes in this category, match by value + // If there are more than one checkboxes in this category, match by value if (checkboxes.length > 1) { - assocCheckbox = _.find(checkboxes, function (checkbox) { - return $(checkbox).val() == value; - }); + assocCheckbox = _.find( + checkboxes, + (checkbox) => $(checkbox).val() == value, + ); } - //If there is only one checkbox in this category, default to it + // If there is only one checkbox in this category, default to it else if (checkboxes.length == 1) { assocCheckbox = checkboxes[0]; } - //If there is an associated checkbox, uncheck it + // If there is an associated checkbox, uncheck it if (assocCheckbox) { - //Uncheck it + // Uncheck it $(assocCheckbox).prop("checked", false); } @@ -1343,8 +1336,8 @@ define([ }, // Clear all the currently applied filters - resetFilters: function () { - var viewRef = this; + resetFilters() { + const viewRef = this; this.allowSearch = true; @@ -1359,7 +1352,7 @@ define([ // Then reset the model this.searchModel.clear(); - //Reset the map model + // Reset the map model if (this.mapModel) { this.mapModel.clear(); } @@ -1369,7 +1362,7 @@ define([ this.searchModel.get("yearMin"), this.searchModel.get("yearMax"), ]); - //and the year inputs + // and the year inputs $("#min_year").val(this.searchModel.get("yearMin")); $("#max_year").val(this.searchModel.get("yearMax")); @@ -1394,32 +1387,30 @@ define([ this.triggerSearch(); }, - hideEl: function (element) { + hideEl(element) { // Fade out and remove the element - $(element).fadeOut("slow", function () { + $(element).fadeOut("slow", () => { $(element).remove(); }); }, // Removes a specified filter node from the DOM - hideFilter: function (category, value) { + hideFilter(category, value) { if (!this.filters) return; if (typeof value === "undefined") { var filterNode = this.$( - ".current-filters[data-category='" + category + "']", + `.current-filters[data-category='${category}']`, ).children(".current-filter"); } else { var filterNode = this.$( - ".current-filters[data-category='" + category + "']", - ).children("[data-term='" + value + "']"); + `.current-filters[data-category='${category}']`, + ).children(`[data-term='${value}']`); } // Try finding it a different way if (!filterNode || !filterNode.length) { - filterNode = this.$( - ".current-filter[data-category='" + category + "']", - ); + filterNode = this.$(`.current-filter[data-category='${category}']`); } // Remove the filter node from the DOM @@ -1427,29 +1418,21 @@ define([ }, // Adds a specified filter node to the DOM - showFilter: function ( - category, - term, - checkForDuplicates, - label, - options, - ) { + showFilter(category, term, checkForDuplicates, label, options) { if (!this.filters) return; - var viewRef = this; + const viewRef = this; if (typeof term === "undefined") return false; // Get the element to add the UI filter node to // The pattern is #current--filters - var filterContainer = this.$el.find( - "#current-" + category + "-filters", - ); + const filterContainer = this.$el.find(`#current-${category}-filters`); // Allow the option to only display this exact filter category and term once to the DOM // Helpful when adding a filter that is not stored in the search model (for display only) if (checkForDuplicates) { - var duplicate = false; + let duplicate = false; // Get the current terms from the DOM and check against the new term filterContainer.children().each(function () { @@ -1464,8 +1447,8 @@ define([ } } - var value = null, - desc = null; + let value = null; + let desc = null; // See if this filter is an object and extract the filter attributes if (typeof term === "object") { @@ -1496,7 +1479,7 @@ define([ desc = label; } - var categoryLabel = this.searchModel.fieldLabels[category]; + let categoryLabel = this.searchModel.fieldLabels[category]; if ( typeof categoryLabel === "undefined" && category == "additionalCriteria" @@ -1505,7 +1488,7 @@ define([ if (typeof categoryLabel === "undefined") categoryLabel = category; // Add a filter node to the DOM - var filterEl = viewRef.currentFilterTemplate({ + const filterEl = viewRef.currentFilterTemplate({ category: Utilities.encodeHTML(categoryLabel), value: Utilities.encodeHTML(value), label: Utilities.encodeHTML(label), @@ -1514,7 +1497,7 @@ define([ // Add the filter to the page - either replace or tack on if (options && options.replace) { - var currentFilter = filterContainer.find(".current-filter"); + const currentFilter = filterContainer.find(".current-filter"); if (currentFilter.length > 0) { currentFilter.replaceWith(filterEl); } else { @@ -1530,53 +1513,46 @@ define([ show: 800, }, }); - - return; }, /* * Get the member node list from the model and list the members in the filter list */ - listDataSources: function () { + listDataSources() { if (!this.filters) return; if (MetacatUI.nodeModel.get("members").length < 1) return; // Get the member nodes - var members = _.sortBy( - MetacatUI.nodeModel.get("members"), - function (m) { - if (m.name) { - return m.name.toLowerCase(); - } else { - return ""; - } - }, - ); - var filteredMembers = _.reject(members, function (m) { - return m.status != "operational"; + const members = _.sortBy(MetacatUI.nodeModel.get("members"), (m) => { + if (m.name) { + return m.name.toLowerCase(); + } + return ""; }); + const filteredMembers = _.reject( + members, + (m) => m.status != "operational", + ); // Get the current search filters for data source - var currentFilters = this.searchModel.get("dataSource"); + const currentFilters = this.searchModel.get("dataSource"); // Create an HTML list - var listMax = 4, - numHidden = filteredMembers.length - listMax, - list = $(document.createElement("ul")).addClass("checkbox-list"); + const listMax = 4; + const numHidden = filteredMembers.length - listMax; + const list = $(document.createElement("ul")).addClass("checkbox-list"); // Add a checkbox and label for each member node in the node model - _.each(filteredMembers, function (member, i) { - var listItem = document.createElement("li"), - input = document.createElement("input"), - label = document.createElement("label"); + _.each(filteredMembers, (member, i) => { + const listItem = document.createElement("li"); + const input = document.createElement("input"); + const label = document.createElement("label"); // If this member node is already a data source filter, then the checkbox is checked - var checked = _.findWhere(currentFilters, { + const checked = !!_.findWhere(currentFilters, { value: member.identifier, - }) - ? true - : false; + }); // Create a textual label for this data source $(label) @@ -1616,9 +1592,9 @@ define([ // Insert a "More" link after a certain amount to enable users to expand the list if (i == listMax) { - var moreLink = document.createElement("a"); + const moreLink = document.createElement("a"); $(moreLink) - .html("Show " + numHidden + " more") + .html(`Show ${numHidden} more`) .addClass("more-link pointer toggle-list") .append( $(document.createElement("i")).addClass("icon icon-expand-alt"), @@ -1632,7 +1608,7 @@ define([ }); if (numHidden > 0) { - var lessLink = document.createElement("a"); + const lessLink = document.createElement("a"); $(lessLink) .html("Collapse member nodes") .addClass("less-link toggle-list pointer hidden") @@ -1644,28 +1620,30 @@ define([ } // Add the list of checkboxes to the placeholder - var container = $(".member-nodes-placeholder"); + const container = $(".member-nodes-placeholder"); $(container).html(list); $(".tooltip-this").tooltip(); }, - resetDataSourceList: function () { + resetDataSourceList() { if (!this.filters) return; // Reset the Member Nodes checkboxes - var mnFilterContainer = $("#member-nodes-container"), - defaultMNs = this.searchModel.get("dataSource"); + const mnFilterContainer = $("#member-nodes-container"); + const defaultMNs = this.searchModel.get("dataSource"); // Make sure the member node filter exists if (!mnFilterContainer || mnFilterContainer.length == 0) return false; if (typeof defaultMNs === "undefined" || !defaultMNs) return false; // Reset each member node checkbox - var boxes = $(mnFilterContainer).find(".filter").prop("checked", false); + const boxes = $(mnFilterContainer) + .find(".filter") + .prop("checked", false); // Check the member node checkboxes that are defaults in the search model - _.each(defaultMNs, function (member, i) { - var value = null; + _.each(defaultMNs, (member, i) => { + let value = null; // Allow for string search model filter values and object filter values if (typeof member !== "object" && member) value = member; @@ -1674,20 +1652,20 @@ define([ else value = member.value; $(mnFilterContainer) - .find("checkbox[value='" + value + "']") + .find(`checkbox[value='${value}']`) .prop("checked", true); }); return true; }, - toggleList: function (e) { + toggleList(e) { if (!this.filters) return; - var link = e.target, - controls = $(link).parents("ul").find(".toggle-list"), - list = $(link).parents("ul"), - isHidden = !list.find(".more-link").is(".hidden"); + const link = e.target; + const controls = $(link).parents("ul").find(".toggle-list"); + const list = $(link).parents("ul"); + const isHidden = !list.find(".more-link").is(".hidden"); // Hide/Show the list if (isHidden) { @@ -1701,9 +1679,9 @@ define([ }, // add additional criteria to the search model based on link click - additionalCriteria: function (e) { + additionalCriteria(e) { // Get the clicked node - var targetNode = $(e.target); + const targetNode = $(e.target); // If this additional criteria is already applied, remove it if (targetNode.hasClass("active")) { @@ -1712,10 +1690,10 @@ define([ } // Get the filter criteria - var term = targetNode.attr("data-term"); + const term = targetNode.attr("data-term"); // Find this element's category in the data-category attribute - var category = targetNode.attr("data-category"); + const category = targetNode.attr("data-category"); // style the selection $(".keyword-search-link").removeClass("active"); @@ -1733,26 +1711,26 @@ define([ return false; }, - removeAdditionalCriteria: function (e) { + removeAdditionalCriteria(e) { // Get the clicked node - var targetNode = $(e.target); + const targetNode = $(e.target); // Reference to model - var model = this.searchModel; + const model = this.searchModel; // remove the styling $(".keyword-search-link").removeClass("active"); $(".keyword-search-link").parent().removeClass("active"); // Get the term - var term = targetNode.attr("data-term"); + const term = targetNode.attr("data-term"); // Get the current search model additional criteria - var current = this.searchModel.get("additionalCriteria"); + const current = this.searchModel.get("additionalCriteria"); // If this term is in the current search model (should be)... if (_.contains(current, term)) { - //then remove it - var newTerms = _.without(current, term); + // then remove it + const newTerms = _.without(current, term); model.set("additionalCriteria", newTerms); } @@ -1764,24 +1742,21 @@ define([ }, // Get the facet counts - getAutocompletes: function (e) { + getAutocompletes(e) { if (!e) return; // Get the text input to determine the filter type - var input = $(e.target), - category = input.attr("data-category"); + const input = $(e.target); + const category = input.attr("data-category"); if (!this.filters || !category) return; - var viewRef = this; + const viewRef = this; // Create the facet query by using our current search query - var facetQuery = - "q=" + - this.searchResults.currentquery + - "&rows=0" + - this.searchModel.getFacetQuery(category) + - "&wt=json&"; + const facetQuery = `q=${ + this.searchResults.currentquery + }&rows=0${this.searchModel.getFacetQuery(category)}&wt=json&`; // If we've cached these filter results, then use the cache instead of sending a new request if (!MetacatUI.appSearchModel.autocompleteCache) @@ -1795,25 +1770,25 @@ define([ } // Get the facet counts for the autocomplete - var requestSettings = { + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + facetQuery, type: "GET", dataType: "json", - success: function (data, textStatus, xhr) { - var suggestions = [], - facetLimit = 999; + success(data, textStatus, xhr) { + let suggestions = []; + const facetLimit = 999; // Get all the facet counts - _.each(category.split(","), function (c) { - if (typeof c == "string") c = [c]; - _.each(c, function (thisCategory) { + _.each(category.split(","), (c) => { + if (typeof c === "string") c = [c]; + _.each(c, (thisCategory) => { // Get the field name(s) - var fieldNames = + let fieldNames = MetacatUI.appSearchModel.facetNameMap[thisCategory]; - if (typeof fieldNames == "string") fieldNames = [fieldNames]; + if (typeof fieldNames === "string") fieldNames = [fieldNames]; // Get the facet counts - _.each(fieldNames, function (fieldName) { + _.each(fieldNames, (fieldName) => { suggestions.push(data.facet_counts.facet_fields[fieldName]); }); }); @@ -1821,24 +1796,24 @@ define([ suggestions = _.flatten(suggestions); // Format the suggestions - var rankedSuggestions = new Array(); + const rankedSuggestions = new Array(); for ( - var i = 0; + let i = 0; i < Math.min(suggestions.length - 1, facetLimit); i += 2 ) { - //The label is the item value - var label = suggestions[i]; + // The label is the item value + let label = suggestions[i]; - //For all categories except the 'all' category, display the facet count + // For all categories except the 'all' category, display the facet count if (category != "all") { - label += " (" + suggestions[i + 1] + ")"; + label += ` (${suggestions[i + 1]})`; } - //Push the autocomplete item to the array + // Push the autocomplete item to the array rankedSuggestions.push({ value: suggestions[i], - label: label, + label, }); } @@ -1858,14 +1833,14 @@ define([ ); }, - setupAutocomplete: function (input, rankedSuggestions) { - var viewRef = this; + setupAutocomplete(input, rankedSuggestions) { + const viewRef = this; - //Override the _renderItem() function which renders a single autocomplete item. + // Override the _renderItem() function which renders a single autocomplete item. // We want to use the 'title' HTML attribute on each item. // This method must create a new
  • element, append it to the menu, and return it. $.widget("custom.autocomplete", $.ui.autocomplete, { - _renderItem: function (ul, item) { + _renderItem(ul, item) { return $(document.createElement("li")) .attr("title", item.label) .append(item.label) @@ -1873,25 +1848,23 @@ define([ }, }); input.autocomplete({ - source: function (request, response) { - var term = $.ui.autocomplete.escapeRegex(request.term), - startsWithMatcher = new RegExp("^" + term, "i"), - startsWith = $.grep(rankedSuggestions, function (value) { - return startsWithMatcher.test( - value.label || value.value || value, - ); - }), - containsMatcher = new RegExp(term, "i"), - contains = $.grep(rankedSuggestions, function (value) { - return ( - $.inArray(value, startsWith) < 0 && - containsMatcher.test(value.label || value.value || value) - ); - }); + source(request, response) { + const term = $.ui.autocomplete.escapeRegex(request.term); + const startsWithMatcher = new RegExp(`^${term}`, "i"); + const startsWith = $.grep(rankedSuggestions, (value) => + startsWithMatcher.test(value.label || value.value || value), + ); + const containsMatcher = new RegExp(term, "i"); + const contains = $.grep( + rankedSuggestions, + (value) => + $.inArray(value, startsWith) < 0 && + containsMatcher.test(value.label || value.value || value), + ); response(startsWith.concat(contains)); }, - select: function (event, ui) { + select(event, ui) { // set the text field input.val(ui.item.value); // add to the filter immediately @@ -1907,7 +1880,7 @@ define([ }); }, - hideClearButton: function () { + hideClearButton() { if (!this.filters) return; // Hide the current filters panel @@ -1918,7 +1891,7 @@ define([ this.setAutoHeight(); }, - showClearButton: function () { + showClearButton() { if (!this.filters) return; // Show the current filters panel @@ -1942,7 +1915,7 @@ define([ * ================================================================================================== */ // Update all the statistics throughout the page - updateStats: function () { + updateStats() { if (this.searchResults.header != null) { this.$statcounts = this.$("#statcounts"); this.$statcounts.html( @@ -1960,9 +1933,9 @@ define([ this.updatePager(); }, - updatePager: function () { + updatePager() { if (this.searchResults.header != null) { - var pageCount = Math.ceil( + const pageCount = Math.ceil( this.searchResults.header.get("numFound") / this.searchResults.header.get("rows"), ); @@ -1981,10 +1954,10 @@ define([ this.$("#resultspager").html(""); this.$(".resultspager").html(""); } else { - var pages = new Array(pageCount); + const pages = new Array(pageCount); // mark current page correctly, avoid NaN - var currentPage = -1; + let currentPage = -1; try { currentPage = Math.floor( (this.searchResults.header.get("start") / @@ -1992,39 +1965,39 @@ define([ pageCount, ); } catch (ex) { - console.log("Exception when calculating pages:" + ex.message); + console.log(`Exception when calculating pages:${ex.message}`); } // Populate the pagination element in the UI this.$(".resultspager").html( this.pagerTemplate({ - pages: pages, - currentPage: currentPage, + pages, + currentPage, }), ); this.$("#resultspager").html( this.pagerTemplate({ - pages: pages, - currentPage: currentPage, + pages, + currentPage, }), ); } } }, - updatePageNumber: function (page) { + updatePageNumber(page) { MetacatUI.appModel.set("page", page); if (!this.isSubView) { - var route = Backbone.history.fragment, - subroutePos = route.indexOf("/page/"), - newPage = parseInt(page) + 1; + let route = Backbone.history.fragment; + const subroutePos = route.indexOf("/page/"); + const newPage = parseInt(page) + 1; - //replace the last number with the new one + // replace the last number with the new one if (page > 0 && subroutePos > -1) { route = route.replace(/\d+$/, newPage); } else if (page > 0) { - route += "/page/" + newPage; + route += `/page/${newPage}`; } else if (subroutePos >= 0) { route = route.substring(0, subroutePos); } @@ -2034,35 +2007,35 @@ define([ }, // Next page of results - nextpage: function () { + nextpage() { this.loading(); this.searchResults.nextpage(); this.$resultsview.show(); this.updateStats(); - var page = MetacatUI.appModel.get("page"); + let page = MetacatUI.appModel.get("page"); page++; this.updatePageNumber(page); }, // Previous page of results - prevpage: function () { + prevpage() { this.loading(); this.searchResults.prevpage(); this.$resultsview.show(); this.updateStats(); - var page = MetacatUI.appModel.get("page"); + let page = MetacatUI.appModel.get("page"); page--; this.updatePageNumber(page); }, - navigateToPage: function (event) { - var page = $(event.target).attr("page"); + navigateToPage(event) { + const page = $(event.target).attr("page"); this.showPage(page); }, - showPage: function (page) { + showPage(page) { this.loading(); this.searchResults.toPage(page); this.$resultsview.show(); @@ -2076,7 +2049,7 @@ define([ * THE MAP * ================================================================================================== */ - renderMap: function () { + renderMap() { // If gmaps isn't enabled or loaded with an error, use list mode if (!gmaps || this.mode == "list") { this.ready = true; @@ -2092,8 +2065,8 @@ define([ // Get the map options and create the map gmaps.visualRefresh = true; - var mapOptions = this.mapModel.get("mapOptions"); - var defaultZoom = mapOptions.zoom; + const mapOptions = this.mapModel.get("mapOptions"); + const defaultZoom = mapOptions.zoom; $("#map-container").append("
    "); this.map = new gmaps.Map($("#map-canvas")[0], mapOptions); this.mapModel.set("map", this.map); @@ -2104,61 +2077,61 @@ define([ this.$(this.mapFilterToggle).hide(); // Store references - var mapRef = this.map; - var viewRef = this; + const mapRef = this.map; + const viewRef = this; - google.maps.event.addListener(mapRef, "zoom_changed", function () { + google.maps.event.addListener(mapRef, "zoom_changed", () => { // If the map is zoomed in further than the default zoom level, // than we want to mark the map as zoomed in if (viewRef.map.getZoom() > defaultZoom) { viewRef.hasZoomed = true; } - //If we are at the default zoom level or higher, than do not mark the map + // If we are at the default zoom level or higher, than do not mark the map // as zoomed in else { viewRef.hasZoomed = false; } }); - google.maps.event.addListener(mapRef, "dragend", function () { + google.maps.event.addListener(mapRef, "dragend", () => { viewRef.hasDragged = true; }); - google.maps.event.addListener(mapRef, "idle", function () { + google.maps.event.addListener(mapRef, "idle", () => { // Remove all markers from the map - for (var i = 0; i < viewRef.resultMarkers.length; i++) { + for (let i = 0; i < viewRef.resultMarkers.length; i++) { viewRef.resultMarkers[i].setMap(null); } viewRef.resultMarkers = new Array(); - //Check if the user has interacted with the map just now, and if so, we + // Check if the user has interacted with the map just now, and if so, we // want to alter the geohash filter (changing the geohash values or resetting it completely) - var alterGeohashFilter = + const alterGeohashFilter = viewRef.allowSearch || viewRef.hasZoomed || viewRef.hasDragged; if (!alterGeohashFilter) { return; } - //Determine if the map needs to be recentered. The map only needs to be + // Determine if the map needs to be recentered. The map only needs to be // recentered if it is not at the default lat,long center point AND it // is not zoomed in or dragged to a new center point - var setGeohashFilter = + const setGeohashFilter = viewRef.hasZoomed && viewRef.isMapFilterEnabled(); - //If we are using the geohash filter defined by this map, then + // If we are using the geohash filter defined by this map, then // apply the filter and trigger a new search if (setGeohashFilter) { viewRef.$(viewRef.mapFilterToggle).show(); // Get the Google map bounding box - var boundingBox = mapRef.getBounds(); + const boundingBox = mapRef.getBounds(); // Set the search model spatial filters // Encode the Google Map bounding box into geohash - var north = boundingBox.getNorthEast().lat(), - west = boundingBox.getSouthWest().lng(), - south = boundingBox.getSouthWest().lat(), - east = boundingBox.getNorthEast().lng(); + const north = boundingBox.getNorthEast().lat(); + const west = boundingBox.getSouthWest().lng(); + const south = boundingBox.getSouthWest().lat(); + const east = boundingBox.getNorthEast().lng(); viewRef.searchModel.set("north", north); viewRef.searchModel.set("west", west); @@ -2170,12 +2143,12 @@ define([ viewRef.mapModel.get("mapOptions").zoom = mapRef.getZoom(); // Determine the precision of geohashes to search for - var zoom = mapRef.getZoom(); + const zoom = mapRef.getZoom(); - var precision = viewRef.mapModel.getSearchPrecision(zoom); + const precision = viewRef.mapModel.getSearchPrecision(zoom); // Get all the geohash tiles contained in the map bounds - var geohashBBoxes = nGeohash.bboxes( + const geohashBBoxes = nGeohash.bboxes( south, west, north, @@ -2187,10 +2160,10 @@ define([ viewRef.searchModel.set("geohashes", geohashBBoxes); viewRef.searchModel.set("geohashLevel", precision); - //Start back at page 0 + // Start back at page 0 MetacatUI.appModel.set("page", 0); - //Mark the view as ready to start a search + // Mark the view as ready to start a search viewRef.ready = true; // Trigger a new search @@ -2198,37 +2171,35 @@ define([ viewRef.allowSearch = false; } else { - //Reset the map filter + // Reset the map filter viewRef.resetMap(); - //Start back at page 0 + // Start back at page 0 MetacatUI.appModel.set("page", 0); - //Mark the view as ready to start a search + // Mark the view as ready to start a search viewRef.ready = true; // Trigger a new search viewRef.triggerSearch(); viewRef.allowSearch = false; - - return; } }); }, // Resets the model and view settings related to the map - resetMap: function () { + resetMap() { if (!gmaps) { return; } // First reset the model // The categories pertaining to the map - var categories = ["east", "west", "north", "south"]; + const categories = ["east", "west", "north", "south"]; // Loop through each and remove the filters from the model - for (var i = 0; i < categories.length; i++) { + for (let i = 0; i < categories.length; i++) { this.searchModel.set(categories[i], null); } @@ -2239,18 +2210,18 @@ define([ this.allowSearch = false; }, - isMapFilterEnabled: function () { - var toggleInput = this.$("input" + this.mapFilterToggle); + isMapFilterEnabled() { + const toggleInput = this.$(`input${this.mapFilterToggle}`); if (typeof toggleInput === "undefined" || !toggleInput) return; return $(toggleInput).prop("checked"); }, - toggleMapFilter: function (e, a) { - var toggleInput = this.$("input" + this.mapFilterToggle); + toggleMapFilter(e, a) { + const toggleInput = this.$(`input${this.mapFilterToggle}`); if (typeof toggleInput === "undefined" || !toggleInput) return; - var isOn = $(toggleInput).prop("checked"); + let isOn = $(toggleInput).prop("checked"); // If the user clicked on the label, then change the checkbox for them if (e.target.tagName != "INPUT") { @@ -2265,25 +2236,25 @@ define([ }, /** - * Show the marker, infoWindow, and bounding coordinates polygon on + * Show the marker, infoWindow, and bounding coordinates polygon on the map when the user hovers on the marker icon in the result list - * @param {Event} e - */ - showResultOnMap: function (e) { + * @param {Event} e + */ + showResultOnMap(e) { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; } // Get the attributes about this dataset - var resultRow = e.target, - id = $(resultRow).attr("data-id"); + let resultRow = e.target; + let id = $(resultRow).attr("data-id"); // The mouseover event might be triggered by a nested element, so loop through the parents to find the id - if (typeof id == "undefined") { + if (typeof id === "undefined") { $(resultRow) .parents() .each(function () { - if (typeof $(this).attr("data-id") != "undefined") { + if (typeof $(this).attr("data-id") !== "undefined") { id = $(this).attr("data-id"); resultRow = this; } @@ -2291,24 +2262,25 @@ define([ } // Find the tile for this data set and highlight it on the map - var resultGeohashes = this.searchResults + const resultGeohashes = this.searchResults .findWhere({ - id: id, + id, }) .get("geohash_9"); - for (var i = 0; i < resultGeohashes.length; i++) { - var thisGeohash = resultGeohashes[i], - latLong = nGeohash.decode(thisGeohash), - position = new google.maps.LatLng( - latLong.latitude, - latLong.longitude, - ), - containingTileGeohash = _.find(this.tileGeohashes, function (g) { - return thisGeohash.indexOf(g) == 0; - }), - containingTile = _.findWhere(this.tiles, { - geohash: containingTileGeohash, - }); + for (let i = 0; i < resultGeohashes.length; i++) { + var thisGeohash = resultGeohashes[i]; + const latLong = nGeohash.decode(thisGeohash); + const position = new google.maps.LatLng( + latLong.latitude, + latLong.longitude, + ); + const containingTileGeohash = _.find( + this.tileGeohashes, + (g) => thisGeohash.indexOf(g) == 0, + ); + const containingTile = _.findWhere(this.tiles, { + geohash: containingTileGeohash, + }); // If this is a geohash for a georegion outside the map, do not highlight a tile or display a marker if (typeof containingTile === "undefined") continue; @@ -2316,40 +2288,40 @@ define([ this.highlightTile(containingTile); // Set up the options for each marker - var markerOptions = { - position: position, + const markerOptions = { + position, icon: this.mapModel.get("markerImage"), zIndex: 99999, map: this.map, }; // Create the marker and add to the map - var marker = new google.maps.Marker(markerOptions); + const marker = new google.maps.Marker(markerOptions); this.resultMarkers.push(marker); } }, /** - * Hide the marker, infoWindow, and bounding coordinates polygon on + * Hide the marker, infoWindow, and bounding coordinates polygon on the map when the user stops hovering on the marker icon in the result list - * @param {Event} e - The event that brought us to this function - */ - hideResultOnMap: function (e) { + * @param {Event} e - The event that brought us to this function + */ + hideResultOnMap(e) { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; } // Get the attributes about this dataset - var resultRow = e.target, - id = $(resultRow).attr("data-id"); + let resultRow = e.target; + let id = $(resultRow).attr("data-id"); // The mouseover event might be triggered by a nested element, so loop through the parents to find the id - if (typeof id == "undefined") { + if (typeof id === "undefined") { $(e.target) .parents() .each(function () { - if (typeof $(this).attr("data-id") != "undefined") { + if (typeof $(this).attr("data-id") !== "undefined") { id = $(this).attr("data-id"); resultRow = this; } @@ -2357,19 +2329,20 @@ define([ } // Get the map tile for this result and un-highlight it - var resultGeohashes = this.searchResults + const resultGeohashes = this.searchResults .findWhere({ - id: id, + id, }) .get("geohash_9"); - for (var i = 0; i < resultGeohashes.length; i++) { - var thisGeohash = resultGeohashes[i], - containingTileGeohash = _.find(this.tileGeohashes, function (g) { - return thisGeohash.indexOf(g) == 0; - }), - containingTile = _.findWhere(this.tiles, { - geohash: containingTileGeohash, - }); + for (let i = 0; i < resultGeohashes.length; i++) { + var thisGeohash = resultGeohashes[i]; + const containingTileGeohash = _.find( + this.tileGeohashes, + (g) => thisGeohash.indexOf(g) == 0, + ); + const containingTile = _.findWhere(this.tiles, { + geohash: containingTileGeohash, + }); // If this is a geohash for a georegion outside the map, do not unhighlight a tile if (typeof containingTile === "undefined") continue; @@ -2379,7 +2352,7 @@ define([ } // Remove all markers from the map - _.each(this.resultMarkers, function (marker) { + _.each(this.resultMarkers, (marker) => { marker.setMap(null); }); this.resultMarkers = new Array(); @@ -2387,8 +2360,8 @@ define([ /** * Create a tile for each geohash facet. A separate tile label is added to the map with the count of the facet. - **/ - drawTiles: function () { + */ + drawTiles() { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; @@ -2396,6 +2369,10 @@ define([ TextOverlay.prototype = new google.maps.OverlayView(); + /** + * + * @param options + */ function TextOverlay(options) { // Now initialize all properties. this.bounds_ = options.bounds; @@ -2403,7 +2380,7 @@ define([ this.text = options.text; this.color = options.color; - var length = options.text.toString().length; + const { length } = options.text.toString(); if (length == 1) this.width = 8; else if (length == 2) this.width = 17; else if (length == 3) this.width = 25; @@ -2421,7 +2398,7 @@ define([ TextOverlay.prototype.onAdd = function () { // Create the DIV and set some basic attributes. - var div = document.createElement("div"); + const div = document.createElement("div"); div.style.color = this.color; div.style.fontSize = "15px"; div.style.position = "absolute"; @@ -2436,7 +2413,7 @@ define([ // We add an overlay to a map via one of the map's panes. // We'll add this overlay to the overlayLayer pane. - var panes = this.getPanes(); + const panes = this.getPanes(); panes.overlayLayer.appendChild(div); }; @@ -2444,28 +2421,28 @@ define([ // Size and position the overlay. We use a southwest and northeast // position of the overlay to peg it to the correct position and size. // We need to retrieve the projection from this overlay to do this. - var overlayProjection = this.getProjection(); + const overlayProjection = this.getProjection(); // Retrieve the southwest and northeast coordinates of this overlay // in latlngs and convert them to pixels coordinates. // We'll use these coordinates to resize the DIV. - var sw = overlayProjection.fromLatLngToDivPixel( + const sw = overlayProjection.fromLatLngToDivPixel( this.bounds_.getSouthWest(), ); - var ne = overlayProjection.fromLatLngToDivPixel( + const ne = overlayProjection.fromLatLngToDivPixel( this.bounds_.getNorthEast(), ); // Resize the image's DIV to fit the indicated dimensions. - var div = this.div_; - var width = this.width; - var height = 20; - - div.style.left = sw.x - width / 2 + "px"; - div.style.top = ne.y - height / 2 + "px"; - div.style.width = width + "px"; - div.style.height = height + "px"; - div.style.width = width + "px"; - div.style.height = height + "px"; + const div = this.div_; + const { width } = this; + const height = 20; + + div.style.left = `${sw.x - width / 2}px`; + div.style.top = `${ne.y - height / 2}px`; + div.style.width = `${width}px`; + div.style.height = `${height}px`; + div.style.width = `${width}px`; + div.style.height = `${height}px`; }; TextOverlay.prototype.onRemove = function () { @@ -2474,16 +2451,17 @@ define([ }; // Determine the geohash level we will use to draw tiles - var currentZoom = this.map.getZoom(), - geohashLevelNum = this.mapModel.determineGeohashLevel(currentZoom), - geohashLevel = "geohash_" + geohashLevelNum, - geohashes = this.searchResults.facetCounts[geohashLevel]; + const currentZoom = this.map.getZoom(); + const geohashLevelNum = + this.mapModel.determineGeohashLevel(currentZoom); + const geohashLevel = `geohash_${geohashLevelNum}`; + const geohashes = this.searchResults.facetCounts[geohashLevel]; // Save the current geohash level in the map model this.mapModel.set("tileGeohashLevel", geohashLevelNum); // Get all the geohashes contained in the map - var mapBBoxes = _.flatten( + const mapBBoxes = _.flatten( _.values(this.searchModel.get("geohashGroups")), ); @@ -2495,10 +2473,10 @@ define([ var filteredTileGeohashes = []; for (var i = 0; i < geohashes.length - 1; i += 2) { // Get the geohash for this tile - var tileGeohash = geohashes[i], - isInsideMap = false, - index = 0, - searchString = tileGeohash; + var tileGeohash = geohashes[i]; + let isInsideMap = false; + let index = 0; + let searchString = tileGeohash; // Find if any of the bounding boxes/geohashes inside our map contain this tile geohash while (!isInsideMap && searchString.length > 0) { @@ -2517,59 +2495,56 @@ define([ } } - //If there are no tiles on the page, the map may have failed to render, so exit. + // If there are no tiles on the page, the map may have failed to render, so exit. if ( - typeof filteredTileGeohashes == "undefined" || + typeof filteredTileGeohashes === "undefined" || !filteredTileGeohashes.length ) { return; } // Make a copy of the array that is geohash counts only - var countsOnly = []; + const countsOnly = []; for (var i = 1; i < filteredTileGeohashes.length; i += 2) { countsOnly.push(filteredTileGeohashes[i]); } // Create a range of lightness to make different colors on the tiles - var lightnessMin = this.mapModel.get("tileLightnessMin"), - lightnessMax = this.mapModel.get("tileLightnessMax"), - lightnessRange = lightnessMax - lightnessMin; + const lightnessMin = this.mapModel.get("tileLightnessMin"); + const lightnessMax = this.mapModel.get("tileLightnessMax"); + const lightnessRange = lightnessMax - lightnessMin; // Get some stats on our tile counts so we can normalize them to create a color scale - var findMedian = function (nums) { + const findMedian = function (nums) { if (nums.length % 2 == 0) { return (nums[nums.length / 2 - 1] + nums[nums.length / 2]) / 2; - } else { - return nums[nums.length / 2 - 0.5]; } + return nums[nums.length / 2 - 0.5]; }; - var sortedCounts = countsOnly.sort(function (a, b) { - return a - b; - }), - maxCount = sortedCounts[sortedCounts.length - 1], - minCount = sortedCounts[0]; + const sortedCounts = countsOnly.sort((a, b) => a - b); + const maxCount = sortedCounts[sortedCounts.length - 1]; + const minCount = sortedCounts[0]; - var viewRef = this; + const viewRef = this; // Now draw a tile for each geohash facet for (var i = 0; i < filteredTileGeohashes.length - 1; i += 2) { // Convert this geohash to lat,long values - var tileGeohash = filteredTileGeohashes[i], - decodedGeohash = nGeohash.decode(tileGeohash), - latLngCenter = new google.maps.LatLng( - decodedGeohash.latitude, - decodedGeohash.longitude, - ), - geohashBox = nGeohash.decode_bbox(tileGeohash), - swLatLng = new google.maps.LatLng(geohashBox[0], geohashBox[1]), - neLatLng = new google.maps.LatLng(geohashBox[2], geohashBox[3]), - bounds = new google.maps.LatLngBounds(swLatLng, neLatLng), - tileCount = filteredTileGeohashes[i + 1], - drawMarkers = this.mapModel.get("drawMarkers"), - marker, - count, - color; + var tileGeohash = filteredTileGeohashes[i]; + const decodedGeohash = nGeohash.decode(tileGeohash); + const latLngCenter = new google.maps.LatLng( + decodedGeohash.latitude, + decodedGeohash.longitude, + ); + const geohashBox = nGeohash.decode_bbox(tileGeohash); + const swLatLng = new google.maps.LatLng(geohashBox[0], geohashBox[1]); + const neLatLng = new google.maps.LatLng(geohashBox[2], geohashBox[3]); + const bounds = new google.maps.LatLngBounds(swLatLng, neLatLng); + const tileCount = filteredTileGeohashes[i + 1]; + const drawMarkers = this.mapModel.get("drawMarkers"); + var marker; + var count; + var color; // Normalize the range of tiles counts and convert them to a lightness domain of 20-70% lightness. if (maxCount - minCount == 0) { @@ -2581,11 +2556,10 @@ define([ lightnessMin; } - var color = - "hsl(" + this.mapModel.get("tileHue") + "," + lightness + "%,50%)"; + var color = `hsl(${this.mapModel.get("tileHue")},${lightness}%,50%)`; // Add the count to the tile - var countLocation = new google.maps.LatLngBounds( + const countLocation = new google.maps.LatLngBounds( latLngCenter, latLngCenter, ); @@ -2599,22 +2573,22 @@ define([ }); // Set up the default tile options - var tileOptions = { + const tileOptions = { fillColor: color, strokeColor: color, map: this.map, visible: true, - bounds: bounds, + bounds, }; // Merge these options with any tile options set in the map model - var modelTileOptions = this.mapModel.get("tileOptions"); - for (var attr in modelTileOptions) { + const modelTileOptions = this.mapModel.get("tileOptions"); + for (const attr in modelTileOptions) { tileOptions[attr] = modelTileOptions[attr]; } // Draw this tile - var tile = this.drawTile(tileOptions, tileGeohash, count); + const tile = this.drawTile(tileOptions, tileGeohash, count); // Save the geohashes for tiles in the view for later this.tileGeohashes.push(tileGeohash); @@ -2632,47 +2606,51 @@ define([ * @param {object} options * @param {string} geohash * @param {string} label - **/ - drawTile: function (options, geohash, label) { + */ + drawTile(options, geohash, label) { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; } // Add the tile for these datasets to the map - var tile = new google.maps.Rectangle(options); + const tile = new google.maps.Rectangle(options); - var viewRef = this; + const viewRef = this; // Save our tiles in the view - var tileObject = { + const tileObject = { text: label, shape: tile, - geohash: geohash, - options: options, + geohash, + options, }; this.tiles.push(tileObject); // Change styles when the tile is hovered on - google.maps.event.addListener(tile, "mouseover", function (event) { + google.maps.event.addListener(tile, "mouseover", (event) => { viewRef.highlightTile(tileObject); }); // Change the styles back after the tile is hovered on - google.maps.event.addListener(tile, "mouseout", function (event) { + google.maps.event.addListener(tile, "mouseout", (event) => { viewRef.unhighlightTile(tileObject); }); // If we are at the max zoom, we will display an info window. If not, we will zoom in. if (!this.mapModel.isMaxZoom(viewRef.map)) { - /** Set up some helper functions for zooming in on the map **/ - var myFitBounds = function (myMap, bounds) { + /** + * Set up some helper functions for zooming in on the map + * @param myMap + * @param bounds + */ + const myFitBounds = function (myMap, bounds) { myMap.fitBounds(bounds); // calling fitBounds() here to center the map for the bounds - var overlayHelper = new google.maps.OverlayView(); + const overlayHelper = new google.maps.OverlayView(); overlayHelper.draw = function () { if (!this.ready) { - var extraZoom = getExtraZoom( + const extraZoom = getExtraZoom( this.getProjection(), bounds, myMap.getBounds(), @@ -2692,11 +2670,11 @@ define([ actualBounds, ) { // in: LatLngBounds bounds -> out: height and width as a Point - var getSizeInPixels = function (bounds) { - var sw = projection.fromLatLngToContainerPixel( + const getSizeInPixels = function (bounds) { + const sw = projection.fromLatLngToContainerPixel( bounds.getSouthWest(), ); - var ne = projection.fromLatLngToContainerPixel( + const ne = projection.fromLatLngToContainerPixel( bounds.getNorthEast(), ); return new google.maps.Point( @@ -2705,8 +2683,8 @@ define([ ); }; - var expectedSize = getSizeInPixels(expectedBounds), - actualSize = getSizeInPixels(actualBounds); + const expectedSize = getSizeInPixels(expectedBounds); + const actualSize = getSizeInPixels(actualBounds); if ( Math.floor(expectedSize.x) == 0 || @@ -2715,9 +2693,9 @@ define([ return 0; } - var qx = actualSize.x / expectedSize.x; - var qy = actualSize.y / expectedSize.y; - var min = Math.min(qx, qy); + const qx = actualSize.x / expectedSize.x; + const qy = actualSize.y / expectedSize.y; + const min = Math.min(qx, qy); if (min < 1) { return 0; @@ -2727,24 +2705,24 @@ define([ }; // Zoom in when the tile is clicked on - gmaps.event.addListener(tile, "click", function (clickEvent) { + gmaps.event.addListener(tile, "click", (clickEvent) => { // Change the center viewRef.map.panTo(clickEvent.latLng); // Get this tile's bounds - var tileBounds = tile.getBounds(); + const tileBounds = tile.getBounds(); // Get the current map bounds - var mapBounds = viewRef.map.getBounds(); + const mapBounds = viewRef.map.getBounds(); // Change the zoom - //viewRef.map.fitBounds(tileBounds); + // viewRef.map.fitBounds(tileBounds); myFitBounds(viewRef.map, tileBounds); // Track this event MetacatUI.analytics?.trackEvent( "map", "clickTile", - "geohash : " + tileObject.geohash, + `geohash : ${tileObject.geohash}`, ); }); } @@ -2752,12 +2730,12 @@ define([ return tile; }, - highlightTile: function (tile) { + highlightTile(tile) { // Change the tile style on hover tile.shape.setOptions(this.mapModel.get("tileOnHover")); // Change the label color on hover - var div = tile.text.div_; + const div = tile.text.div_; if (div) { div.style.color = this.mapModel.get("tileLabelColorOnHover"); tile.text.div_ = div; @@ -2765,12 +2743,12 @@ define([ } }, - unhighlightTile: function (tile) { + unhighlightTile(tile) { // Change back the tile to it's original styling tile.shape.setOptions(tile.options); // Change back the label color - var div = tile.text.div_; + const div = tile.text.div_; div.style.color = this.mapModel.get("tileLabelColor"); tile.text.div_ = div; $(div).css("color", this.mapModel.get("tileLabelColor")); @@ -2780,47 +2758,43 @@ define([ * Get the details on each marker * And create an infowindow for that marker */ - addMarkers: function () { + addMarkers() { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; } // Clone the Search model - var searchModelClone = this.searchModel.clone(), - geohashLevel = this.mapModel.get("tileGeohashLevel"), - viewRef = this, - markers = this.markers; + const searchModelClone = this.searchModel.clone(); + const geohashLevel = this.mapModel.get("tileGeohashLevel"); + const viewRef = this; + const { markers } = this; // Change the geohash filter to match our tiles searchModelClone.set("geohashLevel", geohashLevel); searchModelClone.set("geohashes", this.markerGeohashes); // Now run a query to get a list of documents that are represented by our markers - var query = - "q=" + - searchModelClone.getQuery() + - "&fl=id,title,geohash_9,abstract,geohash_" + - geohashLevel + - "&rows=1000" + - "&wt=json"; - - var requestSettings = { + const query = + `q=${searchModelClone.getQuery()}&fl=id,title,geohash_9,abstract,geohash_${geohashLevel}&rows=1000` + + `&wt=json`; + + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (data, textStatus, xhr) { - var docs = data.response.docs; - var uniqueGeohashes = viewRef.markerGeohashes; + success(data, textStatus, xhr) { + const { docs } = data.response; + let uniqueGeohashes = viewRef.markerGeohashes; // Create a marker and infoWindow for each document - _.each(docs, function (doc, key, list) { - var marker, - drawMarkersAt = []; + _.each(docs, (doc, key, list) => { + let marker; + const drawMarkersAt = []; // Find the tile place that this document belongs to // For each geohash value at the current geohash level for this document, - _.each(doc.geohash_9, function (geohash, key, list) { + _.each(doc.geohash_9, (geohash, key, list) => { // Loop through each unique tile location to find its match - for (var i = 0; i <= uniqueGeohashes.length; i++) { + for (let i = 0; i <= uniqueGeohashes.length; i++) { if (uniqueGeohashes[i] == geohash.substr(0, geohashLevel)) { drawMarkersAt.push(geohash); uniqueGeohashes = _.without(uniqueGeohashes, geohash); @@ -2829,14 +2803,14 @@ define([ }); _.each(drawMarkersAt, function (markerGeohash, key, list) { - var decodedGeohash = nGeohash.decode(markerGeohash), - latLng = new google.maps.LatLng( - decodedGeohash.latitude, - decodedGeohash.longitude, - ); + const decodedGeohash = nGeohash.decode(markerGeohash); + const latLng = new google.maps.LatLng( + decodedGeohash.latitude, + decodedGeohash.longitude, + ); // Set up the options for each marker - var markerOptions = { + const markerOptions = { position: latLng, icon: this.mapModel.get("markerImage"), zIndex: 99999, @@ -2844,7 +2818,7 @@ define([ }; // Create the marker and add to the map - var marker = new google.maps.Marker(markerOptions); + const marker = new google.maps.Marker(markerOptions); }); }); }, @@ -2861,60 +2835,51 @@ define([ * Get the details on each tile - a list of ids and titles for each dataset contained in that tile * And create an infowindow for that tile */ - addTileInfoWindows: function () { + addTileInfoWindows() { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; } // Clone the Search model - var searchModelClone = this.searchModel.clone(), - geohashLevel = this.mapModel.get("tileGeohashLevel"), - geohashName = "geohash_" + geohashLevel, - viewRef = this, - infoWindows = []; + const searchModelClone = this.searchModel.clone(); + const geohashLevel = this.mapModel.get("tileGeohashLevel"); + const geohashName = `geohash_${geohashLevel}`; + const viewRef = this; + const infoWindows = []; // Change the geohash filter to match our tiles searchModelClone.set("geohashLevel", geohashLevel); searchModelClone.set("geohashes", this.tileGeohashes); // Now run a query to get a list of documents that are represented by our tiles - var query = - "q=" + - searchModelClone.getQuery() + - "&fl=id,title,geohash_9," + - geohashName + - "&rows=1000" + - "&wt=json"; - - var requestSettings = { + const query = + `q=${searchModelClone.getQuery()}&fl=id,title,geohash_9,${geohashName}&rows=1000` + + `&wt=json`; + + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (data, textStatus, xhr) { + success(data, textStatus, xhr) { // Make an infoWindow for each doc - var docs = data.response.docs; + const { docs } = data.response; // For each tile, loop through the docs to find which ones to include in its infoWindow - _.each(viewRef.tiles, function (tile, key, list) { - var infoWindowContent = ""; + _.each(viewRef.tiles, (tile, key, list) => { + let infoWindowContent = ""; - _.each(docs, function (doc, key, list) { - var docGeohashes = doc[geohashName]; + _.each(docs, (doc, key, list) => { + const docGeohashes = doc[geohashName]; if (docGeohashes) { // Is this document in this tile? - for (var i = 0; i < docGeohashes.length; i++) { + for (let i = 0; i < docGeohashes.length; i++) { if (docGeohashes[i] == tile.geohash) { // Add this doc to the infoWindow content - infoWindowContent += - "" + - doc.title + - " (" + - doc.id + - ")
    "; + infoWindowContent += `${doc.title} (${ + doc.id + })
    `; break; } } @@ -2922,21 +2887,19 @@ define([ }); // The center of the tile - var decodedGeohash = nGeohash.decode(tile.geohash), - tileCenter = new google.maps.LatLng( - decodedGeohash.latitude, - decodedGeohash.longitude, - ); + const decodedGeohash = nGeohash.decode(tile.geohash); + const tileCenter = new google.maps.LatLng( + decodedGeohash.latitude, + decodedGeohash.longitude, + ); // The infowindow - var infoWindow = new gmaps.InfoWindow({ + const infoWindow = new gmaps.InfoWindow({ content: - "
    " + - "

    Datasets located here

    " + - "

    " + - infoWindowContent + - "

    " + - "
    ", + `
    ` + + `

    Datasets located here

    ` + + `

    ${infoWindowContent}

    ` + + `
    `, isOpen: false, disableAutoPan: false, maxWidth: 250, @@ -2950,7 +2913,7 @@ define([ tile.shape, "click", function (clickEvent) { - //--- We are at max zoom, display an infowindow ----// + // --- We are at max zoom, display an infowindow ----// if (this.mapModel.isMaxZoom(viewRef.map)) { // Find the infowindow that belongs to this tile in the view infoWindow.open(viewRef.map); @@ -2960,13 +2923,13 @@ define([ viewRef.closeInfoWindows(infoWindow); } - //------ We are not at max zoom, so zoom into this tile ----// + // ------ We are not at max zoom, so zoom into this tile ----// else { // Change the center viewRef.map.panTo(clickEvent.latLng); // Get this tile's bounds - var bounds = tile.shape.getBounds(); + const bounds = tile.shape.getBounds(); // Change the zoom viewRef.map.fitBounds(bounds); @@ -2975,7 +2938,7 @@ define([ ); // Close the infowindow upon any click on the map - gmaps.event.addListener(viewRef.map, "click", function () { + gmaps.event.addListener(viewRef.map, "click", () => { infoWindow.close(); infoWindow.isOpen = false; }); @@ -2998,13 +2961,14 @@ define([ * Iterate over each infowindow that we have stored in the view and close it. * Pass an infoWindow object to this function to keep that infoWindow open/skip it * @param {infoWindow} - An infoWindow to keep open + * @param except */ - closeInfoWindows: function (except) { - var infoWindowLists = [this.markerInfoWindows, this.tileInfoWindows]; + closeInfoWindows(except) { + const infoWindowLists = [this.markerInfoWindows, this.tileInfoWindows]; - _.each(infoWindowLists, function (infoWindows, key, list) { + _.each(infoWindowLists, (infoWindows, key, list) => { // Iterate over all the marker infowindows and close all of them except for this one - for (var i = 0; i < infoWindows.length; i++) { + for (let i = 0; i < infoWindows.length; i++) { if (infoWindows[i].isOpen && infoWindows[i] != except) { // Close this info window and stop looking, since only one of each kind should be open anyway infoWindows[i].close(); @@ -3017,15 +2981,15 @@ define([ /** * Remove all the tiles and text from the map - **/ - removeTiles: function () { + */ + removeTiles() { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; } // Remove the tile from the map - _.each(this.tiles, function (tile, key, list) { + _.each(this.tiles, (tile, key, list) => { if (tile.shape) tile.shape.setMap(null); if (tile.text) tile.text.setMap(null); }); @@ -3039,14 +3003,14 @@ define([ /** * Iterate over all the markers in the view and remove them from the map and view */ - removeMarkers: function () { + removeMarkers() { // Exit if maps are not in use if (this.mode != "map" || !gmaps) { return false; } // Remove the marker from the map - _.each(this.markers, function (marker, key, list) { + _.each(this.markers, (marker, key, list) => { marker.marker.setMap(null); }); @@ -3062,17 +3026,18 @@ define([ * ================================================================================================== */ - /** Add all items in the **SearchResults** collection + /** + * Add all items in the **SearchResults** collection * This loads the first 25, then waits for the map to be * fully loaded and then loads the remaining items. * Without this delay, the app waits until all records are processed */ - addAll: function () { + addAll() { // After the map is done loading, then load the rest of the results into the list if (this.ready) this.renderAll(); else { - var viewRef = this; - var intervalID = setInterval(function () { + const viewRef = this; + var intervalID = setInterval(() => { if (viewRef.ready) { clearInterval(intervalID); viewRef.renderAll(); @@ -3081,10 +3046,10 @@ define([ } // After all the results are loaded, query for our facet counts in the background - //this.getAutocompletes(); + // this.getAutocompletes(); }, - renderAll: function () { + renderAll() { // do this first to indicate coming results this.updateStats(); @@ -3096,7 +3061,7 @@ define([ this.$results.removeClass("loading"); // If there are no results, display so - var numFound = this.searchResults.length; + const numFound = this.searchResults.length; if (numFound == 0) { // Add a No Results Found message this.$results.html("

    No results found.

    "); @@ -3115,12 +3080,12 @@ define([ !MetacatUI.appSearchResults.length ) { $("#no-results-found").after( - "

    Where are my data sets?

    If you are a previous ACADIS Gateway user, " + - "you will need to take additional steps to access your data sets in the new NSF Arctic Data Center." + - "Send us a message at support@arcticdata.io with your old ACADIS " + - "Gateway username and your ORCID identifier (" + - MetacatUI.appUserModel.get("username") + - "), we will help.

    ", + `

    Where are my data sets?

    If you are a previous ACADIS Gateway user, ` + + `you will need to take additional steps to access your data sets in the new NSF Arctic Data Center.` + + `Send us a message at support@arcticdata.io with your old ACADIS ` + + `Gateway username and your ORCID identifier (${MetacatUI.appUserModel.get( + "username", + )}), we will help.

    `, ); } } @@ -3130,7 +3095,7 @@ define([ // Clear the results list before we start adding new rows this.$results.html(""); - //--First map all the results-- + // --First map all the results-- if (gmaps && this.mapModel) { // Draw all the tiles on the map to represent the datasets this.drawTiles(); @@ -3139,25 +3104,25 @@ define([ $("#map-container").removeClass("loading"); } - var pid_list = new Array(); + const pid_list = new Array(); - //--- Add all the results to the list --- + // --- Add all the results to the list --- for (i = 0; i < this.searchResults.length; i++) { pid_list.push(this.searchResults.models[i].get("id")); } if (MetacatUI.appModel.get("displayDatasetMetrics")) { - var metricsModel = new MetricsModel({ - pid_list: pid_list, + const metricsModel = new MetricsModel({ + pid_list, type: "catalog", }); metricsModel.fetch(); this.metricsModel = metricsModel; } - //--- Add all the results to the list --- + // --- Add all the results to the list --- for (i = 0; i < this.searchResults.length; i++) { - var element = this.searchResults.models[i]; + const element = this.searchResults.models[i]; if (typeof element !== "undefined") this.addOne(element, this.metricsModel); } @@ -3172,8 +3137,9 @@ define([ /** * Add a single SolrResult item to the list by creating a view for it and appending its element to the DOM. + * @param result */ - addOne: function (result) { + addOne(result) { // Get the view and package service URL's this.$view_service = MetacatUI.appModel.get("viewServiceUrl"); this.$package_service = MetacatUI.appModel.get("packageServiceUrl"); @@ -3182,7 +3148,7 @@ define([ package_service: this.$package_service, }); - var view = new SearchResultView({ + const view = new SearchResultView({ model: result, metricsModel: this.metricsModel, }); @@ -3194,23 +3160,23 @@ define([ if ( gmaps && this.mapModel && - typeof result.get("geohash_9") != "undefined" && + typeof result.get("geohash_9") !== "undefined" && result.get("geohash_9") != null ) { - var title = result.get("title"); + const title = result.get("title"); - for (var i = 0; i < result.get("geohash_9").length; i++) { - var centerGeohash = result.get("geohash_9")[i], - decodedGeohash = nGeohash.decode(centerGeohash), - position = new google.maps.LatLng( - decodedGeohash.latitude, - decodedGeohash.longitude, - ), - marker = new gmaps.Marker({ - position: position, - icon: this.mapModel.get("markerImage"), - zIndex: 99999, - }); + for (let i = 0; i < result.get("geohash_9").length; i++) { + const centerGeohash = result.get("geohash_9")[i]; + const decodedGeohash = nGeohash.decode(centerGeohash); + const position = new google.maps.LatLng( + decodedGeohash.latitude, + decodedGeohash.longitude, + ); + const marker = new gmaps.Marker({ + position, + icon: this.mapModel.get("markerImage"), + zIndex: 99999, + }); } } }, @@ -3221,9 +3187,9 @@ define([ * @param {SolrResult} model * @param {XMLHttpRequest.response} response */ - showError: function (model, response) { - var errorMessage = ""; - var statusCode = response.status; + showError(model, response) { + let errorMessage = ""; + let statusCode = response.status; if (!statusCode) { statusCode = parseInt(response.statusText); @@ -3241,17 +3207,14 @@ define([ errorMessage = ""; } } finally { - if (typeof errorMessage == "string" && errorMessage.length) { - errorMessage = "

    Error details: " + errorMessage + "

    "; + if (typeof errorMessage === "string" && errorMessage.length) { + errorMessage = `

    Error details: ${errorMessage}

    `; } } } MetacatUI.appView.showAlert( - "

    " + - this.solrErrorTitle + - ".

    " + - errorMessage, + `

    ${this.solrErrorTitle}.

    ${errorMessage}`, "alert-error", this.$results, ); @@ -3264,7 +3227,7 @@ define([ * STYLING THE UI * ================================================================================================== */ - toggleMapMode: function (e) { + toggleMapMode(e) { if (typeof e === "object") { e.preventDefault(); } @@ -3289,7 +3252,7 @@ define([ }, // Communicate that the page is loading - loading: function () { + loading() { $("#map-container").addClass("loading"); this.$results.addClass("loading"); @@ -3301,13 +3264,13 @@ define([ }, // Toggles the collapseable filters sidebar and result list in the default theme - collapse: function (e) { - var id = $(e.target).attr("data-collapse"); + collapse(e) { + const id = $(e.target).attr("data-collapse"); - $("#" + id).toggleClass("collapsed"); + $(`#${id}`).toggleClass("collapsed"); }, - toggleFilterCollapse: function (e) { + toggleFilterCollapse(e) { if (typeof e !== "undefined") { var container = $(e.target).parents(".filter-contain.collapsable"); } else { @@ -3349,7 +3312,7 @@ define([ /* * Either hides or shows the "clear all filters" button */ - toggleClearButton: function () { + toggleClearButton() { if (this.searchModel.filterCount() > 0) { this.showClearButton(); } else { @@ -3358,7 +3321,7 @@ define([ }, // Move the popover element up the page a bit if it runs off the bottom of the page - preventPopoverRunoff: function (e) { + preventPopoverRunoff(e) { // In map view only (because all elements are fixed and you can't scroll) if (this.mode == "map") { var viewportHeight = $("#map-container").outerHeight(); @@ -3367,9 +3330,9 @@ define([ } if ($(".popover").length > 0) { - var offset = $(".popover").offset(); - var popoverHeight = $(".popover").outerHeight(); - var topPosition = offset.top; + const offset = $(".popover").offset(); + const popoverHeight = $(".popover").outerHeight(); + const topPosition = offset.top; // If pixels are cut off the top of the page, readjust its vertical position if (topPosition < 0) { @@ -3378,11 +3341,11 @@ define([ }); } else { // Else, let's check if it is cut off at the bottom - var totalHeight = topPosition + popoverHeight; + const totalHeight = topPosition + popoverHeight; - var pixelsHidden = totalHeight - viewportHeight; + const pixelsHidden = totalHeight - viewportHeight; - var newTopPosition = topPosition - pixelsHidden - 40; + const newTopPosition = topPosition - pixelsHidden - 40; // If pixels are cut off the bottom of the page, readjust its vertical position if (pixelsHidden > 0) { @@ -3394,7 +3357,7 @@ define([ } }, - onClose: function () { + onClose() { this.stopListening(); $(".DataCatalog").removeClass("DataCatalog"); diff --git a/src/js/views/DataPackageView.js b/src/js/views/DataPackageView.js index a7ba4c97d..613ea3499 100644 --- a/src/js/views/DataPackageView.js +++ b/src/js/views/DataPackageView.js @@ -15,7 +15,7 @@ "text!templates/dataPackage.html", "text!templates/dataPackageStart.html", "text!templates/dataPackageHeader.html", -], function ( +], ( $, _, Backbone, @@ -32,7 +32,7 @@ DataPackageTemplate, DataPackageStartTemplate, DataPackageHeaderTemplate, -) { +) => { "use strict"; /** @@ -41,7 +41,7 @@ * a file/folder browser * @classcategory Views * @screenshot views/DataPackageView.png - * @extends Backbone.View + * @augments Backbone.View */ var DataPackageView = Backbone.View.extend( /** @lends DataPackageView.prototype */ { @@ -82,11 +82,11 @@ /* Flag indicating the open or closed state of the package rows */ isOpen: true, - initialize: function (options) { + initialize(options) { if (options === undefined || !options) var options = {}; if (!options.edit) { - //The edit option will allow the user to edit the table + // The edit option will allow the user to edit the table this.edit = options.edit || false; this.mode = "view"; this.packageId = options.packageId || null; @@ -109,18 +109,18 @@ this.listenTo(this.packageModel, "changeAll", this.render); } else { - //Get the options sent to this view - if (typeof options == "object") { - //The edit option will allow the user to edit the table + // Get the options sent to this view + if (typeof options === "object") { + // The edit option will allow the user to edit the table this.edit = options.edit || false; this.mode = "edit"; - //The data package to render + // The data package to render this.dataPackage = options.dataPackage || new DataPackage(); this.parentEditorView = options.parentEditorView || null; } - //Create a new DataPackage collection if one wasn't sent + // Create a new DataPackage collection if one wasn't sent else if (!this.dataPackage) { this.dataPackage = new DataPackage(); } @@ -132,7 +132,7 @@ /** * Render the DataPackage HTML */ - render: function () { + render() { this.$el.addClass("download-contents table-condensed"); this.$el.append( this.template({ @@ -160,13 +160,13 @@ this.addAll(); if (this.edit) { - //If this is a new data package, then display a message and button + // If this is a new data package, then display a message and button if ( (this.dataPackage.length == 1 && this.dataPackage.models[0].isNew()) || !this.dataPackage.length ) { - var messageRow = this.startMessageTemplate(); + const messageRow = this.startMessageTemplate(); this.$("tbody").append(messageRow); @@ -175,7 +175,7 @@ }); } - //Render the Share control(s) + // Render the Share control(s) this.renderShareControl(); } else { // check for nessted datasets @@ -190,13 +190,14 @@ /** * Add a single DataItemView row to the DataPackageView + * @param item + * @param dataPackage */ - addOne: function (item, dataPackage) { + addOne(item, dataPackage) { if (!item) return false; - //Don't add duplicate rows - if (this.$(".data-package-item[data-id='" + item.id + "']").length) - return; + // Don't add duplicate rows + if (this.$(`.data-package-item[data-id='${item.id}']`).length) return; // Don't add data package if ( @@ -206,18 +207,21 @@ return; } - var dataItemView, scimetaParent, parentRow, delayed_models; + let dataItemView; + let scimetaParent; + let parentRow; + let delayed_models; if (_.contains(Object.keys(this.subviews), item.id)) { return false; // Don't double render } - var itemPath = null, - view = this; + let itemPath = null; + const view = this; if (!_.isEmpty(this.atLocationObj)) { itemPath = this.atLocationObj[item.get("id")]; if (itemPath[0] != "/") { - itemPath = "/" + itemPath; + itemPath = `/${itemPath}`; } } @@ -228,37 +232,37 @@ if (typeof dataPackageId === "undefined") dataPackageId = this.dataPackage.id; - var insertInfoIcon = this.edit + const insertInfoIcon = this.edit ? false : view.dataEntities.includes(item.id); dataItemView = new DataItemView({ model: item, metricsModel: this.metricsModel, - itemPath: itemPath, - insertInfoIcon: insertInfoIcon, + itemPath, + insertInfoIcon, currentlyViewing: this.currentlyViewing, mode: this.mode, parentEditorView: this.parentEditorView, - dataPackageId: dataPackageId, + dataPackageId, }); this.subviews[item.id] = dataItemView; // keep track of all views if (this.edit) { - //Get the science metadata that documents this item + // Get the science metadata that documents this item scimetaParent = item.get("isDocumentedBy"); - //If this item is not documented by a science metadata object, + // If this item is not documented by a science metadata object, // and there is only one science metadata doc in the package, then assume it is // documented by that science metadata doc - if (typeof scimetaParent == "undefined" || !scimetaParent) { - //Get the science metadata models - var metadataIds = this.dataPackage.sciMetaPids; + if (typeof scimetaParent === "undefined" || !scimetaParent) { + // Get the science metadata models + const metadataIds = this.dataPackage.sciMetaPids; - //If there is only one science metadata model in the package, then use it + // If there is only one science metadata model in the package, then use it if (metadataIds.length == 1) scimetaParent = metadataIds[0]; } - //Otherwise, get the first science metadata doc that documents this object + // Otherwise, get the first science metadata doc that documents this object else { scimetaParent = scimetaParent[0]; } @@ -278,7 +282,7 @@ } else { // Find the parent row by it's id, stored in a custom attribute if (scimetaParent) - parentRow = this.$("[data-id='" + scimetaParent + "']"); + parentRow = this.$(`[data-id='${scimetaParent}']`); if (typeof parentRow !== "undefined" && parentRow.length) { // This is a data row, insert below it's metadata parent folder @@ -287,7 +291,7 @@ // Remove it from the delayedModels list if necessary if (_.contains(Object.keys(this.delayedModels), scimetaParent)) { delayed_models = this.delayedModels[scimetaParent]; - var index = _.indexOf(delayed_models, item); + const index = _.indexOf(delayed_models, item); delayed_models = delayed_models.splice(index, 1); // Put the shortened array back if delayed models remains @@ -301,9 +305,7 @@ this.trigger("addOne"); } else { console.warn( - "Couldn't render " + - item.id + - ". Delayed until parent is rendered.", + `Couldn't render ${item.id}. Delayed until parent is rendered.`, ); // Postpone the data row until the parent is rendered delayed_models = this.delayedModels[scimetaParent]; @@ -332,8 +334,8 @@ /** * Render the Data Package View and insert it into this view */ - renderDataPackage: function () { - var view = this; + renderDataPackage() { + const view = this; if (MetacatUI.rootDataPackage.packageModel.isNew()) { view.renderMember(this.model); @@ -345,23 +347,23 @@ this.listenTo(model, "sync", view.renderMember); else if (model.get("synced")) view.renderMember(model); - //Listen for changes on this member + // Listen for changes on this member model.on("change:fileName", model.addToUploadQueue); }); - //Render the Data Package view + // Render the Data Package view this.dataPackageView = new DataPackageView({ edit: true, dataPackage: MetacatUI.rootDataPackage, parentEditorView: this, }); - //Render the view - var $packageTableContainer = this.$("#data-package-container"); + // Render the view + const $packageTableContainer = this.$("#data-package-container"); $packageTableContainer.html(this.dataPackageView.render().el); - //Make the view resizable on the bottom - var handle = $(document.createElement("div")) + // Make the view resizable on the bottom + const handle = $(document.createElement("div")) .addClass("ui-resizable-handle ui-resizable-s") .attr("title", "Drag to resize") .append( @@ -372,15 +374,15 @@ handles: { s: handle }, minHeight: 100, maxHeight: 900, - resize: function () { + resize() { view.emlView.resizeTOC(); }, }); - var tableHeight = ($(window).height() - $("#Navbar").height()) * 0.4; - $packageTableContainer.css("height", tableHeight + "px"); + const tableHeight = ($(window).height() - $("#Navbar").height()) * 0.4; + $packageTableContainer.css("height", `${tableHeight}px`); - var table = this.dataPackageView.$el; + const table = this.dataPackageView.$el; this.listenTo(this.dataPackageView, "addOne", function () { if ( table.outerHeight() > $packageTableContainer.outerHeight() && @@ -396,7 +398,7 @@ if (this.emlView) this.emlView.resizeTOC(); - //Save the view as a subview + // Save the view as a subview this.subviews.push(this.dataPackageView); this.listenTo( @@ -409,12 +411,12 @@ /** * Add all rows to the DataPackageView */ - addAll: function () { + addAll() { this.$el.find("#data-package-table-body").html(""); // clear the table first this.dataPackage.sort(); if (!this.edit) { - var atLocationObj = this.dataPackage.getAtLocation(); + const atLocationObj = this.dataPackage.getAtLocation(); this.atLocationObj = atLocationObj; // form path to D1 object dictionary @@ -430,11 +432,11 @@ } }, this); - for (let key of Object.keys(this.atLocationObj)) { - var path = this.atLocationObj[key]; - var pathArray = path.split("/"); + for (const key of Object.keys(this.atLocationObj)) { + const path = this.atLocationObj[key]; + const pathArray = path.split("/"); pathArray.pop(); - var parentPath = pathArray.join("/"); + const parentPath = pathArray.join("/"); if (filePathObj.hasOwnProperty(parentPath)) { filePathObj[parentPath].push(key); } else { @@ -445,31 +447,33 @@ } // add top level data package row to the package table - var tableRow = null, - view = this, - title = this.packageTitle, - packageUrl = null; + let tableRow = null; + const view = this; + let title = this.packageTitle; + let packageUrl = null; if (title === "") { - let metadataObj = _.filter(this.dataPackage.models, function (m) { - return m.get("id") == view.currentlyViewing; - }); + const metadataObj = _.filter( + this.dataPackage.models, + (m) => m.get("id") == view.currentlyViewing, + ); if (metadataObj.length > 0) { title = metadataObj[0].get("title"); - let metaId = metadataObj[0].get("id"); + const metaId = metadataObj[0].get("id"); this.metaId = metaId; } else { title = this.dataPackage.get("id"); } } - let titleTooltip = title; + const titleTooltip = title; title = title.length > 150 - ? title.slice(0, 75) + - "..." + - title.slice(title.length - 75, title.length) + ? `${title.slice(0, 75)}...${title.slice( + title.length - 75, + title.length, + )}` : title; // set the package URL @@ -478,13 +482,13 @@ MetacatUI.appModel.get("packageServiceUrl") + encodeURIComponent(view.dataPackage.id); - var disablePackageDownloads = this.disablePackageDownloads; + const { disablePackageDownloads } = this; tableRow = this.dataPackageHeaderTemplate({ id: view.dataPackage.id, - title: title, - titleTooltip: titleTooltip, + title, + titleTooltip, downloadUrl: packageUrl, - disablePackageDownloads: disablePackageDownloads, + disablePackageDownloads, disablePackageUrl: true, }); @@ -492,7 +496,7 @@ if (this.atLocationObj !== undefined && filePathObj !== undefined) { // sort the filePath by length - var sortedFilePathObj = Object.keys(filePathObj) + const sortedFilePathObj = Object.keys(filePathObj) .sort() .reduce((obj, key) => { obj[key] = filePathObj[key]; @@ -511,23 +515,25 @@ /** * Add all the files and folders + * @param sortedFilePathObj */ - addFilesAndFolders: function (sortedFilePathObj) { + addFilesAndFolders(sortedFilePathObj) { if (!sortedFilePathObj) return false; - var insertedPath = new Array(); - let pathMap = new Object(); + const insertedPath = new Array(); + const pathMap = new Object(); pathMap[""] = ""; - for (let key of Object.keys(sortedFilePathObj)) { + for (const key of Object.keys(sortedFilePathObj)) { // add folder - var pathArray = key.split("/"); - //skip the first empty value + const pathArray = key.split("/"); + // skip the first empty value for (let i = 0; i < pathArray.length; i++) { if (pathArray[i].length < 1) continue; if (!(pathArray[i] in pathMap)) { // insert path - var dataItemView, itemPath; + var dataItemView; + var itemPath; // root if (i == 0) { @@ -539,7 +545,7 @@ dataItemView = new DataItemView({ mode: this.mode, itemName: pathArray[i], - itemPath: itemPath, + itemPath, itemType: "folder", parentEditorView: this.parentEditorView, dataPackageId: this.dataPackage.id, @@ -551,12 +557,12 @@ this.trigger("addOne"); - pathMap[pathArray[i]] = itemPath + "/" + pathArray[i]; + pathMap[pathArray[i]] = `${itemPath}/${pathArray[i]}`; } } // add files in the folder - var itemArray = sortedFilePathObj[key]; + const itemArray = sortedFilePathObj[key]; // Add metadata object at the top of the file table if ( @@ -564,12 +570,12 @@ this.metaId !== "undefined" && itemArray.includes(this.metaId) ) { - let item = this.metaId; + const item = this.metaId; this.addOne(this.dataPackage.get(item)); } for (let i = 0; i < itemArray.length; i++) { - let item = itemArray[i]; + const item = itemArray[i]; this.addOne(this.dataPackage.get(item)); } } @@ -577,10 +583,9 @@ /** Remove the subview represented by the given model item. - @param item The model representing the sub view to be removed - */ - removeOne: function (item) { + */ + removeOne(item) { if (_.contains(Object.keys(this.subviews), item.id)) { // Remove the view and the its reference in the subviews list this.subviews[item.id].remove(); @@ -588,8 +593,8 @@ } }, - handleAddFiles: function (e) { - //Pass this on to the DataItemView for the root data package + handleAddFiles(e) { + // Pass this on to the DataItemView for the root data package this.$(".data-package-item.folder") .first() .data("view") @@ -600,7 +605,7 @@ * Renders a control that opens the AccessPolicyView for editing permissions on this package * @since 2.15.0 */ - renderShareControl: function () { + renderShareControl() { if ( this.parentEditorView && !this.parentEditorView.isAccessPolicyEditEnabled() @@ -612,31 +617,32 @@ /** * Close subviews as needed */ - onClose: function () { + onClose() { // Close each subview _.each( Object.keys(this.subviews), function (id) { - var subview = this.subviews[id]; + const subview = this.subviews[id]; subview.onClose(); }, this, ); - //Reset the subviews from the view completely (by removing it from the prototype) + // Reset the subviews from the view completely (by removing it from the prototype) this.__proto__.subviews = {}; }, /** Show or hide the data rows associated with the event row science metadata - */ - toggleRows: function (event) { + * @param event + */ + toggleRows(event) { if (this.isOpen) { // Get the DataItemView associated with each id _.each( Object.keys(this.subviews), function (id) { - var subview = this.subviews[id]; + const subview = this.subviews[id]; if (subview.model.get("type") === "Data" && subview.remove) { // Remove the view from the DOM @@ -664,7 +670,7 @@ this.isOpen = false; } else { // Add sub rows to the view - var dataModels = this.dataPackage.where({ type: "Data" }); + const dataModels = this.dataPackage.where({ type: "Data" }); _.each( dataModels, function (model) { @@ -698,23 +704,23 @@ * @param {Event} e - The event object. * @since 2.28.0 */ - expand: function (e) { + expand(e) { // Don't do anything... e.preventDefault(); - var view = this; - var eventEl = $(e.target).parents("td"); - var rowEl = $(e.target).parents("tr"); + const view = this; + const eventEl = $(e.target).parents("td"); + const rowEl = $(e.target).parents("tr"); - var parentId = rowEl.data("id"); - var children = "tr[data-parent='" + parentId + "']"; + const parentId = rowEl.data("id"); + const children = `tr[data-parent='${parentId}']`; this.$(children).fadeIn(); this.$(eventEl) .children() .children(".expand-control") - .fadeOut(function () { + .fadeOut(() => { view .$(eventEl) .children() @@ -727,7 +733,7 @@ .children() .children() .children(".collapse-control") - .fadeOut(function () { + .fadeOut(() => { view .$(children) .children() @@ -740,25 +746,24 @@ /** * Collapse function to hide rows when a user clicks on a collapse control. * @param {Event} e - The event object. - * * @since 2.28.0 */ - collapse: function (e) { + collapse(e) { // Don't do anything... e.preventDefault(); - var view = this; - var eventEl = $(e.target).parents("td"); - var rowEl = $(e.target).parents("tr"); + const view = this; + const eventEl = $(e.target).parents("td"); + const rowEl = $(e.target).parents("tr"); - var parentId = rowEl.data("id"); - var children = "tr[data-parent^='" + parentId + "']"; + const parentId = rowEl.data("id"); + const children = `tr[data-parent^='${parentId}']`; this.$(children).fadeOut(); this.$(eventEl) .children() .children(".collapse-control") - .fadeOut(function () { + .fadeOut(() => { view.$(eventEl).children().children(".expand-control").fadeIn(); view.$(".tooltip-this").tooltip(); }); @@ -767,25 +772,24 @@ /** * Expand all function to show all child rows when a user clicks on an expand-all control. * @param {Event} e - The event object. - * * @since 2.28.0 */ - expandAll: function (e) { + expandAll(e) { // Don't do anything... e.preventDefault(); - var view = this; - var eventEl = $(e.target).parents("td"); - var rowEl = $(e.target).parents("tr"); + const view = this; + const eventEl = $(e.target).parents("td"); + const rowEl = $(e.target).parents("tr"); - var parentId = rowEl.data("id"); - var children = "tr[data-packageid='" + parentId + "']"; + const parentId = rowEl.data("id"); + const children = `tr[data-packageid='${parentId}']`; this.$(children).fadeIn(); this.$(eventEl) .children(".d1package-expand") - .fadeOut(function () { + .fadeOut(() => { view.$(eventEl).children(".d1package-collapse").fadeIn("fast"); view.$(".tooltip-this").tooltip(); }); @@ -794,7 +798,7 @@ .children() .children() .children(".collapse-control") - .fadeOut(function () { + .fadeOut(() => { view .$(children) .children() @@ -807,31 +811,30 @@ /** * Collapse all function to hide all child rows when a user clicks on a collapse-all control. * @param {Event} e - The event object. - * * @since 2.28.0 */ - collapseAll: function (e) { + collapseAll(e) { // Don't do anything... e.preventDefault(); - var view = this; - var eventEl = $(e.target).parents("td"); - var rowEl = $(e.target).parents("tr"); + const view = this; + const eventEl = $(e.target).parents("td"); + const rowEl = $(e.target).parents("tr"); - var parentId = rowEl.data("id"); - var children = "tr[data-packageid='" + parentId + "']"; + const parentId = rowEl.data("id"); + const children = `tr[data-packageid='${parentId}']`; this.$(children).each(function () { $(this).fadeOut(); - let childId = $(this).data("id"); - let grandchildren = "tr[data-parent^='" + childId + "']"; + const childId = $(this).data("id"); + const grandchildren = `tr[data-parent^='${childId}']`; $(grandchildren).fadeOut(); }); this.$(eventEl) .children(".d1package-collapse") - .fadeOut(function () { + .fadeOut(() => { view.$(eventEl).children(".d1package-expand").fadeIn(); view.$(".tooltip-this").tooltip(); }); @@ -839,27 +842,26 @@ /** * Check for private members and disable download buttons if necessary. - * * @since 2.28.0 */ - checkForPrivateMembers: function () { + checkForPrivateMembers() { try { - var packageModel = this.model, - packageCollection = this.dataPackage; + const packageModel = this.model; + const packageCollection = this.dataPackage; if (!packageModel || !packageCollection) { return; } - var numMembersFromSolr = packageModel.get("members").length, - numMembersFromRDF = packageCollection.length; + const numMembersFromSolr = packageModel.get("members").length; + const numMembersFromRDF = packageCollection.length; if (numMembersFromRDF > numMembersFromSolr) { - var downloadButtons = this.$(".btn.download"); + const downloadButtons = this.$(".btn.download"); - for (var i = 0; i < downloadButtons.length; i++) { - var btn = downloadButtons[i]; - var downloadURL = $(btn).attr("href"); + for (let i = 0; i < downloadButtons.length; i++) { + const btn = downloadButtons[i]; + const downloadURL = $(btn).attr("href"); if ( downloadURL.indexOf(packageModel.get("id")) > -1 || @@ -890,24 +892,23 @@ /** * Retrieves and processes nested packages for the current package. - * * @since 2.28.0 */ - getNestedPackages: function () { - var nestedPackages = new Array(); - var nestedPackageIds = new Array(); + getNestedPackages() { + const nestedPackages = new Array(); + const nestedPackageIds = new Array(); this.nestedPackages = nestedPackages; // get all the child packages for this resource map - var childPackages = this.dataPackage.filter(function (m) { - return m.get("formatType") === "RESOURCE"; - }); + const childPackages = this.dataPackage.filter( + (m) => m.get("formatType") === "RESOURCE", + ); // iterate over the list of child packages and add their members - for (var ite in childPackages) { - var childPkg = childPackages[ite]; + for (const ite in childPackages) { + const childPkg = childPackages[ite]; if (!nestedPackageIds.includes(childPkg.get("id"))) { - var nestedPackage = new PackageModel(); + const nestedPackage = new PackageModel(); nestedPackage.set("id", childPkg.get("id")); nestedPackage.setURL(); nestedPackage.getMembers(); @@ -926,65 +927,63 @@ /** * Adds a nested data package to the package table. - * - * @param {Object} dataPackage - The data package to be added. + * @param {object} dataPackage - The data package to be added. * @since 2.28.0 */ - addNestedPackages: function (dataPackage) { + addNestedPackages(dataPackage) { /** * Generates the table row for the data package header. * @type {null|Element} */ - var tableRow = null, - /** - * Reference to the current view. - * @type {Object} - */ - view = this, - /** - * The title of the data package. - * @type {null|string} - */ - title = null, - /** - * The URL of the data package. - * @type {null|string} - */ - packageUrl = null, - /** - * The URL of the nested data package. - * @type {null|string} - */ - nestedPackageUrl = null; + let tableRow = null; + /** + * Reference to the current view. + * @type {object} + */ + var view = this; + /** + * The title of the data package. + * @type {null|string} + */ + let title = null; + /** + * The URL of the data package. + * @type {null|string} + */ + let packageUrl = null; + /** + * The URL of the nested data package. + * @type {null|string} + */ + let nestedPackageUrl = null; /** * The members of the data package. - * * @type {Array} */ - var members = dataPackage.get("members"); + let members = dataPackage.get("members"); /** * Filters out metadata objects from the members. - * * @type {Array} */ - let metadataObj = _.filter(members, function (m) { - return m.get("type") == "Metadata" || m.get("type") == "metadata"; - }); + const metadataObj = _.filter( + members, + (m) => m.get("type") == "Metadata" || m.get("type") == "metadata", + ); title = metadataObj[0].get("title"); /** * The tooltip for the title (used for long titles). - * * @type {string} */ - let titleTooltip = title; + const titleTooltip = title; title = title.length > 150 - ? title.slice(0, 75) + - "..." + - title.slice(title.length - 75, title.length) + ? `${title.slice(0, 75)}...${title.slice( + title.length - 75, + title.length, + )}` : title; // Set the package URL @@ -995,18 +994,16 @@ // Set the nested package URL if (MetacatUI.root !== undefined && dataPackage.id !== undefined) - nestedPackageUrl = - MetacatUI.root + "/view/" + encodeURIComponent(dataPackage.id); + nestedPackageUrl = `${MetacatUI.root}/view/${encodeURIComponent(dataPackage.id)}`; /** * The HTML content for the data package header. - * * @type {string} */ tableRow = this.dataPackageHeaderTemplate({ id: dataPackage.id, - title: title, - titleTooltip: titleTooltip, + title, + titleTooltip, disablePackageDownloads: false, downloadUrl: packageUrl, disablePackageUrl: false, @@ -1026,17 +1023,15 @@ // Add the downloadButtonView el to the span this.$el - .find(".downloadAction[data-id='" + dataPackage.id + "']") + .find(`.downloadAction[data-id='${dataPackage.id}']`) .html(this.downloadButtonView.el); // Filter out the packages from the member list - members = _.filter(members, function (m) { - return m.type != "Package"; - }); + members = _.filter(members, (m) => m.type != "Package"); // Add each member to the package table view var view = this; - _.each(members, function (m) { + _.each(members, (m) => { // Update the size to bytes format m.set({ size: Utilities.bytesToSize(m.get("size")) }); @@ -1045,7 +1040,7 @@ }); }, - /*showDownloadProgress: function(e){ + /* showDownloadProgress: function(e){ e.preventDefault(); var button = $(e.target); @@ -1054,7 +1049,7 @@ return true; - }*/ + } */ }, ); return DataPackageView; diff --git a/src/js/views/DownloadButtonView.js b/src/js/views/DownloadButtonView.js index f35d06bcc..e97cef402 100644 --- a/src/js/views/DownloadButtonView.js +++ b/src/js/views/DownloadButtonView.js @@ -5,15 +5,15 @@ define([ "models/SolrResult", "models/DataONEObject", "models/PackageModel", -], function ($, _, Backbone, SolrResult, DataONEObject, PackageModel) { +], ($, _, Backbone, SolrResult, DataONEObject, PackageModel) => { "use strict"; - var DownloadButtonView = Backbone.View.extend({ + const DownloadButtonView = Backbone.View.extend({ tagName: "a", className: "btn download", - initialize: function (options) { + initialize(options) { if (!options) var options = {}; this.view = options.view || null; this.id = options.id || null; @@ -25,14 +25,14 @@ define([ click: "download", }, - render: function () { - var fileName = this.model.get("fileName") || ""; + render() { + let fileName = this.model.get("fileName") || ""; - if (typeof fileName == "string") { + if (typeof fileName === "string") { fileName = fileName.trim(); } - //Add the href and id attributes + // Add the href and id attributes let hrefLink = this.model.get("url"); if ( this.model instanceof DataONEObject && @@ -54,7 +54,7 @@ define([ .attr("data-id", this.model.get("id")) .attr("download", fileName); - //Check for CORS downloads. For CORS, the 'download' attribute may not work, + // Check for CORS downloads. For CORS, the 'download' attribute may not work, // so open in a new tab. if ( typeof hrefLink !== "undefined" && @@ -63,7 +63,7 @@ define([ this.$el.attr("target", "_blank"); } - //For packages + // For packages if (typeof this.view !== "undefined" && this.view == "actionsView") { this.$el.append( $(document.createElement("i")).addClass( @@ -77,7 +77,7 @@ define([ if (this.model.type == "Package") { this.$el.text("Download All").addClass("btn-primary"); - //if the Package Model has no Solr index document associated with it, then we + // if the Package Model has no Solr index document associated with it, then we // can assume the resource map object is private. So disable the download button. if (!this.model.get("indexDoc")) { this.$el @@ -93,18 +93,18 @@ define([ }); } } - //For individual DataONEObjects + // For individual DataONEObjects else { this.$el.text("Download"); } - //Add a download icon + // Add a download icon this.$el.append( $(document.createElement("i")).addClass("icon icon-cloud-download"), ); } - //If this is a Download All button for a package but it's too large, then disable the button with a message + // If this is a Download All button for a package but it's too large, then disable the button with a message if ( this.model.type == "Package" && this.model.getTotalSize() > MetacatUI.appModel.get("maxDownloadSize") @@ -128,12 +128,11 @@ define([ } }, - download: function (e) { + download(e) { // Checking if the Download All button is disabled because the package is too large - var isDownloadDisabled = + const isDownloadDisabled = !!( this.$el.attr("disabled") === "disabled" || this.$el.is(".disabled") - ? true - : false; + ); // Do nothing if the `disabled` attribute is set!. // If the download is already in progress, don't try to download again @@ -142,41 +141,38 @@ define([ return; } - //If the user isn't logged in, let the browser handle the download normally + // If the user isn't logged in, let the browser handle the download normally if ( MetacatUI.appUserModel.get("tokenChecked") && !MetacatUI.appUserModel.get("loggedIn") ) { return; } - //If the authentication hasn't been checked yet, wait for it - else if (!MetacatUI.appUserModel.get("tokenChecked")) { - var view = this; - this.listenTo( - MetacatUI.appUserModel, - "change:tokenChecked", - function () { - view.download(e); - }, - ); + // If the authentication hasn't been checked yet, wait for it + if (!MetacatUI.appUserModel.get("tokenChecked")) { + const view = this; + this.listenTo(MetacatUI.appUserModel, "change:tokenChecked", () => { + view.download(e); + }); return; } - //If the user is logged in but the object is public, download normally - else if (this.model.get("isPublic")) { - //If this is a "Download All" button for a package, and at least object is private, then + // If the user is logged in but the object is public, download normally + if (this.model.get("isPublic")) { + // If this is a "Download All" button for a package, and at least object is private, then // we need to download via XHR with credentials if (this.model.type == "Package") { - //If we found a private object, download the package via XHR so we can send the auth token. - var privateObject = _.find(this.model.get("members"), function (m) { - return m.get("isPublic") !== true; - }); - //If no private object is found, download normally. + // If we found a private object, download the package via XHR so we can send the auth token. + const privateObject = _.find( + this.model.get("members"), + (m) => m.get("isPublic") !== true, + ); + // If no private object is found, download normally. // This may still fail when there is a private object that the logged-in user doesn't have access to. if (!privateObject) { return; } } - //All other object types (data and metadata objects) can be downloaded normally + // All other object types (data and metadata objects) can be downloaded normally else { return; } @@ -184,9 +180,9 @@ define([ e.preventDefault(); - //Show that the download has started + // Show that the download has started this.$el.addClass("in-progress"); - var buttonHTML = this.$el.html(); + const buttonHTML = this.$el.html(); if (typeof this.view !== "undefined" && this.view == "actionsView") { this.$el.html( "", @@ -197,9 +193,9 @@ define([ ); } - var thisRef = this; + const thisRef = this; - this.listenToOnce(this.model, "downloadComplete", function () { + this.listenToOnce(this.model, "downloadComplete", () => { let iconEl = ""; let downloadEl = ""; @@ -208,27 +204,27 @@ define([ downloadEl = buttonHTML; } - //Show that the download is complete + // Show that the download is complete thisRef.$el .html(iconEl) .addClass("complete") .removeClass("in-progress error"); - //Put the download button back to normal - setTimeout(function () { - //After one second, change the background color with an animation + // Put the download button back to normal + setTimeout(() => { + // After one second, change the background color with an animation thisRef.$el.removeClass("complete").html(downloadEl); }, 2000); }); - this.listenToOnce(this.model, "downloadError", function () { + this.listenToOnce(this.model, "downloadError", () => { let iconEl = ""; if (thisRef.view != "actionsView") { iconEl += "Error "; } - //Show that the download failed to compelete. + // Show that the download failed to compelete. thisRef.$el .html(iconEl) .addClass("error") @@ -241,7 +237,7 @@ define([ }); }); - //Fire the download event via the SolrResult model + // Fire the download event via the SolrResult model this.model.downloadWithCredentials(); }, }); diff --git a/src/js/views/MetadataIndexView.js b/src/js/views/MetadataIndexView.js index b9192035b..3ee33ea9d 100644 --- a/src/js/views/MetadataIndexView.js +++ b/src/js/views/MetadataIndexView.js @@ -10,7 +10,7 @@ define([ "text!templates/alert.html", "text!templates/attribute.html", "text!templates/dataDisplay.html", -], function ( +], ( $, _, Backbone, @@ -22,8 +22,8 @@ define([ alertTemplate, AttributeTemplate, DataDisplayTemplate, -) { - var MetadataIndexView = Backbone.View.extend({ +) => { + const MetadataIndexView = Backbone.View.extend({ type: "MetadataIndex", id: "Metadata", @@ -46,9 +46,9 @@ define([ events: {}, - initialize: function (options) { + initialize(options) { this.pid = options.pid || null; - //this.el.id = this.id + "-" + this.pid; //Give this element a specific ID in case multiple MetadataIndex views are on one page + // this.el.id = this.id + "-" + this.pid; //Give this element a specific ID in case multiple MetadataIndex views are on one page this.parentView = options.parentView || null; // use these to tailor the annotation ui widget @@ -63,30 +63,29 @@ define([ }; }, - render: function () { + render() { if (!this.pid) return false; - var view = this; + const view = this; - //Get all the fields from the Solr index - var query = - 'q=(id:"' + - encodeURIComponent(this.pid) + - '"+OR+seriesId:"' + - encodeURIComponent(this.pid) + - '")&rows=1&start=0&fl=*&wt=json'; + // Get all the fields from the Solr index + const query = `q=(id:"${encodeURIComponent( + this.pid, + )}"+OR+seriesId:"${encodeURIComponent( + this.pid, + )}")&rows=1&start=0&fl=*&wt=json`; var requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (data, textStatus, xhr) { + success(data, textStatus, xhr) { try { if (!data?.response?.numFound) { if (view.parentView && view.parentView.model) { - //Show a "not indexed" message if there is system metadata but nothing in + // Show a "not indexed" message if there is system metadata but nothing in // the index if (view.parentView.model.get("systemMetadata")) { view.showNotIndexed(); } - //Show a "not found" message if there is no system metadata and no results in the index + // Show a "not found" message if there is no system metadata and no results in the index else { view.parentView.model.set("notFound", true); view.parentView.showNotFound(); @@ -97,8 +96,8 @@ define([ } else { view.docs = data.response.docs; - _.each(data.response.docs, function (doc, i, list) { - //If this is a data object and there is a science metadata doc that describes it, then navigate to that Metadata View. + _.each(data.response.docs, (doc, i, list) => { + // If this is a data object and there is a science metadata doc that describes it, then navigate to that Metadata View. if ( doc.formatType == "DATA" && doc.isDocumentedBy && @@ -106,26 +105,26 @@ define([ ) { view.onClose(); MetacatUI.uiRouter.navigate( - "view/" + doc.isDocumentedBy[0], + `view/${doc.isDocumentedBy[0]}`, true, ); return; } - var metadataEl = $(document.createElement("section")).attr( - "id", - "metadata-index-details", - ), - id = doc.id, - creator = doc.origin, - title = doc.title, - pubDate = doc.pubDate, - dateUploaded = doc.dateUploaded, - keys = Object.keys(doc), - docModel = new SolrResult(doc); - - //Extract General Info details that we want to list first - var generalInfoKeys = [ + const metadataEl = $(document.createElement("section")).attr( + "id", + "metadata-index-details", + ); + const { id } = doc; + const creator = doc.origin; + const { title } = doc; + const { pubDate } = doc; + const { dateUploaded } = doc; + let keys = Object.keys(doc); + const docModel = new SolrResult(doc); + + // Extract General Info details that we want to list first + const generalInfoKeys = [ "title", "id", "abstract", @@ -141,8 +140,8 @@ define([ ), ); - //Extract Spatial details - var spatialKeys = [ + // Extract Spatial details + const spatialKeys = [ "site", "southBoundCoord", "northBoundCoord", @@ -158,8 +157,8 @@ define([ ), ); - //Extract Temporal Coverage details - var temporalKeys = ["beginDate", "endDate"]; + // Extract Temporal Coverage details + const temporalKeys = ["beginDate", "endDate"]; keys = _.difference(keys, temporalKeys); $(metadataEl).append( view.formatAttributeSection( @@ -169,8 +168,8 @@ define([ ), ); - //Extract Taxonomic Coverage details - var taxonKeys = [ + // Extract Taxonomic Coverage details + const taxonKeys = [ "order", "phylum", "family", @@ -187,8 +186,8 @@ define([ ), ); - //Extract People details - var peopleKeys = [ + // Extract People details + const peopleKeys = [ "origin", "investigator", "contactOrganization", @@ -203,8 +202,8 @@ define([ ), ); - //Extract Access Control details - var accessKeys = [ + // Extract Access Control details + const accessKeys = [ "isPublic", "submitter", "rightsHolder", @@ -222,7 +221,7 @@ define([ ), ); - //Add the rest of the metadata + // Add the rest of the metadata $(metadataEl).append( view.formatAttributeSection(docModel, keys, "Other"), ); @@ -233,16 +232,14 @@ define([ }); } } catch (e) { - console.log("Error parsing Solr response: " + e); - console.log("Solr response: " + data); + console.log(`Error parsing Solr response: ${e}`); + console.log(`Solr response: ${data}`); view.parentView.showNotFound(); } }, - error: function () { - var msg = "

    Sorry, no dataset was found.

    "; - view.$el.html( - view.alertTemplate({ msg: msg, classes: "alert-danger" }), - ); + error() { + const msg = "

    Sorry, no dataset was found.

    "; + view.$el.html(view.alertTemplate({ msg, classes: "alert-danger" })); }, }; @@ -250,65 +247,64 @@ define([ _.extend(requestSettings, MetacatUI.appUserModel.createAjaxSettings()), ); - //Send a request for the EML doc itself to extract certain info + // Send a request for the EML doc itself to extract certain info if (this.parentView && this.parentView.model) { - var formatId = this.parentView.model.get("formatId"); + const formatId = this.parentView.model.get("formatId"); if (formatId.indexOf("eml://") >= 0) { - var url = + const url = `${ MetacatUI.appModel.get("baseUrl") + MetacatUI.appModel.get("context") + - MetacatUI.appModel.get("d1Service") + - "/object/" + - encodeURIComponent(this.parentView.model.get("id")); + MetacatUI.appModel.get("d1Service") + }/object/${encodeURIComponent(this.parentView.model.get("id"))}`; var requestSettings = { - url: url, - success: function (data, textStatus, xhr) { + url, + success(data, textStatus, xhr) { if (!data || !$(data).length) return; - //Find the distribution information - var emlDoc = $(data) + // Find the distribution information + const emlDoc = $(data) .find("distribution") - .each(function (i, dist) { - var onlineDist = $(dist).children("online"); + .each((i, dist) => { + const onlineDist = $(dist).children("online"); if (onlineDist.length) { var linkText = $(onlineDist).text(); if (linkText.indexOf("ecogrid") >= 0) { - //Clean up the link text - var start = linkText.lastIndexOf("/"); - var ecogridPid = linkText.substr(start + 1).trim(), - dataObjects = []; + // Clean up the link text + const start = linkText.lastIndexOf("/"); + const ecogridPid = linkText.substr(start + 1).trim(); + let dataObjects = []; - //Iterate over each id in the package and try to fuzzily match the ecogrid link to the id + // Iterate over each id in the package and try to fuzzily match the ecogrid link to the id if (view.parentView.packageModels) { - //Get all the data objects in this metadata's packages - _.each(view.parentView.packageModels, function (pckg) { + // Get all the data objects in this metadata's packages + _.each(view.parentView.packageModels, (pckg) => { dataObjects.push(pckg.get("members")); }); dataObjects = _.flatten(dataObjects); } for (var i = 0; i < dataObjects.length; i++) { - //If we find a match, replace the ecogrid links with a DataONE API link to the object + // If we find a match, replace the ecogrid links with a DataONE API link to the object if (dataObjects[i].get("id").indexOf(ecogridPid) > -1) { var linkText = dataObjects[i].get("url"); - //We can stop looking now + // We can stop looking now i = dataObjects.length; } } } - var link = $(document.createElement("a")) - .attr("href", linkText) - .text(linkText), - fullHTML = view.formatAttribute( - "Online Distribution Info", - link, - ); + const link = $(document.createElement("a")) + .attr("href", linkText) + .text(linkText); + const fullHTML = view.formatAttribute( + "Online Distribution Info", + link, + ); - //Find the "General" section of this page + // Find the "General" section of this page if (view.$(".General").length) view.$(".General").after(fullHTML); else view.$el.append(fullHTML); @@ -329,7 +325,7 @@ define([ return this; }, - formatAttributeSection: function (doc, keys, title, className) { + formatAttributeSection(doc, keys, title, className) { if (keys.length == 0) return ""; if (typeof title === "string") { @@ -343,15 +339,15 @@ define([ var titleText = titleHTML.text(); } - var html = "", - sectionClass = - typeof className === "undefined" - ? titleText.replace(/ /g, "") - : className, - view = this, - populated = false; + let html = ""; + const sectionClass = + typeof className === "undefined" + ? titleText.replace(/ /g, "") + : className; + const view = this; + let populated = false; - _.each(keys, function (key, keyNum, list) { + _.each(keys, (key, keyNum, list) => { if (typeof key === "object" && doc.get(key.field)) { html += view.formatAttribute(key.display, doc.get(key.field)); populated = true; @@ -362,37 +358,38 @@ define([ }); if (populated) { - var section = $(document.createElement("section")) + const section = $(document.createElement("section")) .addClass(sectionClass) .append(titleHTML) .append(html); return section; - } else return null; + } + return null; }, - formatAttribute: function (attribute, value) { - var html = "", - view = this, - embeddedAttributes = "", - type = "sem_annotation"; + formatAttribute(attribute, value) { + let html = ""; + const view = this; + let embeddedAttributes = ""; + let type = "sem_annotation"; // see if there is special handling for this field if (this.semanticFields[attribute]) { type = this.semanticFields[attribute]; } - //If this is a multi-valued field from Solr, the attribute value is actually multiple embedded attribute templates - var numAttributes = + // If this is a multi-valued field from Solr, the attribute value is actually multiple embedded attribute templates + const numAttributes = Array.isArray(value) && value.length > 1 ? value.length : 0; - for (var i = 0; i < numAttributes; i++) { + for (let i = 0; i < numAttributes; i++) { embeddedAttributes += view.attributeTemplate({ attribute: "", formattedAttribute: view.transformCamelCase(attribute), value: value[i].toString(), - id: attribute + "_" + (i + 1), - type: type, - resource: "#xpointer(//" + attribute + "[" + (i + 1) + "])", + id: `${attribute}_${i + 1}`, + type, + resource: `#xpointer(//${attribute}[${i + 1}])`, }); } @@ -401,39 +398,39 @@ define([ } html += view.attributeTemplate({ - attribute: attribute, + attribute, formattedAttribute: view.transformCamelCase(attribute), value: embeddedAttributes || value.toString(), id: attribute, - type: type, - resource: "#xpointer(//" + attribute + ")", + type, + resource: `#xpointer(//${attribute})`, }); return html; }, - transformCamelCase: function (string) { - var result = string + transformCamelCase(string) { + const result = string .replace(/([A-Z]+)/g, " $1") .replace(/([A-Z][a-z])/g, " $1"); - var finalResult = result.charAt(0).toUpperCase() + result.slice(1); + const finalResult = result.charAt(0).toUpperCase() + result.slice(1); return finalResult; }, - insertDataDetails: function () { - var view = this; + insertDataDetails() { + const view = this; - //Get the Package Model - it is attached with the parent Metadata View - var pkg = this.parentView.packageModel; + // Get the Package Model - it is attached with the parent Metadata View + const pkg = this.parentView.packageModel; if (!pkg) return; if (pkg.get("members").length <= 1) return; - //Start some html - var html = $(document.createElement("section")); + // Start some html + const html = $(document.createElement("section")); - _.each(pkg.get("members"), function (solrResult, i) { + _.each(pkg.get("members"), (solrResult, i) => { if (solrResult.get("formatType") != "DATA") return; solrResult.set( @@ -441,8 +438,8 @@ define([ Utilities.bytesToSize(solrResult.get("size")), ); - //Add a section for the data details, just like the other attribute sections - var keys = [ + // Add a section for the data details, just like the other attribute sections + const keys = [ "id", { field: "formattedSize", display: "size" }, "views", @@ -451,42 +448,42 @@ define([ "formatId", ]; - //Determine the icon type based on format id - var type = solrResult.getType(), - icon = ""; + // Determine the icon type based on format id + const type = solrResult.getType(); + var icon = ""; if (type == "program") icon = "icon-code"; else if (type == "metadata") icon = "icon-file-text"; else if (type == "image") icon = "icon-picture"; else if (type == "pdf") icon = "icon-file pdf"; else icon = "icon-table"; - var icon = $(document.createElement("i")).addClass(icon), - title = $(document.createElement("span")) - .text(solrResult.get("id")) - .addClass("title"), - downloadBtn = new DownloadButtonView({ model: solrResult }), - anchor = $(document.createElement("a")).attr( - "name", - encodeURIComponent(solrResult.get("id")), - ), - header = $(document.createElement("h4")) - .append(anchor) - .append(icon) - .append(title) - .append(downloadBtn.render().el); - - //Create the section - var entityDetailsSection = view + var icon = $(document.createElement("i")).addClass(icon); + const title = $(document.createElement("span")) + .text(solrResult.get("id")) + .addClass("title"); + const downloadBtn = new DownloadButtonView({ model: solrResult }); + const anchor = $(document.createElement("a")).attr( + "name", + encodeURIComponent(solrResult.get("id")), + ); + const header = $(document.createElement("h4")) + .append(anchor) + .append(icon) + .append(title) + .append(downloadBtn.render().el); + + // Create the section + const entityDetailsSection = view .formatAttributeSection(solrResult, keys, header, "entitydetails") .attr("data-id", solrResult.get("id")); - //Create an image thumbnail, if this is an image + // Create an image thumbnail, if this is an image if (type == "image") { - //var thumbnail = view.parentView.createThumbnail(solrResult.get("id")); - //$(entityDetailsSection).prepend(thumbnail); + // var thumbnail = view.parentView.createThumbnail(solrResult.get("id")); + // $(entityDetailsSection).prepend(thumbnail); } - //Mark this section with an anchor tag with the doc id + // Mark this section with an anchor tag with the doc id $(entityDetailsSection).prepend( $(document.createElement("a")).attr( "id", @@ -497,19 +494,19 @@ define([ $(html).append(entityDetailsSection); }); - //Glue together the header and attribute info section - var header = $(document.createElement("h4")).text( + // Glue together the header and attribute info section + const header = $(document.createElement("h4")).text( "Data Table, Image, and Other Data Details", ); - var section = $(html).prepend(header); + const section = $(html).prepend(header); - //Insert into the DOM right after the "general" information + // Insert into the DOM right after the "general" information this.$(".General").after(section); }, - //Shows a message to the user that indicates this object has not been indexed - showNotIndexed: function () { - var message = this.alertTemplate({ + // Shows a message to the user that indicates this object has not been indexed + showNotIndexed() { + const message = this.alertTemplate({ classes: "alert-warning", msg: "

    There is limited information about this content.

    " + @@ -520,44 +517,44 @@ define([ }); this.$el.append(message); - //If this metadata doc is not indexed, we need to search the system metadata - //to see if it is publicly accessible. + // If this metadata doc is not indexed, we need to search the system metadata + // to see if it is publicly accessible. if (this.parentView && this.parentView.model) { - //Get the system metadata string - var sysMeta = this.parentView.model.get("systemMetadata"); + // Get the system metadata string + let sysMeta = this.parentView.model.get("systemMetadata"); if (sysMeta) { - //Parse it into XML nodes + // Parse it into XML nodes sysMeta = $.parseXML(sysMeta); - //Find the allow permission for the public - var publicPermission = $(sysMeta).find( + // Find the allow permission for the public + const publicPermission = $(sysMeta).find( "allow subject:contains('public')", ); if (publicPermission.length) { - //Remove the "private" icon + // Remove the "private" icon $("#metadata-controls-container .private").remove(); } } - //If there is no system metadata, default to hiding the private icon + // If there is no system metadata, default to hiding the private icon else { $("#metadata-controls-container .private").remove(); } } }, - flagComplete: function () { + flagComplete() { this.complete = true; this.trigger("complete"); }, - onClose: function () { + onClose() { this.$el.html(this.loadingTemplate()); this.pid = null; - //Detach this view from its parent view + // Detach this view from its parent view this.parentView.subviews = _.without(this.parentView.subviews, this); this.parentView = null; - //Remove listeners + // Remove listeners this.stopListening(); }, }); diff --git a/src/js/views/MetadataView.js b/src/js/views/MetadataView.js index 793f43d1b..341117d6a 100644 --- a/src/js/views/MetadataView.js +++ b/src/js/views/MetadataView.js @@ -38,7 +38,7 @@ define([ "text!templates/metaTagsHighwirePress.html", "uuid", "views/MetricView", -], function ( +], ( $, $ui, _, @@ -78,18 +78,18 @@ define([ metaTagsHighwirePressTemplate, uuid, MetricView, -) { +) => { "use strict"; /** * @class MetadataView * @classdesc A human-readable view of a science metadata file * @classcategory Views - * @extends Backbone.View - * @constructor + * @augments Backbone.View + * @class * @screenshot views/MetadataView.png */ - var MetadataView = Backbone.View.extend( + const MetadataView = Backbone.View.extend( /** @lends MetadataView.prototype */ { subviews: [], @@ -116,7 +116,7 @@ define([ type: "Metadata", - //Templates + // Templates template: _.template(MetadataTemplate), alertTemplate: _.template(AlertTemplate), doiTemplate: _.template(PublishDoiTemplate), @@ -156,7 +156,7 @@ define([ "click #save-metadata-prov": "saveProv", }, - initialize: function (options) { + initialize(options) { if (options === undefined || !options) var options = {}; this.pid = @@ -168,13 +168,13 @@ define([ }, // Render the main metadata view - render: function () { + render() { this.stopListening(); MetacatUI.appModel.set("headerType", "default"); // this.showLoading("Loading..."); - //Reset various properties of this view first + // Reset various properties of this view first this.classMap = new Array(); this.subviews = new Array(); this.model.set(this.model.defaults); @@ -185,18 +185,18 @@ define([ this.listenTo(MetacatUI.appUserModel, "change:loggedIn", this.render); - //Listen to when the metadata has been rendered + // Listen to when the metadata has been rendered this.once("metadataLoaded", function () { this.createAnnotationViews(); this.insertMarkdownViews(); }); - //Listen to when the package table has been rendered + // Listen to when the package table has been rendered this.once("dataPackageRendered", function () { - var packageTableContainer = this.$("#data-package-container"); + const packageTableContainer = this.$("#data-package-container"); $(packageTableContainer).children(".loading").remove(); - //Scroll to the element on the page that is in the hash fragment (if there is one) + // Scroll to the element on the page that is in the hash fragment (if there is one) this.scrollToFragment(); }); @@ -211,11 +211,11 @@ define([ * table view, if there is one. * @param {string} pid - The PID of the resource map */ - getDataPackage: function (pid) { - //Create a DataONEObject model to use in the DataPackage collection. - var dataOneObject = new ScienceMetadata({ id: this.model.get("id") }); + getDataPackage(pid) { + // Create a DataONEObject model to use in the DataPackage collection. + const dataOneObject = new ScienceMetadata({ id: this.model.get("id") }); - var view = this; + const view = this; // Create a new data package with this id this.dataPackage = new DataPackage([dataOneObject], { id: pid }); @@ -235,7 +235,7 @@ define([ this.listenToOnce(this.dataPackage, "complete", function () { this.dataPackageSynced = true; this.trigger("changed:dataPackageSynced"); - var dataPackageView = _.findWhere(this.subviews, { + const dataPackageView = _.findWhere(this.subviews, { type: "DataPackage", }); if (dataPackageView) { @@ -244,16 +244,16 @@ define([ } }); - this.listenToOnce(this.dataPackage, "fetchFailed", function () { + this.listenToOnce(this.dataPackage, "fetchFailed", () => { view.dataPackageSynced = false; // stop listening to the fetch complete view.stopListening(view.dataPackage, "complete"); - //Remove the loading elements + // Remove the loading elements view.$(view.tableContainer).find(".loading").remove(); - //Show an error message + // Show an error message MetacatUI.appView.showAlert( "Error retrieving files for this data package.", "alert-error", @@ -281,15 +281,15 @@ 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: function (pid) { - //Get the pid and sid - if (typeof pid === "undefined" || !pid) var pid = this.pid; + 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; - //Get the package ID + // Get the package ID this.model.set({ id: pid, seriesId: sid }); - var model = this.model; + const { model } = this; this.listenToOnce(model, "sync", function () { if ( @@ -299,94 +299,89 @@ define([ this.model = model; this.renderMetadata(); } else if (this.model.get("formatType") == "DATA") { - //Get the metadata pids that document this data object - var isDocBy = this.model.get("isDocumentedBy"); + // 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 + // 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) { this.navigateWithFragment(_.first(isDocBy), this.pid); return; } - //If more than one metadata doc documents this data object, it is most likely + // If more than one metadata doc documents this data object, it is most likely // multiple versions of the same metadata. So we need to find the latest version. - else if (isDocBy && isDocBy.length > 1) { - var view = this; - - require([ - "collections/Filters", - "collections/SolrResults", - ], function (Filters, SolrResults) { - //Create a search for the metadata docs that document this data object - var searchFilters = new Filters([ - { - values: isDocBy, - fields: ["id", "seriesId"], - operator: "OR", - fieldsOperator: "OR", - matchSubstring: false, - }, - ]), - //Create a list of search results - searchResults = new SolrResults([], { - rows: isDocBy.length, - query: searchFilters.getQuery(), - fields: "obsoletes,obsoletedBy,id", - }); + if (isDocBy && isDocBy.length > 1) { + const view = this; + + require(["collections/Filters", "collections/SolrResults"], ( + Filters, + SolrResults, + ) => { + // Create a search for the metadata docs that document this data object + const searchFilters = new Filters([ + { + values: isDocBy, + fields: ["id", "seriesId"], + operator: "OR", + fieldsOperator: "OR", + matchSubstring: false, + }, + ]); + // Create a list of search results + const searchResults = new SolrResults([], { + rows: isDocBy.length, + query: searchFilters.getQuery(), + fields: "obsoletes,obsoletedBy,id", + }); - //When the search results are returned, process those results - view.listenToOnce( - searchResults, - "sync", - function (searchResults) { - //Keep track of the latest version of the metadata doc(s) - var latestVersions = []; - - //Iterate over each search result and find the latest version of each metadata version chain - searchResults.each(function (searchResult) { - //If this metadata isn't obsoleted by another object, it is the latest version - if (!searchResult.get("obsoletedBy")) { - latestVersions.push(searchResult.get("id")); - } - //If it is obsoleted by another object but that newer object does not document this data, then this is the latest version - else if ( - !_.contains(isDocBy, searchResult.get("obsoletedBy")) - ) { - latestVersions.push(searchResult.get("id")); - } - }, view); - - //If at least one latest version was found (should always be the case), - if (latestVersions.length) { - //Set that metadata pid as this view's pid and get that metadata model. - // TODO: Support navigation to multiple metadata docs. This should be a rare occurence, but - // it is possible that more than one metadata version chain documents a data object, and we need - // to show the user that the data is involved in multiple datasets. - view.navigateWithFragment(latestVersions[0], view.pid); + // When the search results are returned, process those results + view.listenToOnce(searchResults, "sync", (searchResults) => { + // Keep track of the latest version of the metadata doc(s) + const latestVersions = []; + + // Iterate over each search result and find the latest version of each metadata version chain + searchResults.each((searchResult) => { + // If this metadata isn't obsoleted by another object, it is the latest version + if (!searchResult.get("obsoletedBy")) { + latestVersions.push(searchResult.get("id")); } - //If a latest version wasn't found, which should never happen, but just in case, default to the - // last metadata pid in the isDocumentedBy field (most liekly to be the most recent since it was indexed last). - else { - view.navigateWithFragment(_.last(isDocBy), view.pid); + // If it is obsoleted by another object but that newer object does not document this data, then this is the latest version + else if ( + !_.contains(isDocBy, searchResult.get("obsoletedBy")) + ) { + latestVersions.push(searchResult.get("id")); } - }, - ); + }, view); + + // If at least one latest version was found (should always be the case), + if (latestVersions.length) { + // Set that metadata pid as this view's pid and get that metadata model. + // TODO: Support navigation to multiple metadata docs. This should be a rare occurence, but + // it is possible that more than one metadata version chain documents a data object, and we need + // to show the user that the data is involved in multiple datasets. + view.navigateWithFragment(latestVersions[0], view.pid); + } + // If a latest version wasn't found, which should never happen, but just in case, default to the + // last metadata pid in the isDocumentedBy field (most liekly to be the most recent since it was indexed last). + else { + view.navigateWithFragment(_.last(isDocBy), view.pid); + } + }); - //Send the query to the Solr search service + // Send the query to the Solr search service searchResults.query(); }); return; - } else { - this.noMetadata(this.model); } + this.noMetadata(this.model); } else if (this.model.get("formatType") == "RESOURCE") { - var packageModel = new Package({ id: this.model.get("id") }); + const packageModel = new Package({ id: this.model.get("id") }); packageModel.on( "complete", function () { - var metadata = packageModel.getMetadata(); + const metadata = packageModel.getMetadata(); if (!metadata) { this.noMetadata(packageModel); @@ -404,23 +399,23 @@ define([ return; } - //Get the package information + // Get the package information this.getPackageDetails(model.get("resourceMap")); }); - //Listen to 404 and 401 errors when we get the metadata object + // Listen to 404 and 401 errors when we get the metadata object this.listenToOnce(model, "404", this.showNotFound); this.listenToOnce(model, "401", this.showIsPrivate); - //Fetch the model + // Fetch the model model.getInfo(); }, - renderMetadata: function () { - var pid = this.model.get("id"); + renderMetadata() { + const pid = this.model.get("id"); this.hideLoading(); - //Load the template which holds the basic structure of the view + // Load the template which holds the basic structure of the view this.$el.html(this.template()); this.$(this.tableContainer).html( this.loadingTemplate({ @@ -428,11 +423,11 @@ define([ }), ); - //Insert the breadcrumbs + // Insert the breadcrumbs this.insertBreadcrumbs(); - //Insert the citation + // Insert the citation this.insertCitation(); - //Insert the data source logo + // Insert the data source logo this.insertDataSource(); // is this the latest version? (includes DOI link when needed) this.showLatestVersion(); @@ -443,11 +438,11 @@ define([ // If we're displaying the metrics well then display copy citation and edit button // inside the well if (MetacatUI.appModel.get("displayDatasetMetrics")) { - //Insert Metrics Stats into the dataset landing pages + // Insert Metrics Stats into the dataset landing pages this.insertMetricsControls(); } - //Show loading icon in metadata section + // Show loading icon in metadata section this.$(this.metadataContainer).html( this.loadingTemplate({ msg: "Retrieving metadata ..." }), ); @@ -461,15 +456,15 @@ define([ MetacatUI.appModel.get("viewServiceUrl") + encodeURIComponent(pid); if (endpoint && typeof endpoint !== "undefined") { - var viewRef = this; - var loadSettings = { + const viewRef = this; + const loadSettings = { url: endpoint, - success: function (response, status, xhr) { + success(response, status, xhr) { try { - //If the user has navigated away from the MetadataView, then don't render anything further + // If the user has navigated away from the MetadataView, then don't render anything further if (MetacatUI.appView.currentView != viewRef) return; - //Our fallback is to show the metadata details from the Solr index + // Our fallback is to show the metadata details from the Solr index if ( status == "error" || !response || @@ -477,7 +472,7 @@ define([ ) viewRef.renderMetadataFromIndex(); else { - //Check for a response that is a 200 OK status, but is an error msg + // Check for a response that is a 200 OK status, but is an error msg if ( response.length < 250 && response.indexOf("Error transforming document") > -1 && @@ -486,8 +481,8 @@ define([ viewRef.renderMetadataFromIndex(); return; } - //Mark this as a metadata doc with no stylesheet, or one that is at least different than usual EML and FGDC - else if (response.indexOf('id="Metadata"') == -1) { + // 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) { viewRef.$el.addClass("container no-stylesheet"); if (viewRef.model.get("indexed")) { @@ -496,12 +491,10 @@ define([ } } - //Now show the response from the view service + // Now show the response from the view service viewRef.$(viewRef.metadataContainer).html(response); - viewRef.storeEntityPIDs(response); - - //If there is no info from the index and there is no metadata doc rendered either, then display a message + // If there is no info from the index and there is no metadata doc rendered either, then display a message if ( viewRef.$el.is(".no-stylesheet") && viewRef.model.get("archived") && @@ -517,7 +510,7 @@ define([ viewRef.trigger("metadataLoaded"); - //Add a map of the spatial coverage + // Add a map of the spatial coverage if (gmaps) viewRef.insertSpatialCoverageMap(); // Injects Clipboard objects into DOM elements returned from the View Service @@ -532,7 +525,7 @@ define([ viewRef.renderMetadataFromIndex(); } }, - error: function (xhr, textStatus, errorThrown) { + error(xhr, textStatus, errorThrown) { viewRef.renderMetadataFromIndex(); }, }; @@ -544,7 +537,7 @@ define([ // Insert the Linked Data into the header of the page. if (MetacatUI.appModel.get("isJSONLDEnabled")) { - var json = this.generateJSONLD(); + const json = this.generateJSONLD(); this.insertJSONLD(json); } @@ -552,76 +545,76 @@ define([ }, /* If there is no view service available, then display the metadata fields from the index */ - renderMetadataFromIndex: function () { - var metadataFromIndex = new MetadataIndex({ + renderMetadataFromIndex() { + const metadataFromIndex = new MetadataIndex({ pid: this.pid, parentView: this, }); this.subviews.push(metadataFromIndex); - //Add the metadata HTML + // Add the metadata HTML this.$(this.metadataContainer).html(metadataFromIndex.render().el); - var view = this; + const view = this; - this.listenTo(metadataFromIndex, "complete", function () { - //Add the package contents + this.listenTo(metadataFromIndex, "complete", () => { + // Add the package contents view.insertPackageDetails(); - //Add a map of the spatial coverage + // Add a map of the spatial coverage if (gmaps) view.insertSpatialCoverageMap(); }); }, - removeCitation: function () { - var citation = "", - citationEl = null; + removeCitation() { + let citation = ""; + let citationEl = null; - //Find the citation element + // Find the citation element if (this.$(".citation").length > 0) { - //Get the text for the citation + // Get the text for the citation citation = this.$(".citation").text(); - //Save this element in the view + // Save this element in the view citationEl = this.$(".citation"); } - //Older versions of Metacat (v2.4.3 and older) will not have the citation class in the XSLT. Find the citation another way + // Older versions of Metacat (v2.4.3 and older) will not have the citation class in the XSLT. Find the citation another way else { - //Find the DOM element with the citation - var wells = this.$(".well"), - viewRef = this; + // 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, function (well) { + // Find the div.well with the citation. If we never find it, we don't insert the list of contents + _.each(wells, (well) => { if ( (!citationEl && $(well).find("#viewMetadataCitationLink").length > 0) || $(well).children(".row-fluid > .span10 > a") ) { - //Save this element in the view + // Save this element in the view citationEl = well; - //Mark this in the DOM for CSS styling + // Mark this in the DOM for CSS styling $(well).addClass("citation"); - //Save the text of the citation + // Save the text of the citation citation = $(well).text(); } }); - //Remove the unnecessary classes that are used in older versions of Metacat (2.4.3 and older) - var citationText = $(citationEl).find(".span10"); + // Remove the unnecessary classes that are used in older versions of Metacat (2.4.3 and older) + const citationText = $(citationEl).find(".span10"); $(citationText).removeClass("span10").addClass("span12"); } - //Set the document title to the citation + // Set the document title to the citation MetacatUI.appModel.set("title", citation); citationEl.remove(); }, - insertBreadcrumbs: function () { - var breadcrumbs = $(document.createElement("ol")) + insertBreadcrumbs() { + const breadcrumbs = $(document.createElement("ol")) .addClass("breadcrumb") .append( $(document.createElement("li")) @@ -640,12 +633,13 @@ define([ $(document.createElement("a")) .attr( "href", - MetacatUI.root + - "/data" + - (MetacatUI.appModel.get("page") > 0 - ? "/page/" + - (parseInt(MetacatUI.appModel.get("page")) + 1) - : ""), + `${MetacatUI.root}/data${ + MetacatUI.appModel.get("page") > 0 + ? `/page/${ + parseInt(MetacatUI.appModel.get("page")) + 1 + }` + : "" + }`, ) .addClass("search") .text("Search"), @@ -656,7 +650,7 @@ define([ $(document.createElement("a")) .attr( "href", - MetacatUI.root + "/view/" + encodeURIComponent(this.pid), + `${MetacatUI.root}/view/${encodeURIComponent(this.pid)}`, ) .addClass("inactive") .text("Metadata"), @@ -668,11 +662,11 @@ define([ $(document.createElement("a")) .attr( "href", - MetacatUI.root + - "/data/page/" + - (MetacatUI.appModel.get("page") > 0 + `${MetacatUI.root}/data/page/${ + MetacatUI.appModel.get("page") > 0 ? parseInt(MetacatUI.appModel.get("page")) + 1 - : ""), + : "" + }`, ) .attr("title", "Back") .addClass("back") @@ -690,16 +684,16 @@ define([ /* * When the metadata object doesn't exist, display a message to the user */ - showNotFound: function () { - //If the model was found, exit this function + showNotFound() { + // If the model was found, exit this function if (!this.model.get("notFound")) { return; } try { - //Check if a query string was in the URL and if so, try removing it in the identifier + // 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) { - let newID = this.model.get("id").replace(/\?\S+\=\S+/g, ""); + const newID = this.model.get("id").replace(/\?\S+\=\S+/g, ""); this.onClose(); this.model.set("id", newID); this.pid = newID; @@ -711,38 +705,38 @@ define([ console.warn("Caught error while determining query string", e); } - //Construct a message that shows this object doesn't exist - var msg = - "

    Nothing was found.

    " + - "

    The dataset identifier '" + - Utilities.encodeHTML(this.model.get("id")) + - "' " + - "does not exist or it may have been removed. Search for " + - "datasets that mention " + - Utilities.encodeHTML(this.model.get("id")) + - "

    "; + // Construct a message that shows this object doesn't exist + const msg = + `

    Nothing was found.

    ` + + `

    The dataset identifier '${Utilities.encodeHTML( + this.model.get("id"), + )}' ` + + `does not exist or it may have been removed. Search for ` + + `datasets that mention ${Utilities.encodeHTML( + this.model.get("id"), + )}

    `; - //Remove the loading message + // Remove the loading message this.hideLoading(); - //Show the not found error message + // Show the not found error message this.showError(msg); - //Add the pid to the link href. Add via JS so it is Attribute-encoded to prevent XSS attacks + // Add the pid to the link href. Add via JS so it is Attribute-encoded to prevent XSS attacks this.$("#metadata-view-not-found-message a").attr( "href", - MetacatUI.root + - "/data/query=" + - encodeURIComponent(this.model.get("id")), + `${MetacatUI.root}/data/query=${encodeURIComponent( + this.model.get("id"), + )}`, ); }, /* * When the metadata object is private, display a message to the user */ - showIsPrivate: function () { - //If we haven't checked the logged-in status of the user yet, wait a bit - //until we show a 401 msg, in case this content is their private content + showIsPrivate() { + // If we haven't checked the logged-in status of the user yet, wait a bit + // until we show a 401 msg, in case this content is their private content if (!MetacatUI.appUserModel.get("checked")) { this.listenToOnce( MetacatUI.appUserModel, @@ -752,7 +746,7 @@ define([ return; } - //If the user is logged in, the message will display that this dataset is private. + // If the user is logged in, the message will display that this dataset is private. if (MetacatUI.appUserModel.get("loggedIn")) { var msg = '' + " This is a private dataset."; } - //If the user isn't logged in, display a log in link. + // If the user isn't logged in, display a log in link. else { var msg = - '' + - '' + - '' + - " This is a private dataset. If you believe you have permission " + - 'to access this dataset, then sign in.'; + `` + + `` + + `` + + ` This is a private dataset. If you believe you have permission ` + + `to access this dataset, then sign in.`; } - //Remove the loading message + // Remove the loading message this.hideLoading(); - //Show the not found error message + // Show the not found error message this.showError(msg); }, - getPackageDetails: function (packageIDs) { - var completePackages = 0; + getPackageDetails(packageIDs) { + let completePackages = 0; - //This isn't a package, but just a lonely metadata doc... + // This isn't a package, but just a lonely metadata doc... if (!packageIDs || !packageIDs.length) { - var thisPackage = new Package({ id: null, members: [this.model] }); + const thisPackage = new Package({ id: null, members: [this.model] }); thisPackage.flagComplete(); this.packageModels = [thisPackage]; this.insertPackageDetails(thisPackage, { @@ -798,32 +790,30 @@ define([ _.each( packageIDs, function (thisPackageID, i) { - //Create a model representing the data package - var thisPackage = new Package({ id: thisPackageID }); + // Create a model representing the data package + const thisPackage = new Package({ id: thisPackageID }); - //Listen for any parent packages + // Listen for any parent packages this.listenToOnce( thisPackage, "change:parentPackageMetadata", this.insertParentLink, ); - //When the package info is fully retrieved + // When the package info is fully retrieved this.listenToOnce( thisPackage, "complete", function (thisPackage) { - //When all packages are fully retrieved + // When all packages are fully retrieved completePackages++; if (completePackages >= packageIDs.length) { - var latestPackages = _.filter( + const latestPackages = _.filter( this.packageModels, - function (m) { - return !_.contains(packageIDs, m.get("obsoletedBy")); - }, + (m) => !_.contains(packageIDs, m.get("obsoletedBy")), ); - //Set those packages as the most recent package + // Set those packages as the most recent package this.packageModels = latestPackages; this.insertPackageDetails(latestPackages); @@ -831,13 +821,13 @@ define([ }, ); - //Save the package in the view + // Save the package in the view this.packageModels.push(thisPackage); - //Make sure we get archived content, too + // Make sure we get archived content, too thisPackage.set("getArchivedMembers", true); - //Get the members + // Get the members thisPackage.getMembers({ getParentMetadata: true }); }, this, @@ -845,19 +835,19 @@ define([ } }, - alterMarkup: function () { - //Find the taxonomic range and give it a class for styling - for older versions of Metacat only (v2.4.3 and older) + alterMarkup() { + // Find the taxonomic range and give it a class for styling - for older versions of Metacat only (v2.4.3 and older) if (!this.$(".taxonomicCoverage").length) this.$('h4:contains("Taxonomic Range")') .parent() .addClass("taxonomicCoverage"); - //Remove ecogrid links and replace them with workable links + // Remove ecogrid links and replace them with workable links this.replaceEcoGridLinks(); - //Find the tab links for attribute names - this.$(".attributeListTable tr a").on("shown", function (e) { - //When the attribute link is clicked on, highlight the tab as active + // Find the tab links for attribute names + this.$(".attributeListTable tr a").on("shown", (e) => { + // When the attribute link is clicked on, highlight the tab as active $(e.target) .parents(".attributeListTable") .find(".active") @@ -865,7 +855,7 @@ define([ $(e.target).parents("tr").first().addClass("active"); }); - //Mark the first row in each attribute list table as active since the first attribute is displayed at first + // Mark the first row in each attribute list table as active since the first attribute is displayed at first this.$(".attributeListTable tr:first-child()").addClass("active"); // Add explanation text to the alternate identifier @@ -879,7 +869,7 @@ define([ * @returns {jQuery} The jQuery object for the icon element. * @since 2.26.0 */ - renderAltIdentifierHelpText: function () { + renderAltIdentifierHelpText() { try { // Find the HTML element that contains the alternate identifier. const altIdentifierLabel = this.$( @@ -917,17 +907,17 @@ define([ /* * Inserts a table with all the data package member information and sends the call to display annotations */ - insertPackageDetails: function (packages, options) { + insertPackageDetails(packages, options) { if (typeof options === "undefined") { var options = {}; } - //Don't insert the package details twice - var view = this; - var tableEls = this.$(view.tableContainer).children().not(".loading"); + // Don't insert the package details twice + const view = this; + const tableEls = this.$(view.tableContainer).children().not(".loading"); if (tableEls.length > 0) return; - //wait for the metadata to load - var metadataEls = this.$(view.metadataContainer).children(); + // wait for the metadata to load + const metadataEls = this.$(view.metadataContainer).children(); if (!metadataEls.length || metadataEls.first().is(".loading")) { this.once("metadataLoaded", function () { view.insertPackageDetails(this.packageModels, options); @@ -937,26 +927,26 @@ define([ if (!packages) var packages = this.packageModels; - //Get the entity names from this page/metadata + // Get the entity names from this page/metadata this.getEntityNames(packages); _.each( packages, function (packageModel) { - //If the package model is not complete, don't do anything + // If the package model is not complete, don't do anything if (!packageModel.complete) return; - //Insert a package table for each package in viewRef dataset - var nestedPckgs = packageModel.getNestedPackages(), - nestedPckgsToDisplay = []; + // Insert a package table for each package in viewRef dataset + const nestedPckgs = packageModel.getNestedPackages(); + let nestedPckgsToDisplay = []; - //If this metadata is not archived, filter out archived packages + // If this metadata is not archived, filter out archived packages if (!this.model.get("archived")) { - nestedPckgsToDisplay = _.reject(nestedPckgs, function (pkg) { - return pkg.get("archived"); - }); + nestedPckgsToDisplay = _.reject(nestedPckgs, (pkg) => + pkg.get("archived"), + ); } else { - //Display all packages is this metadata is archived + // Display all packages is this metadata is archived nestedPckgsToDisplay = nestedPckgs; } @@ -968,16 +958,16 @@ define([ ) ) { var title = packageModel.get("id") - ? 'Package: ' + - packageModel.get("id") + - "" + ? `Package: ${packageModel.get( + "id", + )}` : ""; - options.title = "Files in this dataset " + title; + options.title = `Files in this dataset ${title}`; options.nested = true; this.insertPackageTable(packageModel, options); } } else { - //If this metadata is not archived, then don't display archived packages + // If this metadata is not archived, then don't display archived packages if ( !( !this.model.get("archived") && @@ -985,24 +975,24 @@ define([ ) ) { var title = packageModel.get("id") - ? 'Package: ' + - packageModel.get("id") + - "" + ? `Package: ${packageModel.get( + "id", + )}` : ""; - options.title = "Files in this dataset " + title; + options.title = `Files in this dataset ${title}`; this.insertPackageTable(packageModel, options); } } - //Remove the extra download button returned from the XSLT since the package table will have all the download links + // Remove the extra download button returned from the XSLT since the package table will have all the download links $("#downloadPackage").remove(); }, this, ); - //If this metadata doc is not in a package, but is just a lonely metadata doc... + // If this metadata doc is not in a package, but is just a lonely metadata doc... if (!packages.length) { - var packageModel = new Package({ + const packageModel = new Package({ members: [this.model], }); packageModel.complete = true; @@ -1011,7 +1001,7 @@ define([ this.insertPackageTable(packageModel, options); } - //Insert the data details sections + // Insert the data details sections this.insertDataDetails(); // Get data package, if there is one, before checking write permissions @@ -1025,20 +1015,20 @@ define([ try { // Get the most recent package to display the provenance graphs if (packages.length) { - //Find the most recent Package model and fetch it + // Find the most recent Package model and fetch it let mostRecentPackage = _.find( packages, (p) => !p.get("obsoletedBy"), ); - //If all of the packages are obsoleted, then use the last package in the array, + // If all of the packages are obsoleted, then use the last package in the array, // which is most likely the most recent. /** @todo Use the DataONE version API to find the most recent package in the version chain */ if (!mostRecentPackage) { mostRecentPackage = packages[packages.length - 1]; } - //Get the data package only if it is not the same as the previously fetched package + // Get the data package only if it is not the same as the previously fetched package if (mostRecentPackage.get("id") != packages[0].get("id")) this.getDataPackage(mostRecentPackage.get("id")); } @@ -1049,16 +1039,16 @@ define([ ); } - //Initialize tooltips in the package table(s) + // Initialize tooltips in the package table(s) this.$(".tooltip-this").tooltip(); return this; }, - insertPackageTable: function (packageModel, options) { - var view = this; + insertPackageTable(packageModel, options) { + const view = this; if (this.dataPackage == null || !this.dataPackageSynced) { - this.listenToOnce(this, "changed:dataPackageSynced", function () { + this.listenToOnce(this, "changed:dataPackageSynced", () => { view.insertPackageTable(packageModel, options); }); return; @@ -1083,39 +1073,39 @@ define([ nested = false, disablePackageDownloads = false; - //** Draw the package table **// - var tableView = new DataPackageView({ + //* * Draw the package table **// + const tableView = new DataPackageView({ edit: false, dataPackage: this.dataPackage, currentlyViewing: this.pid, dataEntities: this.entities, - disablePackageDownloads: disablePackageDownloads, + disablePackageDownloads, parentView: this, - title: title, + title, packageTitle: this.model.get("title"), - nested: nested, + nested, metricsModel: this.metricsModel, }); - //Get the package table container - var tablesContainer = this.$(this.tableContainer); + // Get the package table container + const tablesContainer = this.$(this.tableContainer); - //After the first table, start collapsing them - var numTables = $(tablesContainer).find( + // After the first table, start collapsing them + const numTables = $(tablesContainer).find( "table.download-contents", ).length; if (numTables == 1) { var tableContainer = $(document.createElement("div")).attr( "id", - "additional-tables-for-" + this.cid, + `additional-tables-for-${this.cid}`, ); tableContainer.hide(); $(tablesContainer).append(tableContainer); } else if (numTables > 1) - var tableContainer = this.$("#additional-tables-for-" + this.cid); + var tableContainer = this.$(`#additional-tables-for-${this.cid}`); else var tableContainer = tablesContainer; - //Insert the package table HTML + // Insert the package table HTML $(tableContainer).empty(); $(tableContainer).append(tableView.render().el); @@ -1140,39 +1130,39 @@ define([ this.subviews.push(tableView); - //Trigger a custom event in this view that indicates the package table has been rendered + // Trigger a custom event in this view that indicates the package table has been rendered this.trigger("dataPackageRendered"); }, - insertParentLink: function (packageModel) { - var parentPackageMetadata = packageModel.get("parentPackageMetadata"), - view = this; + insertParentLink(packageModel) { + const parentPackageMetadata = packageModel.get("parentPackageMetadata"); + const view = this; - _.each(parentPackageMetadata, function (m, i) { - var title = m.get("title"), - icon = $(document.createElement("i")).addClass( - "icon icon-on-left icon-level-up", - ), - link = $(document.createElement("a")) - .attr( - "href", - MetacatUI.root + "/view/" + encodeURIComponent(m.get("id")), - ) - .addClass("parent-link") - .text("Parent dataset: " + title) - .prepend(icon); + _.each(parentPackageMetadata, (m, i) => { + const title = m.get("title"); + const icon = $(document.createElement("i")).addClass( + "icon icon-on-left icon-level-up", + ); + const link = $(document.createElement("a")) + .attr( + "href", + `${MetacatUI.root}/view/${encodeURIComponent(m.get("id"))}`, + ) + .addClass("parent-link") + .text(`Parent dataset: ${title}`) + .prepend(icon); view.$(view.parentLinkContainer).append(link); }); }, - insertSpatialCoverageMap: function (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 + 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) { - //For EML - var title = this.$('h4:contains("Geographic Region")'); + // For EML + let title = this.$('h4:contains("Geographic Region")'); - //For FGDC + // For FGDC if (title.length == 0) { title = this.$('label:contains("Bounding Coordinates")'); } @@ -1185,11 +1175,11 @@ define([ var directions = new Array("north", "south", "east", "west"); } - for (var i = 0; i < georegionEls.length; i++) { + for (let i = 0; i < georegionEls.length; i++) { var georegion = georegionEls[i]; if (typeof customCoordinates !== "undefined") { - //Extract the coordinates + // Extract the coordinates var n = customCoordinates[0]; var s = customCoordinates[1]; var e = customCoordinates[2]; @@ -1197,16 +1187,16 @@ define([ } else { var coordinates = new Array(); - _.each(directions, function (direction) { - //Parse text for older versions of Metacat (v2.4.3 and earlier) + _.each(directions, (direction) => { + // Parse text for older versions of Metacat (v2.4.3 and earlier) if (parseText) { - var labelEl = $(georegion).find( - 'label:contains("' + direction + '")', + const labelEl = $(georegion).find( + `label:contains("${direction}")`, ); if (labelEl.length) { var coordinate = $(labelEl).next().html(); if ( - typeof coordinate != "undefined" && + typeof coordinate !== "undefined" && coordinate.indexOf(" ") > -1 ) coordinate = coordinate.substring( @@ -1216,115 +1206,62 @@ define([ } } else { var coordinate = $(georegion) - .find("." + direction + "BoundingCoordinate") + .find(`.${direction}BoundingCoordinate`) .attr("data-value"); } - //Save our coordinate value + // Save our coordinate value coordinates.push(coordinate); }); - //Extract the coordinates + // Extract the coordinates var n = coordinates[0]; var s = coordinates[1]; var e = coordinates[2]; var w = coordinates[3]; } - //Create Google Map LatLng objects out of our coordinates - var latLngSW = new gmaps.LatLng(s, w); - var latLngNE = new gmaps.LatLng(n, e); - var latLngNW = new gmaps.LatLng(n, w); - var latLngSE = new gmaps.LatLng(s, e); + // Create Google Map LatLng objects out of our coordinates + const latLngSW = new gmaps.LatLng(s, w); + const latLngNE = new gmaps.LatLng(n, e); + const latLngNW = new gmaps.LatLng(n, w); + const latLngSE = new gmaps.LatLng(s, e); - //Get the centertroid location of this data item - var bounds = new gmaps.LatLngBounds(latLngSW, latLngNE); - var latLngCEN = bounds.getCenter(); + // Get the centertroid location of this data item + const bounds = new gmaps.LatLngBounds(latLngSW, latLngNE); + const latLngCEN = bounds.getCenter(); - //If there isn't a center point found, don't draw the map. - if (typeof latLngCEN == "undefined") { + // If there isn't a center point found, don't draw the map. + if (typeof latLngCEN === "undefined") { return; } - //Get the map path color - var pathColor = MetacatUI.appModel.get("datasetMapPathColor"); + // Get the map path color + let pathColor = MetacatUI.appModel.get("datasetMapPathColor"); if (pathColor) { - pathColor = "color:" + pathColor + "|"; + pathColor = `color:${pathColor}|`; } else { pathColor = ""; } - //Get the map path fill color - var fillColor = MetacatUI.appModel.get("datasetMapFillColor"); + // Get the map path fill color + let fillColor = MetacatUI.appModel.get("datasetMapFillColor"); if (fillColor) { - fillColor = "fillcolor:" + fillColor + "|"; + fillColor = `fillcolor:${fillColor}|`; } else { fillColor = ""; } - //Create a google map image - var mapHTML = - ""; - - //Find the spot in the DOM to insert our map image + // Create a google map image + const mapHTML = + ``; + + // Find the spot in the DOM to insert our map image if (parseText) var insertAfter = $(georegion) .find('label:contains("West")') @@ -1332,7 +1269,7 @@ define([ .parent().length ? $(georegion).find('label:contains("West")').parent().parent() : georegion; - //The last coordinate listed + // The last coordinate listed else var insertAfter = georegion; // Get the URL to the interactive Google Maps instance @@ -1342,7 +1279,7 @@ define([ $(insertAfter).append( this.mapTemplate({ map: mapHTML, - url: url, + url, }), ); @@ -1367,7 +1304,7 @@ define([ * @returns {string} The URL to the Google Maps instance. * @since 2.27.0 */ - getGoogleMapsUrl: function (latLngCEN, bounds) { + getGoogleMapsUrl(latLngCEN, bounds) { // Use the window width and height as a proxy for the map dimensions const mapDim = { height: $(window).height(), @@ -1384,50 +1321,60 @@ define([ * Returns the zoom level that will display the given bounding box at * the given dimensions. * @param {LatLngBounds} bounds - The bounding box to display. - * @param {Object} mapDim - The dimensions of the map. + * @param {object} mapDim - The dimensions of the map. * @param {number} mapDim.height - The height of the map. * @param {number} mapDim.width - The width of the map. * @returns {number} The zoom level. * @since 2.27.0 */ - getBoundsZoomLevel: function (bounds, mapDim) { - var WORLD_DIM = { height: 256, width: 256 }; - var ZOOM_MAX = 15; + getBoundsZoomLevel(bounds, mapDim) { + const WORLD_DIM = { height: 256, width: 256 }; + const ZOOM_MAX = 15; // 21 is actual max, but any closer and the map is too zoomed in to be // useful + /** + * + * @param lat + */ function latRad(lat) { - var sin = Math.sin((lat * Math.PI) / 180); - var radX2 = Math.log((1 + sin) / (1 - sin)) / 2; + const sin = Math.sin((lat * Math.PI) / 180); + const radX2 = Math.log((1 + sin) / (1 - sin)) / 2; return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2; } + /** + * + * @param mapPx + * @param worldPx + * @param fraction + */ function zoom(mapPx, worldPx, fraction) { return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2); } - var ne = bounds.getNorthEast(); - var sw = bounds.getSouthWest(); + const ne = bounds.getNorthEast(); + const sw = bounds.getSouthWest(); - var latFraction = (latRad(ne.lat()) - latRad(sw.lat())) / Math.PI; + const latFraction = (latRad(ne.lat()) - latRad(sw.lat())) / Math.PI; - var lngDiff = ne.lng() - sw.lng(); - var lngFraction = (lngDiff < 0 ? lngDiff + 360 : lngDiff) / 360; + const lngDiff = ne.lng() - sw.lng(); + const lngFraction = (lngDiff < 0 ? lngDiff + 360 : lngDiff) / 360; - var latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction); - var lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction); + const latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction); + const lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction); return Math.min(latZoom, lngZoom, ZOOM_MAX); }, - insertCitation: function () { + insertCitation() { if (!this.model) return false; - //Create a citation header element from the model attributes - var header = new CitationHeaderView({ model: this.model }); + // Create a citation header element from the model attributes + const header = new CitationHeaderView({ model: this.model }); this.$(this.citationContainer).html(header.render().el); }, - insertDataSource: function () { + insertDataSource() { if ( !this.model || !MetacatUI.nodeModel || @@ -1436,12 +1383,12 @@ define([ ) return; - var dataSource = MetacatUI.nodeModel.getMember(this.model), - replicaMNs = MetacatUI.nodeModel.getMembers( - this.model.get("replicaMN"), - ); + const dataSource = MetacatUI.nodeModel.getMember(this.model); + let replicaMNs = MetacatUI.nodeModel.getMembers( + this.model.get("replicaMN"), + ); - //Filter out the data source from the replica nodes + // Filter out the data source from the replica nodes if (Array.isArray(replicaMNs) && replicaMNs.length) { replicaMNs = _.without(replicaMNs, dataSource); } @@ -1449,20 +1396,20 @@ define([ if (dataSource && dataSource.logo) { this.$("img.data-source").remove(); - //Construct a URL to the profile of this repository - var profileURL = + // Construct a URL to the profile of this repository + const profileURL = dataSource.identifier == MetacatUI.appModel.get("nodeId") - ? MetacatUI.root + "/profile" - : MetacatUI.appModel.get("dataoneSearchUrl") + - "/portals/" + - dataSource.shortIdentifier; + ? `${MetacatUI.root}/profile` + : `${MetacatUI.appModel.get("dataoneSearchUrl")}/portals/${ + dataSource.shortIdentifier + }`; - //Insert the data source template + // Insert the data source template this.$(this.dataSourceContainer) .html( this.dataSourceTemplate({ node: dataSource, - profileURL: profileURL, + profileURL, }), ) .addClass("has-data-source"); @@ -1474,25 +1421,19 @@ define([ .popover({ trigger: "manual", html: true, - title: "From the " + dataSource.name + " repository", - content: function () { - var content = "

    " + dataSource.description + "

    "; + title: `From the ${dataSource.name} repository`, + content() { + let content = `

    ${dataSource.description}

    `; if (replicaMNs.length) { - content += - "
    Exact copies hosted by " + - replicaMNs.length + - ' repositories:
      '; - - _.each(replicaMNs, function (node) { - content += - '
    • ' + - node.name + - "
    • "; + content += `
      Exact copies hosted by ${replicaMNs.length} repositories:
      "; @@ -1503,15 +1444,15 @@ define([ animation: false, }) .on("mouseenter", function () { - var _this = this; + const _this = this; $(this).popover("show"); - $(".popover").on("mouseleave", function () { + $(".popover").on("mouseleave", () => { $(_this).popover("hide"); }); }) .on("mouseleave", function () { - var _this = this; - setTimeout(function () { + const _this = this; + setTimeout(() => { if (!$(".popover:hover").length) { $(_this).popover("hide"); } @@ -1525,13 +1466,15 @@ define([ * Once the permission checks have finished, continue with the functions that * depend on them. */ - checkWritePermissions: function () { - var view = this, - authorization = [], - resourceMap = this.dataPackage ? this.dataPackage.packageModel : null, - modelsToCheck = [this.model, resourceMap]; - - modelsToCheck.forEach(function (model, index) { + checkWritePermissions() { + const view = this; + const authorization = []; + const resourceMap = this.dataPackage + ? this.dataPackage.packageModel + : null; + const modelsToCheck = [this.model, resourceMap]; + + 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) { @@ -1548,32 +1491,30 @@ define([ // Return to this function once we've finished checking. } else { view.stopListening(model, "change:isAuthorized_write"); - view.listenToOnce(model, "change:isAuthorized_write", function () { + view.listenToOnce(model, "change:isAuthorized_write", () => { view.checkWritePermissions(); }); view.stopListening(model, "change:notFound"); - view.listenToOnce(model, "change:notFound", function () { + view.listenToOnce(model, "change:notFound", () => { view.checkWritePermissions(); }); model.checkAuthority("write"); - return; } }); // Check that all the models were tested for authorization // Every value in the auth array must be true for the user to have full permissions - var allTrue = _.every(authorization, function (test) { - return test; - }), - // When we have completed checking each of the models that we need to check for - // permissions, every value in the authorization array should be "true" or "false", - // and the array should have the same length as the modelsToCheck array. - allBoolean = _.every(authorization, function (test) { - return typeof test === "boolean"; - }), - allChecked = - allBoolean && authorization.length === modelsToCheck.length; + const allTrue = _.every(authorization, (test) => test); + // When we have completed checking each of the models that we need to check for + // permissions, every value in the authorization array should be "true" or "false", + // and the array should have the same length as the modelsToCheck array. + const allBoolean = _.every( + authorization, + (test) => typeof test === "boolean", + ); + const allChecked = + allBoolean && authorization.length === modelsToCheck.length; // Check for and render prov diagrams now that we know whether or not the user has editor permissions // (There is a different version of the chart for users who can edit the resource map and users who cannot) @@ -1592,16 +1533,18 @@ define([ * Inserts control elements onto the page for the user to interact with the dataset - edit, publish, etc. * Editor permissions should already have been checked before running this function. */ - insertEditorControls: function () { - var view = this, - resourceMap = this.dataPackage ? this.dataPackage.packageModel : null, - modelsToCheck = [this.model, resourceMap], - authorized = _.every(modelsToCheck, function (model) { - // If there is no EML or no resource map, the user doesn't need permission to edit it. - return !model || model.get("notFound") == true - ? true - : model.get("isAuthorized_write") === true; - }); + insertEditorControls() { + const view = this; + const resourceMap = this.dataPackage + ? this.dataPackage.packageModel + : null; + 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 + ? true + : model.get("isAuthorized_write") === true, + ); // Only run this function when the user has full editor permissions // (i.e. write permission on the EML, and write permission on the resource map if there is one.) @@ -1618,23 +1561,23 @@ define([ } // Save the element that will contain the owner control buttons - var container = this.$(this.editorControlsContainer); + const container = this.$(this.editorControlsContainer); // Do not insert the editor controls twice container.empty(); // The PID for the EML model - var pid = this.model.get("id") || this.pid; + const pid = this.model.get("id") || this.pid; - //Insert an Edit button if the Edit button is enabled + // Insert an Edit button if the Edit button is enabled if (MetacatUI.appModel.get("displayDatasetEditButton")) { - //Check that this is an editable metadata format + // Check that this is an editable metadata format if ( _.contains( MetacatUI.appModel.get("editableFormats"), this.model.get("formatId"), ) ) { - //Insert the Edit Metadata template + // Insert the Edit Metadata template container.append( this.editMetadataTemplate({ identifier: pid, @@ -1642,7 +1585,7 @@ define([ }), ); } - //If this format is not editable, insert an unspported Edit Metadata template + // If this format is not editable, insert an unspported Edit Metadata template else { container.append( this.editMetadataTemplate({ @@ -1653,19 +1596,19 @@ define([ } try { - //Determine if this metadata can be published. + // Determine if this metadata can be published. // The Publish feature has to be enabled in the app. // The model cannot already have a DOI - var canBePublished = + let canBePublished = MetacatUI.appModel.get("enablePublishDOI") && !view.model.isDOI(); - //If publishing is enabled, check if only certain users and groups can publish metadata + // 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 - var authorizedPublishers = MetacatUI.appModel.get( + // 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 + // 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) && @@ -1681,9 +1624,9 @@ define([ } } - //If this metadata can be published, then insert the Publish button template + // If this metadata can be published, then insert the Publish button template if (canBePublished) { - //Insert a Publish button template + // Insert a Publish button template container.append( view.doiTemplate({ isAuthorized: true, @@ -1711,14 +1654,14 @@ define([ * * to your XSLT and this should pick it up automatically. */ - insertCopiables: function () { - var copiables = $("#Metadata .copy"); + insertCopiables() { + const copiables = $("#Metadata .copy"); - _.each(copiables, function (copiable) { - var clipboard = new Clipboard(copiable); + _.each(copiables, (copiable) => { + const clipboard = new Clipboard(copiable); - clipboard.on("success", function (e) { - var el = $(e.trigger); + clipboard.on("success", (e) => { + const el = $(e.trigger); $(el).html( $(document.createElement("span")).addClass( @@ -1729,7 +1672,7 @@ define([ // Use setTimeout instead of jQuery's built-in Events system because // it didn't look flexible enough to allow me update innerHTML in // a chain - setTimeout(function () { + setTimeout(() => { $(el).html("Copy"); }, 500); }); @@ -1740,24 +1683,24 @@ define([ * Inserts elements users can use to interact with this dataset: * - A "Copy Citation" button to copy the citation text */ - insertControls: function () { + 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, "\\\\$&"); }; - var mdqFormatIds = MetacatUI.appModel.get("mdqFormatIds"); + 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. - var thisFormatId = this.model.get("formatId"); - var mdqFormatSupported = false; - var formatFound = false; + const thisFormatId = this.model.get("formatId"); + const mdqFormatSupported = false; + let formatFound = false; if (mdqFormatIds !== null) { - for (var ifmt = 0; ifmt < mdqFormatIds.length; ++ifmt) { - var currentFormatId = RegExp.escape(mdqFormatIds[ifmt]); - var re = new RegExp(currentFormatId); + for (let ifmt = 0; ifmt < mdqFormatIds.length; ++ifmt) { + const currentFormatId = RegExp.escape(mdqFormatIds[ifmt]); + const re = new RegExp(currentFormatId); formatFound = re.test(thisFormatId); if (formatFound) { break; @@ -1765,8 +1708,8 @@ define([ } } - //Get template - var controlsContainer = this.controlsTemplate({ + // Get template + const controlsContainer = this.controlsTemplate({ citationTarget: this.citationContainer, url: window.location, displayQualtyReport: @@ -1779,8 +1722,8 @@ define([ $(this.controlsContainer).html(controlsContainer); - //Insert the info icons - var metricsWell = this.$(".metrics-container"); + // Insert the info icons + const metricsWell = this.$(".metrics-container"); metricsWell.append( this.infoIconsTemplate({ model: this.model.toJSON(), @@ -1814,43 +1757,38 @@ define([ /** *Creates a button which the user can click to launch the package in Whole Tale */ - createWholeTaleButton: function () { - let self = this; - MetacatUI.appModel - .get("taleEnvironments") - .forEach(function (environment) { - var queryParams = - "?uri=" + - window.location.href + - "&title=" + - encodeURIComponent(self.model.get("title")) + - "&environment=" + - environment + - "&api=" + - MetacatUI.appModel.get("d1CNBaseUrl") + - MetacatUI.appModel.get("d1CNService"); - var composeUrl = - MetacatUI.appModel.get("dashboardUrl") + queryParams; - var anchor = $(""); - anchor - .attr("href", composeUrl) - .append($("").attr("class", "tab").append(environment)); - anchor.attr("target", "_blank"); - $(".analyze.dropdown-menu").append($("
    • ").append(anchor)); - }); + createWholeTaleButton() { + const self = this; + MetacatUI.appModel.get("taleEnvironments").forEach((environment) => { + const queryParams = `?uri=${ + window.location.href + }&title=${encodeURIComponent( + self.model.get("title"), + )}&environment=${environment}&api=${MetacatUI.appModel.get( + "d1CNBaseUrl", + )}${MetacatUI.appModel.get("d1CNService")}`; + const composeUrl = + MetacatUI.appModel.get("dashboardUrl") + queryParams; + const anchor = $(""); + anchor + .attr("href", composeUrl) + .append($("").attr("class", "tab").append(environment)); + anchor.attr("target", "_blank"); + $(".analyze.dropdown-menu").append($("
    • ").append(anchor)); + }); }, // Inserting the Metric Stats - insertMetricsControls: function () { - //Exit if metrics shouldn't be shown for this dataset + insertMetricsControls() { + // Exit if metrics shouldn't be shown for this dataset if (this.model.hideMetrics()) { return; } - var pid_list = []; + const pid_list = []; pid_list.push(this.pid); - var metricsModel = new MetricsModel({ - pid_list: pid_list, + const metricsModel = new MetricsModel({ + pid_list, type: "dataset", }); metricsModel.fetch(); @@ -1860,10 +1798,10 @@ define([ // TODO: Create a Metric Request Object if (MetacatUI.appModel.get("displayDatasetMetrics")) { - var buttonToolbar = this.$(".metrics-container"); + const buttonToolbar = this.$(".metrics-container"); if (MetacatUI.appModel.get("displayDatasetDownloadMetric")) { - var dwnldsMetricView = new MetricView({ + const dwnldsMetricView = new MetricView({ metricName: "Downloads", model: metricsModel, pid: this.pid, @@ -1873,7 +1811,7 @@ define([ } if (MetacatUI.appModel.get("displayDatasetCitationMetric")) { - var citationsMetricView = new MetricView({ + const citationsMetricView = new MetricView({ metricName: "Citations", model: metricsModel, pid: this.pid, @@ -1882,15 +1820,15 @@ define([ this.subviews.push(citationsMetricView); try { - //Check if the registerCitation=true query string is set + // 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 + // Open the modal for the citations citationsMetricView.showMetricModal(); - //Show the register citation form + // Show the register citation form if (citationsMetricView.modalView) { citationsMetricView.modalView.on( "renderComplete", @@ -1905,7 +1843,7 @@ define([ } if (MetacatUI.appModel.get("displayDatasetViewMetric")) { - var viewsMetricView = new MetricView({ + const viewsMetricView = new MetricView({ metricName: "Views", model: metricsModel, pid: this.pid, @@ -1922,14 +1860,14 @@ define([ * The view must have the DataPackage collection set as view.dataPackage * for this function to run. */ - checkForProv: function () { + checkForProv() { if (!this.dataPackage) { return; } // 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. - var model = this.model; + const { model } = this; if (this.dataPackage.provenanceFlag == "complete") { this.redrawProvCharts(this.dataPackage); } else { @@ -1945,7 +1883,7 @@ 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 */ - drawProvCharts: function (dataPackage) { + drawProvCharts(dataPackage) { // Set a listener to re-draw the prov charts when needed this.stopListening(this.dataPackage, "redrawProvCharts"); this.listenToOnce( @@ -1959,54 +1897,50 @@ define([ // If the user is authorized to edit the provenance for this package // then turn on editing, so that edit icons are displayed. - var editModeOn = + let editModeOn = this.dataPackage.packageModel.get("isAuthorized_write"); - //If this content is archived, then turn edit mode off + // If this content is archived, then turn edit mode off if (this.model.get("archived")) { editModeOn = false; } - //If none of the models in this package have the formatId attributes, + // If none of the models in this package have the formatId attributes, // we should fetch the DataPackage since it likely has only had a shallow fetch so far - var formats = _.compact(dataPackage.pluck("formatId")); + const formats = _.compact(dataPackage.pluck("formatId")); - //If the number of formatIds is less than the number of models in this collection, + // If the number of formatIds is less than the number of models in this collection, // then we need to get them. if (formats.length < dataPackage.length) { - var modelsToMerge = []; + let modelsToMerge = []; - //Get the PackageModel associated with this view + // Get the PackageModel associated with this view if (this.packageModels.length) { - //Get the PackageModel for this DataPackage - var packageModel = _.find( + // Get the PackageModel for this DataPackage + const packageModel = _.find( this.packageModels, - function (packageModel) { - return packageModel.get("id") == dataPackage.id; - }, + (packageModel) => packageModel.get("id") == dataPackage.id, ); - //Merge the SolrResult models into the DataONEObject models + // Merge the SolrResult models into the DataONEObject models if (packageModel && packageModel.get("members").length) { modelsToMerge = packageModel.get("members"); } } - //If there is at least one model to merge into this data package, do so + // If there is at least one model to merge into this data package, do so if (modelsToMerge.length) { dataPackage.mergeModels(modelsToMerge); } - //If there are no models to merge in, get them from the index + // 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 + // Listen to the DataPackage fetch to complete and re-execute this function this.listenToOnce(dataPackage, "complete", function () { this.drawProvCharts(dataPackage); }); - //Create a query that searches for all the members of this DataPackage in Solr - dataPackage.solrResults.currentquery = - dataPackage.filterModel.getQuery() + - "%20AND%20-formatType:METADATA"; + // Create a query that searches for all the members of this DataPackage in Solr + dataPackage.solrResults.currentquery = `${dataPackage.filterModel.getQuery()}%20AND%20-formatType:METADATA`; dataPackage.solrResults.fields = "id,seriesId,formatId,fileName"; dataPackage.solrResults.rows = dataPackage.length; dataPackage.solrResults.sort = null; @@ -2014,36 +1948,36 @@ define([ dataPackage.solrResults.facet = []; dataPackage.solrResults.stats = null; - //Fetch the data package with the "fromIndex" option + // Fetch the data package with the "fromIndex" option dataPackage.fetch({ fromIndex: true }); - //Exit this function since it will be executed again when the fetch is complete + // Exit this function since it will be executed again when the fetch is complete return; } } var view = this; - //Draw two flow charts to represent the sources and derivations at a package level - var packageSources = dataPackage.sourcePackages; - var packageDerivations = dataPackage.derivationPackages; + // Draw two flow charts to represent the sources and derivations at a package level + const packageSources = dataPackage.sourcePackages; + const packageDerivations = dataPackage.derivationPackages; if (Object.keys(packageSources).length) { - var sourceProvChart = new ProvChart({ + const sourceProvChart = new ProvChart({ sources: packageSources, context: dataPackage, contextEl: this.$(this.articleContainer), - dataPackage: dataPackage, + dataPackage, parentView: view, }); this.subviews.push(sourceProvChart); this.$(this.articleContainer).before(sourceProvChart.render().el); } if (Object.keys(packageDerivations).length) { - var derivationProvChart = new ProvChart({ + const derivationProvChart = new ProvChart({ derivations: packageDerivations, context: dataPackage, contextEl: this.$(this.articleContainer), - dataPackage: dataPackage, + dataPackage, parentView: view, }); this.subviews.push(derivationProvChart); @@ -2055,8 +1989,8 @@ define([ dataPackage.derivations.length || editModeOn ) { - //Draw the provenance charts for each member of this package at an object level - _.each(dataPackage.toArray(), function (member, i) { + // Draw the provenance charts for each member of this package at an object level + _.each(dataPackage.toArray(), (member, i) => { // Don't draw prov charts for metadata objects. if ( member.get("type").toLowerCase() == "metadata" || @@ -2064,27 +1998,29 @@ define([ ) { return; } - var entityDetailsSection = view.findEntityDetailsContainer(member); + const entityDetailsSection = + view.findEntityDetailsContainer(member); if (!entityDetailsSection) { return; } - //Retrieve the sources and derivations for this member - var memberSources = member.get("provSources") || new Array(), - memberDerivations = member.get("provDerivations") || new Array(); + // Retrieve the sources and derivations for this member + const memberSources = member.get("provSources") || new Array(); + const memberDerivations = + member.get("provDerivations") || new Array(); - //Make the source chart for this member. + // Make the source chart for this member. // If edit is on, then either a 'blank' sources ProvChart will be displayed if there // are no sources for this member, or edit icons will be displayed with prov icons. if (memberSources.length || editModeOn) { - var memberSourcesProvChart = new ProvChart({ + const memberSourcesProvChart = new ProvChart({ sources: memberSources, context: member, contextEl: entityDetailsSection, - dataPackage: dataPackage, + dataPackage, parentView: view, - editModeOn: editModeOn, + editModeOn, editorType: "sources", }); view.subviews.push(memberSourcesProvChart); @@ -2094,17 +2030,17 @@ define([ view.$(view.articleContainer).addClass("gutters"); } - //Make the derivation chart for this member + // Make the derivation chart for this member // If edit is on, then either a 'blank' derivations ProvChart will be displayed if there, // are no derivations for this member or edit icons will be displayed with prov icons. if (memberDerivations.length || editModeOn) { - var memberDerivationsProvChart = new ProvChart({ + const memberDerivationsProvChart = new ProvChart({ derivations: memberDerivations, context: member, contextEl: entityDetailsSection, - dataPackage: dataPackage, + dataPackage, parentView: view, - editModeOn: editModeOn, + editModeOn, editorType: "derivations", }); view.subviews.push(memberDerivationsProvChart); @@ -2116,44 +2052,44 @@ define([ }); } - //Make all of the prov chart nodes look different based on id + // Make all of the prov chart nodes look different based on id if (this.$(".prov-chart").length > 10000) { - var allNodes = this.$(".prov-chart .node"), - ids = [], - view = this, - i = 1; + const allNodes = this.$(".prov-chart .node"); + let ids = []; + var view = this; + let i = 1; $(allNodes).each(function () { ids.push($(this).attr("data-id")); }); ids = _.uniq(ids); - _.each(ids, function (id) { - var matchingNodes = view - .$(".prov-chart .node[data-id='" + id + "']") + _.each(ids, (id) => { + const matchingNodes = view + .$(`.prov-chart .node[data-id='${id}']`) .not(".editorNode"); - //var matchingEntityDetails = view.findEntityDetailsContainer(id); + // var matchingEntityDetails = view.findEntityDetailsContainer(id); - //Don't use the unique class on images since they will look a lot different anyway by their image + // Don't use the unique class on images since they will look a lot different anyway by their image if (!$(matchingNodes).first().hasClass("image")) { - var className = "uniqueNode" + i; + const className = `uniqueNode${i}`; - //Add the unique class and up the iterator + // Add the unique class and up the iterator if (matchingNodes.prop("tagName") != "polygon") $(matchingNodes).addClass(className); else $(matchingNodes).attr( "class", - $(matchingNodes).attr("class") + " " + className, + `${$(matchingNodes).attr("class")} ${className}`, ); /* if(matchingEntityDetails) - $(matchingEntityDetails).addClass(className);*/ + $(matchingEntityDetails).addClass(className); */ - //Save this id->class mapping in this view + // Save this id->class mapping in this view view.classMap.push({ - id: id, - className: className, + id, + className, }); i++; } @@ -2164,8 +2100,8 @@ define([ /* Step through all prov charts and re-render each one that has been marked for re-rendering. */ - redrawProvCharts: function () { - var view = this; + redrawProvCharts() { + const view = this; // Check if prov edits are active and turn on the prov save bar if so. // Alternatively, turn off save bar if there are no prov edits, which @@ -2177,11 +2113,11 @@ define([ this.hideEditorControls(); // Reset the edited flag for each package member - _.each(this.dataPackage.toArray(), function (item) { + _.each(this.dataPackage.toArray(), (item) => { item.selectedInEditor == false; }); } - _.each(this.subviews, function (thisView, i) { + _.each(this.subviews, (thisView, i) => { // Check if this is a ProvChartView if ( thisView.className && @@ -2194,9 +2130,11 @@ define([ }); // Remove prov charts from the array of subviews. - this.subviews = _.filter(this.subviews, function (item) { - return item.className && item.className.indexOf("prov-chart") == -1; - }); + this.subviews = _.filter( + this.subviews, + (item) => + item.className && item.className.indexOf("prov-chart") == -1, + ); view.drawProvCharts(this.dataPackage); }, @@ -2204,17 +2142,17 @@ define([ /* * When the data package collection saves successfully, tell the user */ - saveSuccess: function (savedObject) { - //We only want to perform these actions after the package saves + saveSuccess(savedObject) { + // We only want to perform these actions after the package saves if (savedObject.type != "DataPackage") return; - //Change the URL to the new id + // Change the URL to the new id MetacatUI.uiRouter.navigate( - "view/" + this.dataPackage.packageModel.get("id"), + `view/${this.dataPackage.packageModel.get("id")}`, { trigger: false, replace: true }, ); - var message = $(document.createElement("div")).append( + const message = $(document.createElement("div")).append( $(document.createElement("span")).text( "Your changes have been saved. ", ), @@ -2240,17 +2178,17 @@ define([ // Update the metadata table header with the new resource map id. // First find the DataPackageView for the top level package, and // then re-render it with the update resmap id. - var view = this; - var metadataId = this.packageModels[0].getMetadata().get("id"); - _.each(this.subviews, function (thisView, i) { + const view = this; + const metadataId = this.packageModels[0].getMetadata().get("id"); + _.each(this.subviews, (thisView, i) => { // Check if this is a ProvChartView if (thisView.type && thisView.type.indexOf("DataPackage") !== -1) { if (thisView.currentlyViewing == metadataId) { - var packageId = view.dataPackage.packageModel.get("id"); - var title = packageId - ? 'Package: ' + packageId + "" + const packageId = view.dataPackage.packageModel.get("id"); + const title = packageId + ? `Package: ${packageId}` : ""; - thisView.title = "Files in this dataset " + title; + thisView.title = `Files in this dataset ${title}`; thisView.render(); } } @@ -2260,17 +2198,17 @@ define([ /* * When the data package collection fails to save, tell the user */ - saveError: function (errorMsg) { - var errorId = "error" + Math.round(Math.random() * 100), - message = $(document.createElement("div")).append( - "

      Your changes could not be saved.

      ", - ); + saveError(errorMsg) { + const errorId = `error${Math.round(Math.random() * 100)}`; + const message = $(document.createElement("div")).append( + "

      Your changes could not be saved.

      ", + ); message.append( $(document.createElement("a")) .text("See details") .attr("data-toggle", "collapse") - .attr("data-target", "#" + errorId) + .attr("data-target", `#${errorId}`) .addClass("pointer"), $(document.createElement("div")) .addClass("collapse") @@ -2279,7 +2217,7 @@ define([ ); MetacatUI.appView.showAlert(message, "alert-error", "body", null, { - emailBody: "Error message: Data Package save error: " + errorMsg, + emailBody: `Error message: Data Package save error: ${errorMsg}`, remove: true, }); @@ -2294,11 +2232,11 @@ define([ /* If provenance relationships have been modified by the provenance editor (in ProvChartView), then update the ORE Resource Map and save it to the server. */ - saveProv: function () { + saveProv() { // Only call this function once per save operation. if (this.saveProvPending) return; - var view = this; + const view = this; if (this.dataPackage.provEditsPending()) { this.saveProvPending = true; // If the Data Package failed saving, display an error message @@ -2312,12 +2250,12 @@ define([ this.showSaving(); this.dataPackage.saveProv(); } else { - //TODO: should a dialog be displayed saying that no prov edits were made? + // TODO: should a dialog be displayed saying that no prov edits were made? } }, - showSaving: function () { - //Change the style of the save button + showSaving() { + // Change the style of the save button this.$("#save-metadata-prov") .html(' Saving...') .addClass("btn-disabled"); @@ -2325,59 +2263,59 @@ define([ this.$("input, textarea, select, button").prop("disabled", true); }, - hideSaving: function () { + hideSaving() { this.$("input, textarea, select, button").prop("disabled", false); - //When prov is saved, revert the Save button back to normal + // When prov is saved, revert the Save button back to normal this.$("#save-metadata-prov").html("Save").removeClass("btn-disabled"); }, - showEditorControls: function () { + showEditorControls() { this.$("#editor-footer").slideDown(); }, - hideEditorControls: function () { + hideEditorControls() { this.$("#editor-footer").slideUp(); }, - getEntityNames: function (packageModels) { - var viewRef = this; + getEntityNames(packageModels) { + const viewRef = this; - _.each(packageModels, function (packageModel) { - //Don't get entity names for larger packages - users must put the names in the system metadata + _.each(packageModels, (packageModel) => { + // Don't get entity names for larger packages - users must put the names in the system metadata if (packageModel.get("members").length > 100) return; - //If this package has a different metadata doc than the one we are currently viewing - var metadataModel = packageModel.getMetadata(); + // If this package has a different metadata doc than the one we are currently viewing + const metadataModel = packageModel.getMetadata(); if (!metadataModel) return; if (metadataModel.get("id") != viewRef.pid) { - var requestSettings = { + const requestSettings = { url: MetacatUI.appModel.get("viewServiceUrl") + encodeURIComponent(metadataModel.get("id")), - success: function (parsedMetadata, response, xhr) { - _.each(packageModel.get("members"), function (solrResult, i) { - var entityName = ""; + success(parsedMetadata, response, xhr) { + _.each(packageModel.get("members"), (solrResult, i) => { + let entityName = ""; if (solrResult.get("formatType") == "METADATA") entityName = solrResult.get("title"); - var container = viewRef.findEntityDetailsContainer( + const container = viewRef.findEntityDetailsContainer( solrResult, parsedMetadata, ); if (container) entityName = viewRef.getEntityName(container); - //Set the entity name + // Set the entity name if (entityName) { solrResult.set("fileName", entityName); - //Update the UI with the new name + // Update the UI with the new name viewRef .$( - ".entity-name-placeholder[data-id='" + - solrResult.get("id") + - "']", + `.entity-name-placeholder[data-id='${solrResult.get( + "id", + )}']`, ) .text(entityName); } @@ -2395,8 +2333,8 @@ define([ return; } - _.each(packageModel.get("members"), function (solrResult, i) { - var entityName = ""; + _.each(packageModel.get("members"), (solrResult, i) => { + let entityName = ""; if (solrResult.get("fileName")) entityName = solrResult.get("fileName"); @@ -2404,23 +2342,23 @@ define([ entityName = solrResult.get("title"); else if (solrResult.get("formatType") == "RESOURCE") return; else { - var container = viewRef.findEntityDetailsContainer(solrResult); + const container = viewRef.findEntityDetailsContainer(solrResult); if (container && container.length > 0) entityName = viewRef.getEntityName(container); else entityName = null; } - //Set the entityName, even if it's null + // Set the entityName, even if it's null solrResult.set("fileName", entityName); }); }); }, - getEntityName: function (containerEl) { + getEntityName(containerEl) { if (!containerEl) return false; - var entityName = $(containerEl) + let entityName = $(containerEl) .find(".entityName") .attr("data-entity-name"); if (typeof entityName === "undefined" || !entityName) { @@ -2434,26 +2372,25 @@ define([ return entityName; }, - //Checks if the metadata has entity details sections - hasEntityDetails: function () { + // Checks if the metadata has entity details sections + hasEntityDetails() { return this.$(".entitydetails").length > 0; }, /** * 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. - * @return {Element} - The DOM element that describbbes the given data entity. + * @returns {Element} - The DOM element that describbbes the given data entity. */ - findEntityDetailsContainer: function (model, el) { - if (!el) var el = this.el; + findEntityDetailsContainer(model, el) { + if (!el) var { el } = this; - //Get the id and file name for this data object - var id = "", - fileName = ""; + // Get the id and file name for this data object + let id = ""; + let fileName = ""; - //If a model is given, get the id and file name from the object + // If a model is given, get the id and file name from the object if ( model && (DataONEObject.prototype.isPrototypeOf(model) || @@ -2462,64 +2399,68 @@ define([ id = model.get("id"); fileName = model.get("fileName"); } - //If a string is given instead, it must be the id of the data object - else if (typeof model == "string") { + // If a string is given instead, it must be the id of the data object + else if (typeof model === "string") { id = model; } - //Otherwise, there isn't enough info to find the element, so exit + // Otherwise, there isn't enough info to find the element, so exit else { return; } - //If we already found it earlier, return it now - var container = this.$( - ".entitydetails[data-id='" + - id + - "'], " + - ".entitydetails[data-id='" + - DataONEObject.prototype.getXMLSafeID(id) + - "']", + // If we already found it earlier, return it now + let container = this.$( + `.entitydetails[data-id='${id}'], ` + + `.entitydetails[data-id='${DataONEObject.prototype.getXMLSafeID( + id, + )}']`, ); - if (container.length) return container; + if (container.length) { + // Store the PID on this element for moreInfo icons + this.storeEntityPIDs(container, id); - //Are we looking for the main object that this MetadataView is displaying? + return container; + } + + // Are we looking for the main object that this MetadataView is displaying? if (id == this.pid) { if (this.$("#Metadata").length > 0) return this.$("#Metadata"); - else return this.el; + return this.el; } - //Metacat 2.4.2 and up will have the Online Distribution Link marked - var link = this.$(".entitydetails a[data-pid='" + id + "']"); + // Metacat 2.4.2 and up will have the Online Distribution Link marked + let link = this.$(`.entitydetails a[data-pid='${id}']`); - //Otherwise, try looking for an anchor with the id matching this object's id + // Otherwise, try looking for an anchor with the id matching this object's id if (!link.length) - link = $(el).find("a#" + id.replace(/[^A-Za-z0-9]/g, "\\$&")); + link = $(el).find(`a#${id.replace(/[^A-Za-z0-9]/g, "\\$&")}`); - //Get metadata index view - var metadataFromIndex = _.findWhere(this.subviews, { + // Get metadata index view + let metadataFromIndex = _.findWhere(this.subviews, { type: "MetadataIndex", }); if (typeof metadataFromIndex === "undefined") metadataFromIndex = null; - //Otherwise, find the Online Distribution Link the hard way + // Otherwise, find the Online Distribution Link the hard way if (link.length < 1 && !metadataFromIndex) link = $(el).find( - ".control-label:contains('Online Distribution Info') + .controls-well > a[href*='" + - id.replace(/[^A-Za-z0-9]/g, "\\$&") + - "']", + `.control-label:contains('Online Distribution Info') + .controls-well > a[href*='${id.replace( + /[^A-Za-z0-9]/g, + "\\$&", + )}']`, ); if (link.length > 0) { - //Get the container element + // Get the container element container = $(link).parents(".entitydetails"); if (container.length < 1) { - //backup - find the parent of this link that is a direct child of the form element - var firstLevelContainer = _.intersection( + // backup - find the parent of this link that is a direct child of the form element + const firstLevelContainer = _.intersection( $(link).parents("form").children(), $(link).parents(), ); - //Find the controls-well inside of that first level container, which is the well that contains info about this data object + // Find the controls-well inside of that first level container, which is the well that contains info about this data object if (firstLevelContainer.length > 0) container = $(firstLevelContainer).children(".controls-well"); @@ -2529,18 +2470,21 @@ define([ $(container).addClass("entitydetails"); } - //Add the id so we can easily find it later + // Add the id so we can easily find it later container.attr("data-id", id); + // Store the PID on this element for moreInfo icons + this.storeEntityPIDs(container, id); + return container; } - //----Find by file name rather than id----- + // ----Find by file name rather than id----- if (!fileName) { - //Get the name of the object first + // 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: id, + id, }); if (model) { fileName = model.get("fileName"); @@ -2550,43 +2494,48 @@ define([ } if (fileName) { - var possibleLocations = [ - ".entitydetails [data-object-name='" + fileName + "']", - ".entitydetails .control-label:contains('Object Name') + .controls-well:contains('" + - fileName + - "')", - ".entitydetails .control-label:contains('Entity Name') + .controls-well:contains('" + - fileName + - "')", + const possibleLocations = [ + `.entitydetails [data-object-name='${fileName}']`, + `.entitydetails .control-label:contains('Object Name') + .controls-well:contains('${fileName}')`, + `.entitydetails .control-label:contains('Entity Name') + .controls-well:contains('${fileName}')`, ]; - //Search through each possible location in the DOM where the file name might be + // Search through each possible location in the DOM where the file name might be for (var i = 0; i < possibleLocations.length; i++) { - //Get the elements in this view that match the possible location - var matches = this.$(possibleLocations[i]); + // Get the elements in this view that match the possible location + const matches = this.$(possibleLocations[i]); - //If exactly one match is found + // If exactly one match is found if (matches.length == 1) { - //Get the entity details parent element + // Get the entity details parent element container = $(matches).parents(".entitydetails").first(); - //Set the object ID on the element for easier locating later + // Set the object ID on the element for easier locating later container.attr("data-id", id); if (container.length) break; } } - if (container.length) return container; + if (container.length) { + // Store the PID on this element for moreInfo icons + this.storeEntityPIDs(container, id); + + return container; + } } - //--- The last option:---- - //If this package has only one item, we can assume the only entity details are about that item - var members = this.packageModels[0].get("members"), - dataMembers = _.filter(members, function (m) { - return m.get("formatType") == "DATA"; - }); + // --- The last option:---- + // If this package has only one item, we can assume the only entity details are about that item + const members = this.packageModels[0].get("members"); + const dataMembers = _.filter( + members, + (m) => m.get("formatType") == "DATA", + ); 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); + return this.$(".entitydetails"); } } @@ -2597,79 +2546,81 @@ 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. */ - insertDataDetails: function () { - //If there is a metadataIndex subview, render from there. - var metadataFromIndex = _.findWhere(this.subviews, { + insertDataDetails() { + // If there is a metadataIndex subview, render from there. + const metadataFromIndex = _.findWhere(this.subviews, { type: "MetadataIndex", }); if (typeof metadataFromIndex !== "undefined") { - _.each(this.packageModels, function (packageModel) { + _.each(this.packageModels, (packageModel) => { metadataFromIndex.insertDataDetails(packageModel); }); return; } - var viewRef = this; + const viewRef = this; - _.each(this.packageModels, function (packageModel) { - var dataDisplay = "", - images = [], - other = [], - packageMembers = packageModel.get("members"); + _.each(this.packageModels, (packageModel) => { + const dataDisplay = ""; + const images = []; + const other = []; + const packageMembers = packageModel.get("members"); - //Don't do this for large packages + // 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, function (solrResult, i) { - //Don't display any info about nested packages + //= === Loop over each visual object and create a dataDisplay template for it to attach to the DOM ==== + _.each(packageMembers, (solrResult, i) => { + // Don't display any info about nested packages if (solrResult.type == "Package") return; - var objID = solrResult.get("id"); + const objID = solrResult.get("id"); if (objID == viewRef.pid) return; - //Is this a visual object (image)? - var type = + // Is this a visual object (image)? + const type = solrResult.type == "SolrResult" ? solrResult.getType() : "Data set"; if (type == "image") images.push(solrResult); - //Find the part of the HTML Metadata view that describes this data object - var anchor = $(document.createElement("a")).attr( - "id", - objID.replace(/[^A-Za-z0-9]/g, "-"), - ), - container = viewRef.findEntityDetailsContainer(objID); + // Find the part of the HTML Metadata view that describes this data object + const anchor = $(document.createElement("a")).attr( + "id", + objID.replace(/[^A-Za-z0-9]/g, "-"), + ); + const container = viewRef.findEntityDetailsContainer(objID); - var downloadButton = new DownloadButtonView({ model: solrResult }); + const downloadButton = new DownloadButtonView({ + model: solrResult, + }); downloadButton.render(); - //Insert the data display HTML and the anchor tag to mark this spot on the page + // 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 + // Only show data displays for images hosted on the same origin if (type == "image") { - //Create the data display HTML - var dataDisplay = $.parseHTML( + // Create the data display HTML + const dataDisplay = $.parseHTML( viewRef .dataDisplayTemplate({ - type: type, + type, src: solrResult.get("url"), - objID: objID, + objID, }) .trim(), ); - //Insert into the page + // Insert into the page if ($(container).children("label").length > 0) $(container).children("label").first().after(dataDisplay); else $(container).prepend(dataDisplay); - //If this image is private, we need to load it via an XHR request + // If this image is private, we need to load it via an XHR request if (!solrResult.get("isPublic")) { - //Create an XHR - var xhr = new XMLHttpRequest(); + // Create an XHR + const xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.onload = function () { @@ -2679,12 +2630,12 @@ define([ .attr("src", window.URL.createObjectURL(xhr.response)); }; - //Open and send the request with the user's auth token + // Open and send the request with the user's auth token xhr.open("GET", solrResult.get("url")); xhr.responseType = "blob"; xhr.setRequestHeader( "Authorization", - "Bearer " + MetacatUI.appUserModel.get("token"), + `Bearer ${MetacatUI.appUserModel.get("token")}`, ); xhr.send(); } @@ -2692,7 +2643,7 @@ define([ $(container).prepend(anchor); - var nameLabel = $(container).find( + const nameLabel = $(container).find( "label:contains('Entity Name')", ); if (nameLabel.length) { @@ -2701,65 +2652,60 @@ define([ } }); - //==== Initialize the fancybox images ===== + //= === 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. - var numImages = images.length, - //The shared lightbox options for both images - lightboxOptions = { - prevEffect: "elastic", - nextEffect: "elastic", - closeEffect: "elastic", - openEffect: "elastic", - aspectRatio: true, - closeClick: true, - afterLoad: function () { - //Create a custom HTML caption based on data stored in the DOM element - viewRef.title = - viewRef.title + - "
      Download "; - }, - helpers: { - title: { - type: "outside", - }, + const numImages = images.length; + // The shared lightbox options for both images + const lightboxOptions = { + prevEffect: "elastic", + nextEffect: "elastic", + closeEffect: "elastic", + openEffect: "elastic", + aspectRatio: true, + closeClick: true, + afterLoad() { + // Create a custom HTML caption based on data stored in the DOM element + viewRef.title = `${viewRef.title} Download `; + }, + helpers: { + title: { + type: "outside", }, - }; + }, + }; if (numImages > 0) { - var numImgChecks = 0, //Keep track of how many interval checks we have so we don't wait forever for images to load - lightboxImgSelector = - "a[class^='fancybox'][data-fancybox-type='image']"; + let numImgChecks = 0; // Keep track of how many interval checks we have so we don't wait forever for images to load + const lightboxImgSelector = + "a[class^='fancybox'][data-fancybox-type='image']"; - //Add additional options for images - var imgLightboxOptions = lightboxOptions; + // Add additional options for images + const imgLightboxOptions = lightboxOptions; imgLightboxOptions.type = "image"; imgLightboxOptions.perload = 1; - var initializeImgLightboxes = function () { + const initializeImgLightboxes = function () { numImgChecks++; - //Initialize what images have loaded so far after 5 seconds + // Initialize what images have loaded so far after 5 seconds if (numImgChecks == 10) { $(lightboxImgSelector).fancybox(imgLightboxOptions); } - //When 15 seconds have passed, stop checking so we don't blow up the browser + // 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); return; } - //Are all of our images loaded yet? + // Are all of our images loaded yet? if (viewRef.$(lightboxImgSelector).length < numImages) return; - else { - //Initialize our lightboxes - $(lightboxImgSelector).fancybox(imgLightboxOptions); - //We're done - clear the interval - window.clearInterval(imgIntervalID); - } + // Initialize our lightboxes + $(lightboxImgSelector).fancybox(imgLightboxOptions); + + // We're done - clear the interval + window.clearInterval(imgIntervalID); }; var imgIntervalID = window.setInterval( @@ -2770,22 +2716,22 @@ define([ }); }, - replaceEcoGridLinks: function () { - var viewRef = this; + replaceEcoGridLinks() { + const viewRef = this; - //Find the element in the DOM housing the ecogrid link - $("a:contains('ecogrid://')").each(function (i, thisLink) { - //Get the link text - var linkText = $(thisLink).text(); + // Find the element in the DOM housing the ecogrid link + $("a:contains('ecogrid://')").each((i, thisLink) => { + // Get the link text + const linkText = $(thisLink).text(); - //Clean up the link text - var withoutPrefix = linkText.substring( - linkText.indexOf("ecogrid://") + 10, - ), - pid = withoutPrefix.substring(withoutPrefix.indexOf("/") + 1), - baseUrl = - MetacatUI.appModel.get("resolveServiceUrl") || - MetacatUI.appModel.get("objectServiceUrl"); + // Clean up the link text + const withoutPrefix = linkText.substring( + linkText.indexOf("ecogrid://") + 10, + ); + const pid = withoutPrefix.substring(withoutPrefix.indexOf("/") + 1); + const baseUrl = + MetacatUI.appModel.get("resolveServiceUrl") || + MetacatUI.appModel.get("objectServiceUrl"); $(thisLink) .attr("href", baseUrl + encodeURIComponent(pid)) @@ -2793,68 +2739,65 @@ define([ }); }, - publish: function (event) { + publish(event) { // target may not actually prevent click events, so double check - var disabled = $(event.target).closest("a").attr("disabled"); + const disabled = $(event.target).closest("a").attr("disabled"); if (disabled) { return false; } - var publishServiceUrl = MetacatUI.appModel.get("publishServiceUrl"); - var pid = $(event.target).closest("a").attr("pid"); - var ret = confirm( - "Are you sure you want to publish " + pid + " with a DOI?", + const publishServiceUrl = MetacatUI.appModel.get("publishServiceUrl"); + const pid = $(event.target).closest("a").attr("pid"); + const ret = confirm( + `Are you sure you want to publish ${pid} with a DOI?`, ); if (ret) { // show the loading icon - var message = "Publishing package...this may take a few moments"; + const message = "Publishing package...this may take a few moments"; this.showLoading(message); - var identifier = null; - var viewRef = this; - var requestSettings = { + let identifier = null; + const viewRef = this; + const requestSettings = { url: publishServiceUrl + pid, type: "PUT", xhrFields: { withCredentials: true, }, - success: function (data, textStatus, xhr) { + success(data, textStatus, xhr) { // the response should have new identifier in it identifier = $(data).find("d1\\:identifier, identifier").text(); if (identifier) { viewRef.hideLoading(); - var msg = - "Published data package '" + - identifier + - "'. If you are not redirected soon, you can view your published data package here"; + const msg = `Published data package '${identifier}'. If you are not redirected soon, you can view your published data package here`; viewRef.$el.find(".container").prepend( viewRef.alertTemplate({ - msg: msg, + msg, classes: "alert-success", }), ); // navigate to the new view after a few seconds - setTimeout(function () { + setTimeout(() => { // avoid a double fade out/in viewRef.$el.html(""); viewRef.showLoading(); - MetacatUI.uiRouter.navigate("view/" + identifier, { + MetacatUI.uiRouter.navigate(`view/${identifier}`, { trigger: true, }); }, 3000); } }, - error: function (xhr, textStatus, errorThrown) { + error(xhr, textStatus, errorThrown) { // show the error message, but stay on the same page - var msg = - "Publish failed: " + - $(xhr.responseText).find("description").text(); + const msg = `Publish failed: ${$(xhr.responseText) + .find("description") + .text()}`; viewRef.hideLoading(); viewRef.showError(msg); @@ -2870,25 +2813,25 @@ define([ } }, - //When the given ID from the URL is a resource map that has no metadata, do the following... - noMetadata: function (solrResultModel) { + // When the given ID from the URL is a resource map that has no metadata, do the following... + noMetadata(solrResultModel) { this.hideLoading(); this.$el.html(this.template()); this.pid = solrResultModel.get("resourceMap") || solrResultModel.get("id"); - //Insert breadcrumbs + // Insert breadcrumbs this.insertBreadcrumbs(); this.insertDataSource(); - //Insert a table of contents + // Insert a table of contents this.insertPackageTable(solrResultModel); this.renderMetadataFromIndex(); - //Insert a message that this data is not described by metadata + // Insert a message that this data is not described by metadata MetacatUI.appView.showAlert( "Additional information about this data is limited since metadata was not provided by the creator.", "alert-warning", @@ -2897,19 +2840,19 @@ define([ }, // this will lookup the latest version of the PID - showLatestVersion: function () { - //If this metadata doc is not obsoleted by a new version, then exit the function + showLatestVersion() { + // If this metadata doc is not obsoleted by a new version, then exit the function if (!this.model.get("obsoletedBy")) { return; } - var view = this; + const view = this; - //When the latest version is found, - this.listenTo(this.model, "change:newestVersion", function () { - //Make sure it has a newer version, and if so, + // 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")) { - //Put a link to the newest version in the content + // Put a link to the newest version in the content view.$(".newer-version").replaceWith( view.versionTemplate({ pid: view.model.get("newestVersion"), @@ -2920,23 +2863,23 @@ define([ } }); - //Insert the newest version template with a loading message + // Insert the newest version template with a loading message this.$el.prepend( this.versionTemplate({ loading: true, }), ); - //Find the latest version of this metadata object + // Find the latest version of this metadata object this.model.findLatestVersion(); }, - showLoading: function (message) { + showLoading(message) { this.hideLoading(); MetacatUI.appView.scrollToTop(); - var loading = this.loadingTemplate({ msg: message }); + const loading = this.loadingTemplate({ msg: message }); if (!loading) return; this.$loading = $($.parseHTML(loading)); @@ -2945,18 +2888,18 @@ define([ this.$el.html(loading); }, - hideLoading: function () { + hideLoading() { if (this.$loading) this.$loading.remove(); if (this.$detached) this.$el.html(this.$detached); }, - showError: function (msg) { - //Remove any existing error messages + showError(msg) { + // Remove any existing error messages this.$el.children(".alert-container").remove(); this.$el.prepend( this.alertTemplate({ - msg: msg, + msg, classes: "alert-error", containerClasses: "page", includeEmail: true, @@ -2968,18 +2911,19 @@ 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) - **/ - previewData: function (e) { - //Don't go anywhere yet... + * @param e + */ + previewData(e) { + // Don't go anywhere yet... e.preventDefault(); - //Get the target and id of the click - var link = $(e.target); + // Get the target and id of the click + 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 + if (typeof id === "undefined" || !id) return false; // This will make the app defualt to the child view previewData function } else return false; // If we are on the Metadata view, update the URL and scroll to the @@ -2998,18 +2942,18 @@ define([ * element on the page set to an XML-safe version of the value in the * fragment/hash. Used to provide direct links to sub-resources on a page. */ - scrollToFragment: function () { - var hash = window.location.hash; + scrollToFragment() { + const { hash } = window.location; if (!hash || hash.length <= 1) { return; } - //Get the id from the URL hash and decode it - var idFragment = decodeURIComponent(hash.substring(1)); + // Get the id from the URL hash and decode it + const idFragment = decodeURIComponent(hash.substring(1)); - //Find the corresponding entity details section for this id - var entityDetailsEl = this.findEntityDetailsContainer(idFragment); + // Find the corresponding entity details section for this id + const entityDetailsEl = this.findEntityDetailsContainer(idFragment); if (entityDetailsEl || entityDetailsEl.length) { MetacatUI.appView.scrollTo(entityDetailsEl); @@ -3023,25 +2967,24 @@ 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. */ - navigateWithFragment: function (metadata_pid, data_pid) { - var next_route = "view/" + encodeURIComponent(metadata_pid); + navigateWithFragment(metadata_pid, data_pid) { + let next_route = `view/${encodeURIComponent(metadata_pid)}`; if (typeof data_pid === "string" && data_pid.length > 0) { - next_route += "#" + encodeURIComponent(data_pid); + next_route += `#${encodeURIComponent(data_pid)}`; } MetacatUI.uiRouter.navigate(next_route, { trigger: true }); }, - closePopovers: function (e) { - //If this is a popover element or an element that has a popover, don't close anything. - //Check with the .classList attribute to account for SVG elements - var svg = $(e.target).parents("svg"); + closePopovers(e) { + // If this is a popover element or an element that has a popover, don't close anything. + // Check with the .classList attribute to account for SVG elements + const svg = $(e.target).parents("svg"); if ( _.contains(e.target.classList, "popover-this") || @@ -3052,38 +2995,38 @@ define([ ) return; - //Close all active popovers + // Close all active popovers this.$(".popover-this.active").popover("hide"); }, - highlightNode: function (e) { - //Find the id - var id = $(e.target).attr("data-id"); + highlightNode(e) { + // Find the id + let id = $(e.target).attr("data-id"); if (typeof id === "undefined" || !id) id = $(e.target).parents("[data-id]").attr("data-id"); - //If there is no id, return + // If there is no id, return if (typeof id === "undefined") return false; - //Highlight its node - $(".prov-chart .node[data-id='" + id + "']").toggleClass("active"); + // Highlight its node + $(`.prov-chart .node[data-id='${id}']`).toggleClass("active"); - //Highlight its metadata section + // Highlight its metadata section if (MetacatUI.appModel.get("pid") == id) this.$("#Metadata").toggleClass("active"); else { - var entityDetails = this.findEntityDetailsContainer(id); + const entityDetails = this.findEntityDetailsContainer(id); if (entityDetails) entityDetails.toggleClass("active"); } }, - onClose: function () { - var viewRef = this; + onClose() { + const viewRef = this; this.stopListening(); - _.each(this.subviews, function (subview) { + _.each(this.subviews, (subview) => { if (subview.onClose) subview.onClose(); }); @@ -3095,10 +3038,10 @@ define([ this.$detached = null; this.$loading = null; - //Put the document title back to the default + // Put the document title back to the default MetacatUI.appModel.resetTitle(); - //Remove view-specific classes + // Remove view-specific classes this.$el.removeClass("container no-stylesheet"); this.$el.empty(); @@ -3109,18 +3052,19 @@ define([ * a dataset citation from the value stored in the underlying model's * origin field. */ - getAuthorText: function () { - var authors = this.model.get("origin"), - count = 0, - authorText = ""; + getAuthorText() { + const authors = this.model.get("origin"); + let count = 0; + let authorText = ""; - _.each(authors, function (author) { + _.each(authors, (author) => { count++; if (count == 6) { authorText += ", et al. "; return; - } else if (count > 6) { + } + if (count > 6) { return; } @@ -3149,29 +3093,27 @@ define([ * dataset citation. This method falls back to the node ID when the proper * node name cannot be fetched from the app's NodeModel instance. */ - getPublisherText: function () { - var datasource = this.model.get("datasource"), - memberNode = MetacatUI.nodeModel.getMember(datasource); + getPublisherText() { + const datasource = this.model.get("datasource"); + const memberNode = MetacatUI.nodeModel.getMember(datasource); if (memberNode) { return memberNode.name; - } else { - return datasource; } + return datasource; }, /** * Generate a string appropriate to be used as the publication date in a * dataset citation. */ - getDatePublishedText: function () { + getDatePublishedText() { // Dataset/datePublished // Prefer pubDate, fall back to dateUploaded so we have something to show if (this.model.get("pubDate") !== "") { return this.model.get("pubDate"); - } else { - return this.model.get("dateUploaded"); } + return this.model.get("dateUploaded"); }, /** @@ -3181,27 +3123,27 @@ define([ * Note: `insertJSONLD` should be called to do the actual inserting into the * DOM. */ - generateJSONLD: function () { - var model = this.model; + generateJSONLD() { + const { model } = this; // Determine the path (either #view or view, depending on router // configuration) for use in the 'url' property - var href = document.location.href, - route = href - .replace(document.location.origin + "/", "") - .split("/")[0]; + 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. - var elJSON = { + const elJSON = { "@context": { "@vocab": "https://schema.org/", }, "@type": "Dataset", - "@id": - "https://dataone.org/datasets/" + - encodeURIComponent(model.get("id")), + "@id": `https://dataone.org/datasets/${encodeURIComponent( + model.get("id"), + )}`, datePublished: this.getDatePublishedText(), dateModified: model.get("dateModified"), publisher: { @@ -3210,9 +3152,9 @@ define([ }, identifier: this.generateSchemaOrgIdentifier(model.get("id")), version: model.get("version"), - url: - "https://dataone.org/datasets/" + - encodeURIComponent(model.get("id")), + url: `https://dataone.org/datasets/${encodeURIComponent( + model.get("id"), + )}`, schemaVersion: model.get("formatId"), isAccessibleForFree: true, }; @@ -3220,10 +3162,10 @@ define([ // Attempt to add in a sameAs property of we have high confidence the // identifier is a DOI if (this.model.isDOI(model.get("id"))) { - var doi = this.getCanonicalDOIIRI(model.get("id")); + const doi = this.getCanonicalDOIIRI(model.get("id")); if (doi) { - elJSON["sameAs"] = doi; + elJSON.sameAs = doi; } } @@ -3231,17 +3173,15 @@ define([ // Name if (model.get("title")) { - elJSON["name"] = model.get("title"); + elJSON.name = model.get("title"); } // Creator if (model.get("origin")) { - elJSON["creator"] = model.get("origin").map(function (creator) { - return { - "@type": "Person", - name: creator, - }; - }); + elJSON.creator = model.get("origin").map((creator) => ({ + "@type": "Person", + name: creator, + })); } // Dataset/spatialCoverage @@ -3251,7 +3191,7 @@ define([ model.get("southBoundCoord") && model.get("westBoundCoord") ) { - var spatialCoverage = { + const spatialCoverage = { "@type": "Place", additionalProperty: [ { @@ -3287,8 +3227,7 @@ define([ if (model.get("beginDate") && !model.get("endDate")) { elJSON.temporalCoverage = model.get("beginDate"); } else if (model.get("beginDate") && model.get("endDate")) { - elJSON.temporalCoverage = - model.get("beginDate") + "/" + model.get("endDate"); + elJSON.temporalCoverage = `${model.get("beginDate")}/${model.get("endDate")}`; } // Dataset/variableMeasured @@ -3300,13 +3239,10 @@ define([ if (model.get("abstract")) { elJSON.description = model.get("abstract"); } else { - var datasets_url = - "https://dataone.org/datasets/" + - encodeURIComponent(model.get("id")); - elJSON.description = - "No description is available. Visit " + - datasets_url + - " for complete metadata about this dataset."; + const datasets_url = `https://dataone.org/datasets/${encodeURIComponent( + model.get("id"), + )}`; + elJSON.description = `No description is available. Visit ${datasets_url} for complete metadata about this dataset.`; } // Dataset/keywords @@ -3320,7 +3256,6 @@ define([ /** * Insert Schema.org-compliant JSONLD for the model bound to the view into * the head tag of the page (at the end). - * * @param {object} json - JSON-LD to insert into the page * * Some notes: @@ -3329,15 +3264,15 @@ define([ * - If not create a new script tag and append otherwise replace the text * for the script */ - insertJSONLD: function (json) { + insertJSONLD(json) { if (!document.getElementById("jsonld")) { - var el = document.createElement("script"); + const el = document.createElement("script"); el.type = "application/ld+json"; el.id = "jsonld"; el.text = JSON.stringify(json); document.querySelector("head").appendChild(el); } else { - var script = document.getElementById("jsonld"); + const script = document.getElementById("jsonld"); script.text = JSON.stringify(json); } }, @@ -3347,15 +3282,14 @@ 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 */ - generateSchemaOrgIdentifier: function (identifier) { + generateSchemaOrgIdentifier(identifier) { if (!this.model.isDOI()) { return identifier; } - var doi = this.getCanonicalDOIIRI(identifier); + const doi = this.getCanonicalDOIIRI(identifier); if (!doi) { return identifier; @@ -3374,20 +3308,23 @@ 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 */ - generateSchemaOrgGeo: function (north, east, south, west) { + generateSchemaOrgGeo(north, east, south, west) { if (north === south) { return { "@type": "GeoCoordinates", latitude: north, longitude: west, }; - } else { - return { - "@type": "GeoShape", - box: west + ", " + south + " " + east + ", " + north, - }; } + return { + "@type": "GeoShape", + box: `${west}, ${south} ${east}, ${north}`, + }; }, /** @@ -3405,46 +3342,41 @@ define([ * * e.g., if the east bounding coordinate is 120 W and west bounding * coordinate is 140 E, geoJSON requires we specify 140 E as 220 - * * @param {number} north - North bounding coordinate * @param {number} east - East bounding coordinate * @param {number} south - South bounding coordinate * @param {number} west - West bounding coordinate */ - generateGeoJSONString: function (north, east, south, west) { + generateGeoJSONString(north, east, south, west) { if (north === south) { return this.generateGeoJSONPoint(north, east); - } else { - return this.generateGeoJSONPolygon(north, east, south, west); } + return this.generateGeoJSONPolygon(north, east, south, west); }, /** - * Generate a GeoJSON Point object - * - * @param {number} north - North bounding coordinate - * @param {number} east - East bounding coordinate - * - * Example: - * { - * "type": "Point", - * "coordinates": [ - * -105.01621, - * 39.57422 - * ]} - - */ - generateGeoJSONPoint: function (north, east) { - var preamble = '{"type":"Point","coordinates":', - inner = "[" + east + "," + north + "]", - postamble = "}"; + * Generate a GeoJSON Point object + * @param {number} north - North bounding coordinate + * @param {number} east - East bounding coordinate + * + * Example: + * { + * "type": "Point", + * "coordinates": [ + * -105.01621, + * 39.57422 + * ]} + */ + generateGeoJSONPoint(north, east) { + const preamble = '{"type":"Point","coordinates":'; + const inner = `[${east},${north}]`; + const postamble = "}"; return preamble + inner + postamble; }, /** * Generate a GeoJSON Polygon object from - * * @param {number} north - North bounding coordinate * @param {number} east - East bounding coordinate * @param {number} south - South bounding coordinate @@ -3462,10 +3394,9 @@ define([ * [ 100, 1 ], * [ 100, 0 ] * ]} - * */ - generateGeoJSONPolygon: function (north, east, south, west) { - var preamble = + generateGeoJSONPolygon(north, east, south, west) { + const preamble = '{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[['; // Handle the case when the polygon wraps across the 180W/180E boundary @@ -3473,44 +3404,24 @@ define([ east = 360 - east; } - var inner = - "[" + - west + - "," + - south + - "]," + - "[" + - east + - "," + - south + - "]," + - "[" + - east + - "," + - north + - "]," + - "[" + - west + - "," + - north + - "]," + - "[" + - west + - "," + - south + - "]"; - - var postamble = "]]}}"; + const inner = + `[${west},${south}],` + + `[${east},${south}],` + + `[${east},${north}],` + + `[${west},${north}],` + + `[${west},${south}]`; + + const postamble = "]]}}"; return preamble + inner + postamble; }, /** * Create a canonical IRI for a DOI given a random DataONE identifier. - * * @param {string} identifier: The identifier to (possibly) create the IRI * for. - * @return {string|null} Returns null when matching the identifier to a DOI + * @param identifier + * @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 @@ -3518,7 +3429,7 @@ define([ * * Note: Really could be generalized to more identifier schemes. */ - getCanonicalDOIIRI: function (identifier) { + getCanonicalDOIIRI(identifier) { return MetacatUI.appModel.DOItoURL(identifier) || null; }, @@ -3528,26 +3439,26 @@ define([ * Currently supports Highwire Press style tags (citation_) which is * supposedly what Google (Scholar), Mendeley, and Zotero support. */ - insertCitationMetaTags: function () { + insertCitationMetaTags() { // Generate template data to use for all templates - var title = this.model.get("title"), - authors = this.model.get("origin"), - publisher = this.getPublisherText(), - date = new Date(this.getDatePublishedText()) - .getUTCFullYear() - .toString(), - isDOI = this.model.isDOI(this.model.get("id")), - id = this.model.get("id"), - abstract = this.model.get("abstract"); + const title = this.model.get("title"); + const authors = this.model.get("origin"); + const publisher = this.getPublisherText(); + const date = new Date(this.getDatePublishedText()) + .getUTCFullYear() + .toString(); + const isDOI = this.model.isDOI(this.model.get("id")); + const id = this.model.get("id"); + const abstract = this.model.get("abstract"); // Generate HTML strings from each template - var hwpt = this.metaTagsHighwirePressTemplate({ - title: title, - authors: authors, - publisher: publisher, - date: date, - isDOI: isDOI, - id: id, + const hwpt = this.metaTagsHighwirePressTemplate({ + title, + authors, + publisher, + date, + isDOI, + id, abstract, }); @@ -3573,12 +3484,12 @@ define([ ); }, - createAnnotationViews: function () { + createAnnotationViews() { try { - var viewRef = this; + const viewRef = this; - _.each($(".annotation"), function (annoEl) { - var newView = new AnnotationView({ + _.each($(".annotation"), (annoEl) => { + const newView = new AnnotationView({ el: annoEl, }); viewRef.subviews.push(newView); @@ -3588,11 +3499,11 @@ define([ } }, - insertMarkdownViews: function () { - var viewRef = this; + insertMarkdownViews() { + const viewRef = this; - _.each($(".markdown"), function (markdownEl) { - var newView = new MarkdownView({ + _.each($(".markdown"), (markdownEl) => { + const newView = new MarkdownView({ markdown: $(markdownEl).text().trim(), el: $(markdownEl).parent(), }); @@ -3606,12 +3517,26 @@ define([ }); }, - storeEntityPIDs: function (responseEl) { - var view = this; - _.each($(responseEl).find(".entitydetails"), function (entityEl) { - var entityId = $(entityEl).data("id"); - view.entities.push(entityId.replace("urn-uuid-", "urn:uuid:")); - }); + storeEntityPIDs(entityEl, entityId) { + // Get the entity ID if it is null or undefined + if (entityId == null) entityId = $(entityEl).data("id"); + + // Perform clean up with the entity ID + if (entityId & (typeof entityId === "string")) { + // Check and replace urn-uuid- with urn:uuid: if the string starts with urn-uuid- + if (entityId.startsWith("urn-uuid-")) { + entityId = entityId.replace("urn-uuid-", "urn:uuid:"); + } + + // Check and replace doi-10. with doi:10. if the string starts with doi-10. + if (entityId.startsWith("doi-10.")) { + entityId = entityId.replace("doi-10.", "doi:10."); + } + } + + if (!this.entities.includes(entityId)) { + this.entities.push(entityId); + } }, }, ); From b4d877d43e7db9f0205cadfa58ee07bdaf7d4f13 Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Thu, 29 Aug 2024 14:18:36 -0700 Subject: [PATCH 3/6] Fix ES Linting errors for DataONEObject & Package model Fix ES Linting errors for DataONEObject & Package model Ref: #2483 --- src/js/models/DataONEObject.js | 1418 ++++++++++++++++---------------- src/js/models/PackageModel.js | 1237 ++++++++++++++-------------- 2 files changed, 1300 insertions(+), 1355 deletions(-) diff --git a/src/js/models/DataONEObject.js b/src/js/models/DataONEObject.js index 58b37eaf0..341fd33c6 100644 --- a/src/js/models/DataONEObject.js +++ b/src/js/models/DataONEObject.js @@ -8,28 +8,18 @@ define([ "collections/ObjectFormats", "common/Utilities", "md5", -], function ( - $, - _, - Backbone, - uuid, - he, - AccessPolicy, - ObjectFormats, - Utilities, - md5, -) { +], ($, _, Backbone, uuid, he, AccessPolicy, ObjectFormats, Utilities, md5) => { /** - * @class DataONEObject - * @classdesc A DataONEObject represents a DataONE object, such as a data file, + * @class DataONEObject + * @classdesc A DataONEObject represents a DataONE object, such as a data file, a science metadata object, or a resource map. It stores the system metadata attributes for the object, performs updates to the system metadata, and other basic DataONE API functions. This model can be extended to provide specific functionality for different object types, such as the {@link ScienceMetadata} model and the {@link EML211} model. - * @classcategory Models - * @augments Backbone.Model - */ + * @classcategory Models + * @augments Backbone.Model + */ var DataONEObject = Backbone.Model.extend( /** @lends DataONEObject.prototype */ { type: "DataONEObject", @@ -37,7 +27,7 @@ define([ PROV: "http://www.w3.org/ns/prov#", PROVONE: "http://purl.dataone.org/provone/2015/01/15/ontology#", - defaults: function () { + defaults() { return { // System Metadata attributes serialVersion: null, @@ -49,7 +39,7 @@ define([ checksumAlgorithm: "MD5", submitter: null, rightsHolder: null, - accessPolicy: [], //An array of accessPolicy literal JS objects + accessPolicy: [], // An array of accessPolicy literal JS objects replicationAllowed: null, replicationPolicy: [], obsoletes: null, @@ -73,7 +63,7 @@ define([ readPermission: null, isPublic: null, dateModified: null, - id: "urn:uuid:" + uuid.v4(), + id: `urn:uuid:${uuid.v4()}`, sizeStr: null, type: "", // Data, Metadata, or DataPackage formatType: "", @@ -86,30 +76,30 @@ define([ nodeLevel: 0, // Indicates hierarchy level in the view for indentation sortOrder: 2, // Metadata: 1, Data: 2, DataPackage: 3 synced: false, // True if the full model has been synced - uploadStatus: null, //c=complete, p=in progress, q=queued, e=error, w=warning, no upload status=not in queue + uploadStatus: null, // c=complete, p=in progress, q=queued, e=error, w=warning, no upload status=not in queue uploadProgress: null, - sysMetaUploadStatus: null, //c=complete, p=in progress, q=queued, e=error, l=loading, no upload status=not in queue + sysMetaUploadStatus: null, // c=complete, p=in progress, q=queued, e=error, l=loading, no upload status=not in queue percentLoaded: 0, // Percent the file is read before caclculating the md5 sum uploadFile: null, // The file reference to be uploaded (JS object: File) errorMessage: null, sysMetaErrorCode: null, // The status code given when there is an error updating the system metadata numSaveAttempts: 0, - notFound: false, //Whether or not this object was found in the system + notFound: false, // Whether or not this object was found in the system originalAttrs: [], // An array of original attributes in a DataONEObject changed: false, // If any attributes have been changed, including attrs in nested objects hasContentChanges: false, // If attributes outside of originalAttrs have been changed sysMetaXML: null, // A cached original version of the fetched system metadata document objectXML: null, // A cached version of the object fetched from the server isAuthorized: null, // If the stated permission is authorized by the user - isAuthorized_read: null, //If the user has permission to read - isAuthorized_write: null, //If the user has permission to write - isAuthorized_changePermission: null, //If the user has permission to changePermission - createSeriesId: false, //If true, a seriesId will be created when this object is saved. - collections: [], //References to collections that this model is in - possibleAuthMNs: [], //A list of possible authoritative MNs of this object + isAuthorized_read: null, // If the user has permission to read + isAuthorized_write: null, // If the user has permission to write + isAuthorized_changePermission: null, // If the user has permission to changePermission + createSeriesId: false, // If true, a seriesId will be created when this object is saved. + collections: [], // References to collections that this model is in + possibleAuthMNs: [], // A list of possible authoritative MNs of this object useAltRepo: false, - isLoadingFiles: false, //Only relevant to Resource Map objects. Is true if there is at least one file still loading into the package. - numLoadingFiles: 0, //Only relevant to Resource Map objects. The number of files still loading into the package. + isLoadingFiles: false, // Only relevant to Resource Map objects. Is true if there is at least one file still loading into the package. + numLoadingFiles: 0, // Only relevant to Resource Map objects. The number of files still loading into the package. provSources: [], provDerivations: [], prov_generated: [], @@ -130,18 +120,18 @@ define([ }; }, - initialize: function (attrs, options) { - if (typeof attrs == "undefined") var attrs = {}; + initialize(attrs, options) { + if (typeof attrs === "undefined") var attrs = {}; this.set("accessPolicy", this.createAccessPolicy()); - var model = this; - this.on("change:size", function () { - var size = Utilities.bytesToSize(model.get("size")); + const model = this; + this.on("change:size", () => { + const size = Utilities.bytesToSize(model.get("size")); model.set("sizeStr", size); }); if (attrs.size) { - var size = Utilities.bytesToSize(model.get("size")); + const size = Utilities.bytesToSize(model.get("size")); model.set("sizeStr", size); } @@ -156,10 +146,10 @@ define([ this.on("successSaving", this.updateRelationships); - //Save a reference to this DataONEObject model in the metadataEntity model - //whenever the metadataEntity is set + // Save a reference to this DataONEObject model in the metadataEntity model + // whenever the metadataEntity is set this.on("change:metadataEntity", function () { - var entityMetadataModel = this.get("metadataEntity"); + const entityMetadataModel = this.get("metadataEntity"); if (entityMetadataModel) entityMetadataModel.set("dataONEObject", this); @@ -169,8 +159,8 @@ define([ this.set("synced", true); }); - //Find Member Node object that might be the authoritative MN - //This is helpful when MetacatUI may be displaying content from multiple MNs + // Find Member Node object that might be the authoritative MN + // This is helpful when MetacatUI may be displaying content from multiple MNs this.setPossibleAuthMNs(); }, @@ -179,7 +169,7 @@ define([ * camel-cased sys meta node names (valid in DataONE). * Used during parse() and serialize() */ - nodeNameMap: function () { + nodeNameMap() { return { accesspolicy: "accessPolicy", accessrule: "accessRule", @@ -208,70 +198,67 @@ define([ * Returns the URL string where this DataONEObject can be fetched from or saved to * @returns {string} */ - url: function () { + url() { // With no id, we can't do anything if (!this.get("id") && !this.get("seriesid")) return ""; - //Get the active alternative repository, if one is configured - var activeAltRepo = MetacatUI.appModel.getActiveAltRepo(); + // Get the active alternative repository, if one is configured + const activeAltRepo = MetacatUI.appModel.getActiveAltRepo(); - //Start the base URL string - var baseUrl = ""; + // Start the base URL string + let baseUrl = ""; // Determine if we're updating a new/existing object, // or just its system metadata // New uploads use the object service URL if (this.isNew()) { - //Use the object service URL from the alt repo + // Use the object service URL from the alt repo if (this.get("useAltRepo") && activeAltRepo) { baseUrl = activeAltRepo.objectServiceUrl; } - //If this MetacatUI deployment is pointing to a MN, use the object service URL from the AppModel + // If this MetacatUI deployment is pointing to a MN, use the object service URL from the AppModel else { baseUrl = MetacatUI.appModel.get("objectServiceUrl"); } - //Return the full URL + // Return the full URL return baseUrl; - } else { - if (this.hasUpdates()) { - if (this.get("hasContentChanges")) { - //Use the object service URL from the alt repo - if (this.get("useAltRepo") && activeAltRepo) { - baseUrl = activeAltRepo.objectServiceUrl; - } else { - baseUrl = MetacatUI.appModel.get("objectServiceUrl"); - } - - // Exists on the server, use MN.update() - return baseUrl + encodeURIComponent(this.get("oldPid")); - } else { - //Use the meta service URL from the alt repo - if (this.get("useAltRepo") && activeAltRepo) { - baseUrl = activeAltRepo.metaServiceUrl; - } else { - baseUrl = MetacatUI.appModel.get("metaServiceUrl"); - } - - // Exists on the server, use MN.updateSystemMetadata() - return baseUrl + encodeURIComponent(this.get("id")); - } - } else { - //Use the meta service URL from the alt repo + } + if (this.hasUpdates()) { + if (this.get("hasContentChanges")) { + // Use the object service URL from the alt repo if (this.get("useAltRepo") && activeAltRepo) { - baseUrl = activeAltRepo.metaServiceUrl; + baseUrl = activeAltRepo.objectServiceUrl; } else { - baseUrl = MetacatUI.appModel.get("metaServiceUrl"); + baseUrl = MetacatUI.appModel.get("objectServiceUrl"); } - // Use MN.getSystemMetadata() - return ( - baseUrl + - (encodeURIComponent(this.get("id")) || - encodeURIComponent(this.get("seriesid"))) - ); + // Exists on the server, use MN.update() + return baseUrl + encodeURIComponent(this.get("oldPid")); + } + // Use the meta service URL from the alt repo + if (this.get("useAltRepo") && activeAltRepo) { + baseUrl = activeAltRepo.metaServiceUrl; + } else { + baseUrl = MetacatUI.appModel.get("metaServiceUrl"); } + + // Exists on the server, use MN.updateSystemMetadata() + return baseUrl + encodeURIComponent(this.get("id")); } + // Use the meta service URL from the alt repo + if (this.get("useAltRepo") && activeAltRepo) { + baseUrl = activeAltRepo.metaServiceUrl; + } else { + baseUrl = MetacatUI.appModel.get("metaServiceUrl"); + } + + // Use MN.getSystemMetadata() + return ( + baseUrl + + (encodeURIComponent(this.get("id")) || + encodeURIComponent(this.get("seriesid"))) + ); }, /** @@ -279,29 +266,30 @@ define([ * @returns PackageURL string for this DataONE Object * @since 2.28.0 */ - getPackageURL: function () { - var url = null; + getPackageURL() { + let url = null; // With no id, we can't do anything if (!this.get("id") && !this.get("seriesid")) return url; - //If we haven't set a packageServiceURL upon app initialization and we are querying a CN, then the packageServiceURL is dependent on the MN this package is from + // If we haven't set a packageServiceURL upon app initialization and we are querying a CN, then the packageServiceURL is dependent on the MN this package is from if ( MetacatUI.appModel.get("d1Service").toLowerCase().indexOf("cn/") > -1 && MetacatUI.nodeModel.get("members").length ) { - var source = this.get("datasource"), - node = _.find(MetacatUI.nodeModel.get("members"), { - identifier: source, - }); + const source = this.get("datasource"); + const node = _.find(MetacatUI.nodeModel.get("members"), { + identifier: source, + }); - //If this node has MNRead v2 services... + // If this node has MNRead v2 services... if (node && node.readv2) - url = - node.baseURL + - "/v2/packages/application%2Fbagit-097/" + - encodeURIComponent(this.get("id")); + url = `${ + node.baseURL + }/v2/packages/application%2Fbagit-097/${encodeURIComponent( + this.get("id"), + )}`; } else if (MetacatUI.appModel.get("packageServiceUrl")) url = MetacatUI.appModel.get("packageServiceUrl") + @@ -312,63 +300,53 @@ define([ /** * Overload Backbone.Model.fetch, so that we can set custom options for each fetch() request + * @param options */ - fetch: function (options) { + fetch(options) { if (!options) var options = {}; else var options = _.clone(options); options.url = this.url(); - //If we are using the Solr service to retrieve info about this object, then construct a query - if (typeof options != "undefined" && options.solrService) { - //Get basic information - var query = ""; + // If we are using the Solr service to retrieve info about this object, then construct a query + if (typeof options !== "undefined" && options.solrService) { + // Get basic information + let query = ""; - //Do not search for seriesId when it is not configured in this model/app + // Do not search for seriesId when it is not configured in this model/app if (typeof this.get("seriesid") === "undefined") - query += 'id:"' + encodeURIComponent(this.get("id")) + '"'; - //If there is no seriesid set, then search for pid or sid + query += `id:"${encodeURIComponent(this.get("id"))}"`; + // If there is no seriesid set, then search for pid or sid else if (!this.get("seriesid")) - query += - '(id:"' + - encodeURIComponent(this.get("id")) + - '" OR seriesId:"' + - encodeURIComponent(this.get("id")) + - '")'; - //If a seriesId is specified, then search for that + query += `(id:"${encodeURIComponent( + this.get("id"), + )}" OR seriesId:"${encodeURIComponent(this.get("id"))}")`; + // If a seriesId is specified, then search for that else if (this.get("seriesid") && this.get("id").length > 0) - query += - '(seriesId:"' + - encodeURIComponent(this.get("seriesid")) + - '" AND id:"' + - encodeURIComponent(this.get("id")) + - '")'; - //If only a seriesId is specified, then just search for the most recent version + query += `(seriesId:"${encodeURIComponent( + this.get("seriesid"), + )}" AND id:"${encodeURIComponent(this.get("id"))}")`; + // If only a seriesId is specified, then just search for the most recent version else if (this.get("seriesid") && !this.get("id")) - query += - 'seriesId:"' + - encodeURIComponent(this.get("id")) + - '" -obsoletedBy:*'; - - //The fields to return - var fl = "formatId,formatType,documents,isDocumentedBy,id,seriesId"; - - //Use the Solr query URL - var solrOptions = { - url: - MetacatUI.appModel.get("queryServiceUrl") + - "q=" + - query + - "&fl=" + - fl + - "&wt=json", + query += `seriesId:"${encodeURIComponent( + this.get("id"), + )}" -obsoletedBy:*`; + + // The fields to return + const fl = "formatId,formatType,documents,isDocumentedBy,id,seriesId"; + + // Use the Solr query URL + const solrOptions = { + url: `${MetacatUI.appModel.get( + "queryServiceUrl", + )}q=${query}&fl=${fl}&wt=json`, }; - //Merge with the options passed to this function + // Merge with the options passed to this function var fetchOptions = _.extend(options, solrOptions); - } else if (typeof options != "undefined") { - //Use custom options for retreiving XML - //Merge with the options passed to this function + } else if (typeof options !== "undefined") { + // Use custom options for retreiving XML + // Merge with the options passed to this function var fetchOptions = _.extend( { dataType: "text", @@ -376,37 +354,38 @@ define([ options, ); } else { - //Use custom options for retreiving XML + // Use custom options for retreiving XML var fetchOptions = _.extend({ dataType: "text", }); } - //Add the authorization options + // Add the authorization options fetchOptions = _.extend( fetchOptions, MetacatUI.appUserModel.createAjaxSettings(), ); - //Call Backbone.Model.fetch to retrieve the info + // Call Backbone.Model.fetch to retrieve the info return Backbone.Model.prototype.fetch.call(this, fetchOptions); }, /** * This function is called by Backbone.Model.fetch. * It deserializes the incoming XML from the /meta REST endpoint and converts it into JSON. + * @param response */ - parse: function (response) { + parse(response) { // If the response is XML - if (typeof response == "string" && response.indexOf("<") == 0) { - var responseDoc = $.parseHTML(response), - systemMetadata; + if (typeof response === "string" && response.indexOf("<") == 0) { + const responseDoc = $.parseHTML(response); + let systemMetadata; - //Save the raw XML in case it needs to be used later + // Save the raw XML in case it needs to be used later this.set("sysMetaXML", response); - //Find the XML node for the system metadata - for (var i = 0; i < responseDoc.length; i++) { + // Find the XML node for the system metadata + for (let i = 0; i < responseDoc.length; i++) { if ( responseDoc[i].nodeType == 1 && responseDoc[i].localName.indexOf("systemmetadata") > -1 @@ -416,14 +395,14 @@ define([ } } - //Parse the XML to JSON - var sysMetaValues = this.toJson(systemMetadata); + // Parse the XML to JSON + const sysMetaValues = this.toJson(systemMetadata); - //Convert the JSON to a camel-cased version, which matches Solr and is easier to work with in code + // Convert the JSON to a camel-cased version, which matches Solr and is easier to work with in code _.each( Object.keys(sysMetaValues), function (key) { - var camelCasedKey = this.nodeNameMap()[key]; + const camelCasedKey = this.nodeNameMap()[key]; if (camelCasedKey) { sysMetaValues[camelCasedKey] = sysMetaValues[key]; delete sysMetaValues[key]; @@ -432,14 +411,14 @@ define([ this, ); - //Save the checksum from the system metadata in a separate attribute on the model + // Save the checksum from the system metadata in a separate attribute on the model sysMetaValues.originalChecksum = sysMetaValues.checksum; sysMetaValues.checksum = this.defaults().checksum; - //Save the identifier as the id attribute + // Save the identifier as the id attribute sysMetaValues.id = sysMetaValues.identifier; - //Parse the Access Policy + // Parse the Access Policy if ( this.get("accessPolicy") && AccessPolicy.prototype.isPrototypeOf(this.get("accessPolicy")) @@ -449,7 +428,7 @@ define([ ); sysMetaValues.accessPolicy = this.get("accessPolicy"); } else { - //Create a new AccessPolicy collection, if there isn't one already. + // Create a new AccessPolicy collection, if there isn't one already. sysMetaValues.accessPolicy = this.createAccessPolicy( $(systemMetadata).find("accesspolicy"), ); @@ -458,58 +437,62 @@ define([ return sysMetaValues; // If the response is a list of Solr docs - } else if ( + } + if ( typeof response === "object" && response.response && response.response.docs ) { - //If no objects were found in the index, mark as notFound and exit + // If no objects were found in the index, mark as notFound and exit if (!response.response.docs.length) { this.set("notFound", true); this.trigger("notFound"); return; } - //Get the Solr document (there should be only one) - var doc = response.response.docs[0]; + // Get the Solr document (there should be only one) + const doc = response.response.docs[0]; - //Take out any empty values - _.each(Object.keys(doc), function (field) { + // Take out any empty values + _.each(Object.keys(doc), (field) => { if (!doc[field] && doc[field] !== 0) delete doc[field]; }); - //Remove any erroneous white space from fields + // Remove any erroneous white space from fields this.removeWhiteSpaceFromSolrFields(doc); return doc; } // Default to returning the raw response - else return response; + return response; }, - /** A utility function for converting XML to JSON */ - toJson: function (xml) { + /** + * A utility function for converting XML to JSON + * @param xml + */ + toJson(xml) { // Create the return object - var obj = {}; + let obj = {}; // do children if (xml.hasChildNodes()) { - for (var i = 0; i < xml.childNodes.length; i++) { - var item = xml.childNodes.item(i); + for (let i = 0; i < xml.childNodes.length; i++) { + const item = xml.childNodes.item(i); - //If it's an empty text node, skip it + // If it's an empty text node, skip it if (item.nodeType == 3 && !item.nodeValue.trim()) continue; - //Get the node name - var nodeName = item.localName; + // Get the node name + const nodeName = item.localName; - //If it's a new container node, convert it to JSON and add as a new object attribute - if (typeof obj[nodeName] == "undefined" && item.nodeType == 1) { + // If it's a new container node, convert it to JSON and add as a new object attribute + if (typeof obj[nodeName] === "undefined" && item.nodeType == 1) { obj[nodeName] = this.toJson(item); } - //If it's a new text node, just store the text value and add as a new object attribute + // If it's a new text node, just store the text value and add as a new object attribute else if ( - typeof obj[nodeName] == "undefined" && + typeof obj[nodeName] === "undefined" && item.nodeType == 3 ) { obj = @@ -519,16 +502,16 @@ define([ ? true : item.nodeValue; } - //If this node name is already stored as an object attribute... - else if (typeof obj[nodeName] != "undefined") { - //Cache what we have now - var old = obj[nodeName]; + // If this node name is already stored as an object attribute... + else if (typeof obj[nodeName] !== "undefined") { + // Cache what we have now + let old = obj[nodeName]; if (!Array.isArray(old)) old = [old]; - //Create a new object to store this node info + // Create a new object to store this node info var newNode = {}; - //Add the new node info to the existing array we have now + // Add the new node info to the existing array we have now if (item.nodeType == 1) { newNode = this.toJson(item); var newArray = old.concat(newNode); @@ -537,22 +520,22 @@ define([ var newArray = old.concat(newNode); } - //Store the attributes for this node - _.each(item.attributes, function (attr) { + // Store the attributes for this node + _.each(item.attributes, (attr) => { newNode[attr.localName] = attr.nodeValue; }); - //Replace the old array with the updated one + // Replace the old array with the updated one obj[nodeName] = newArray; - //Exit + // Exit continue; } - //Store the attributes for this node - /*_.each(item.attributes, function(attr){ + // Store the attributes for this node + /* _.each(item.attributes, function(attr){ obj[nodeName][attr.localName] = attr.nodeValue; - });*/ + }); */ } } return obj; @@ -563,38 +546,39 @@ define([ @param {object} json - the JSON object to convert to XML @param {Element} containerNode - an HTML element to insertt the resulting XML into @returns {Element} The updated HTML Element - */ - toXML: function (json, containerNode) { - if (typeof json == "string") { + */ + toXML(json, containerNode) { + if (typeof json === "string") { containerNode.textContent = json; return containerNode; } - for (var i = 0; i < Object.keys(json).length; i++) { - var key = Object.keys(json)[i], - contents = json[key] || json[key]; + for (let i = 0; i < Object.keys(json).length; i++) { + const key = Object.keys(json)[i]; + const contents = json[key] || json[key]; - var node = document.createElement(key); + let node = document.createElement(key); - //Skip this attribute if it is not populated + // Skip this attribute if it is not populated if (!contents || (Array.isArray(contents) && !contents.length)) continue; - //If it's a simple text node - if (typeof contents == "string") { + // If it's a simple text node + if (typeof contents === "string") { containerNode.textContent = contents; return containerNode; - } else if (Array.isArray(contents)) { - var allNewNodes = []; + } + if (Array.isArray(contents)) { + const allNewNodes = []; - for (var ii = 0; ii < contents.length; ii++) { + for (let ii = 0; ii < contents.length; ii++) { allNewNodes.push(this.toXML(contents[ii], $(node).clone()[0])); } if (allNewNodes.length) node = allNewNodes; - } else if (typeof contents == "object") { + } else if (typeof contents === "object") { $(node).append(this.toXML(contents, node)); - var attributeNames = _.without(Object.keys(json[key]), "content"); + const attributeNames = _.without(Object.keys(json[key]), "content"); } $(containerNode).append(node); @@ -605,21 +589,23 @@ define([ /** * Saves the DataONEObject System Metadata to the server + * @param attributes + * @param options */ - save: function (attributes, options) { + save(attributes, options) { // Set missing file names before saving if (!this.get("fileName")) { this.setMissingFileName(); } else { - //Replace all non-alphanumeric characters with underscores - var fileNameWithoutExt = this.get("fileName").substring( - 0, - this.get("fileName").lastIndexOf("."), - ), - extension = this.get("fileName").substring( - this.get("fileName").lastIndexOf("."), - this.get("fileName").length, - ); + // Replace all non-alphanumeric characters with underscores + const fileNameWithoutExt = this.get("fileName").substring( + 0, + this.get("fileName").lastIndexOf("."), + ); + const extension = this.get("fileName").substring( + this.get("fileName").lastIndexOf("."), + this.get("fileName").length, + ); this.set( "fileName", fileNameWithoutExt.replace(/[^a-zA-Z0-9]/g, "_") + extension, @@ -631,51 +617,51 @@ define([ return; } - //Set the upload transfer as in progress + // Set the upload transfer as in progress this.set("uploadProgress", 2); this.set("uploadStatus", "p"); - //Check if the checksum has been calculated yet. + // Check if the checksum has been calculated yet. if (!this.get("checksum")) { - //When it is calculated, restart this function + // When it is calculated, restart this function this.on("checksumCalculated", this.save); - //Calculate the checksum for this file + // Calculate the checksum for this file this.calculateChecksum(); - //Exit this function until the checksum is done + // Exit this function until the checksum is done return; } - //Create a FormData object to send data with our XHR - var formData = new FormData(); + // Create a FormData object to send data with our XHR + const formData = new FormData(); - //If this is not a new object, update the id. New DataONEObjects will have an id + // If this is not a new object, update the id. New DataONEObjects will have an id // created during initialize. if (!this.isNew()) { this.updateID(); formData.append("pid", this.get("oldPid")); formData.append("newPid", this.get("id")); } else { - //Create an ID if there isn't one + // Create an ID if there isn't one if (!this.get("id")) { - this.set("id", "urn:uuid:" + uuid.v4()); + this.set("id", `urn:uuid:${uuid.v4()}`); } - //Add the identifier to the XHR data + // Add the identifier to the XHR data formData.append("pid", this.get("id")); } - //Create the system metadata XML - var sysMetaXML = this.serializeSysMeta(); + // Create the system metadata XML + const sysMetaXML = this.serializeSysMeta(); - //Send the system metadata as a Blob - var xmlBlob = new Blob([sysMetaXML], { type: "application/xml" }); - //Add the system metadata XML to the XHR data + // Send the system metadata as a Blob + const xmlBlob = new Blob([sysMetaXML], { type: "application/xml" }); + // Add the system metadata XML to the XHR data formData.append("sysmeta", xmlBlob, "sysmeta.xml"); // Create the new object (MN.create()) formData.append("object", this.get("uploadFile"), this.get("fileName")); - var model = this; + const model = this; // On create(), add to the package and the metadata // Note: This should be added to the parent collection @@ -691,8 +677,8 @@ define([ this, ); - //Put together the AJAX and Backbone.save() options - var requestSettings = { + // Put together the AJAX and Backbone.save() options + let requestSettings = { url: this.url(), cache: false, contentType: false, @@ -700,15 +686,15 @@ define([ processData: false, data: formData, parse: false, - xhr: function () { - var xhr = new window.XMLHttpRequest(); + xhr() { + const xhr = new window.XMLHttpRequest(); - //Upload progress + // Upload progress xhr.upload.addEventListener( "progress", - function (evt) { + (evt) => { if (evt.lengthComputable) { - var percentComplete = (evt.loaded / evt.total) * 100; + const percentComplete = (evt.loaded / evt.total) * 100; model.set("uploadProgress", percentComplete); } @@ -719,24 +705,24 @@ define([ return xhr; }, success: this.onSuccessfulSave, - error: function (model, response, xhr) { - //Reset the identifier changes + error(model, response, xhr) { + // Reset the identifier changes model.resetID(); - //Reset the checksum, if this is a model that needs to be serialized with each save. + // Reset the checksum, if this is a model that needs to be serialized with each save. if (model.serialize) { model.set("checksum", model.defaults().checksum); } model.set("numSaveAttempts", model.get("numSaveAttempts") + 1); - var numSaveAttempts = model.get("numSaveAttempts"); + const numSaveAttempts = model.get("numSaveAttempts"); if ( numSaveAttempts < 3 && (response.status == 408 || response.status == 0) ) { - //Try saving again in 10, 40, and 90 seconds + // Try saving again in 10, 40, and 90 seconds setTimeout( - function () { + () => { model.save.call(model); }, numSaveAttempts * numSaveAttempts * 10000, @@ -744,11 +730,11 @@ define([ } else { model.set("numSaveAttempts", 0); - var parsedResponse = $(response.responseText) + let parsedResponse = $(response.responseText) .not("style, title") .text(); - //When there is no network connection (status == 0), there will be no response text + // When there is no network connection (status == 0), there will be no response text if (!parsedResponse) parsedResponse = "There was a network issue that prevented this file from uploading. " + @@ -756,10 +742,10 @@ define([ model.set("errorMessage", parsedResponse); - //Set the model status as e for error + // Set the model status as e for error model.set("uploadStatus", "e"); - //Trigger a custom event for the model save error + // Trigger a custom event for the model save error model.trigger("errorSaving", parsedResponse); // Track this error in our analytics @@ -772,13 +758,13 @@ define([ }, }; - //Add the user settings + // Add the user settings requestSettings = _.extend( requestSettings, MetacatUI.appUserModel.createAjaxSettings(), ); - //Send the Save request + // Send the Save request Backbone.Model.prototype.save.call(this, null, requestSettings); }, @@ -790,8 +776,8 @@ define([ * @param {XMLHttpRequest.response} [response] The XHR response object * @param {XMLHttpRequest} [xhr] The XHR that was just completed successfully */ - onSuccessfulSave: function (model, response, xhr) { - if (typeof model == "undefined") { + onSuccessfulSave(model, response, xhr) { + if (typeof model === "undefined") { var model = this; } @@ -809,13 +795,13 @@ define([ // Reset the content changes status model.set("hasContentChanges", false); - //Reset the model isNew attribute + // Reset the model isNew attribute model.set("isNew", false); // Reset oldPid so we can replace again model.set("oldPid", null); - //Set the last-calculated checksum as the original checksum + // Set the last-calculated checksum as the original checksum model.set("originalChecksum", model.get("checksum")); model.set("checksum", model.defaults().checksum); }, @@ -823,39 +809,39 @@ define([ /** * Updates the DataONEObject System Metadata to the server */ - updateSysMeta: function () { - //Update the upload status to "p" for "in progress" + updateSysMeta() { + // Update the upload status to "p" for "in progress" this.set("uploadStatus", "p"); - //Update the system metadata upload status to "p" as well, so the app + // Update the system metadata upload status to "p" as well, so the app // knows that the system metadata, specifically, is being updated. this.set("sysMetaUploadStatus", "p"); - var formData = new FormData(); + const formData = new FormData(); - //Add the identifier to the XHR data + // Add the identifier to the XHR data formData.append("pid", this.get("id")); - var sysMetaXML = this.serializeSysMeta(); + const sysMetaXML = this.serializeSysMeta(); - //Send the system metadata as a Blob - var xmlBlob = new Blob([sysMetaXML], { type: "application/xml" }); - //Add the system metadata XML to the XHR data + // Send the system metadata as a Blob + const xmlBlob = new Blob([sysMetaXML], { type: "application/xml" }); + // Add the system metadata XML to the XHR data formData.append("sysmeta", xmlBlob, "sysmeta.xml"); - var model = this; + const model = this; - var baseUrl = "", - activeAltRepo = MetacatUI.appModel.getActiveAltRepo(); - //Use the meta service URL from the alt repo + let baseUrl = ""; + const activeAltRepo = MetacatUI.appModel.getActiveAltRepo(); + // Use the meta service URL from the alt repo if (activeAltRepo) { baseUrl = activeAltRepo.metaServiceUrl; } - //If this MetacatUI deployment is pointing to a MN, use the meta service URL from the AppModel + // If this MetacatUI deployment is pointing to a MN, use the meta service URL from the AppModel else { baseUrl = MetacatUI.appModel.get("metaServiceUrl"); } - var requestSettings = { + let requestSettings = { url: baseUrl + encodeURIComponent(this.get("id")), cache: false, contentType: false, @@ -864,29 +850,29 @@ define([ processData: false, data: formData, parse: false, - success: function () { + success() { model.set("numSaveAttempts", 0); - //Fetch the system metadata from the server so we have a fresh copy of the newest sys meta. + // Fetch the system metadata from the server so we have a fresh copy of the newest sys meta. model.fetch({ systemMetadataOnly: true }); model.set("sysMetaErrorCode", null); - //Update the upload status to "c" for "complete" + // Update the upload status to "c" for "complete" model.set("uploadStatus", "c"); model.set("sysMetaUploadStatus", "c"); - //Trigger a custom event that the sys meta was updated + // Trigger a custom event that the sys meta was updated model.trigger("sysMetaUpdated"); }, - error: function (xhr, status, statusCode) { + error(xhr, status, statusCode) { model.set("numSaveAttempts", model.get("numSaveAttempts") + 1); - var numSaveAttempts = model.get("numSaveAttempts"); + const numSaveAttempts = model.get("numSaveAttempts"); if (numSaveAttempts < 3 && (statusCode == 408 || statusCode == 0)) { - //Try saving again in 10, 40, and 90 seconds + // Try saving again in 10, 40, and 90 seconds setTimeout( - function () { + () => { model.updateSysMeta.call(model); }, numSaveAttempts * numSaveAttempts * 10000, @@ -894,11 +880,11 @@ define([ } else { model.set("numSaveAttempts", 0); - var parsedResponse = $(xhr.responseText) + let parsedResponse = $(xhr.responseText) .not("style, title") .text(); - //When there is no network connection (status == 0), there will be no response text + // When there is no network connection (status == 0), there will be no response text if (!parsedResponse) parsedResponse = "There was a network issue that prevented this file from updating. " + @@ -926,67 +912,67 @@ define([ }, }; - //Add the user settings + // Add the user settings requestSettings = _.extend( requestSettings, MetacatUI.appUserModel.createAjaxSettings(), ); - //Send the XHR + // Send the XHR $.ajax(requestSettings); }, /** * Check if the current user is authorized to perform an action on this object. This function doesn't return * the result of the check, but it sends an XHR, updates this model, and triggers a change event. - * @param {string} [action=changePermission] - The action (read, write, or changePermission) to check + * @param {string} [action] - The action (read, write, or changePermission) to check * if the current user has authorization to perform. By default checks for the highest level of permission. * @param {object} [options] Additional options for this function. See the properties below. - * @property {function} options.onSuccess - A function to execute when the checkAuthority API is successfully completed - * @property {function} options.onError - A function to execute when the checkAuthority API returns an error, or when no PID or SID can be found for this object. - * @return {boolean} + * @property {Function} options.onSuccess - A function to execute when the checkAuthority API is successfully completed + * @property {Function} options.onError - A function to execute when the checkAuthority API returns an error, or when no PID or SID can be found for this object. + * @returns {boolean} */ - checkAuthority: function (action = "changePermission", options) { + checkAuthority(action = "changePermission", options) { try { // return false - if neither PID nor SID is present to check the authority if (this.get("id") == null && this.get("seriesId") == null) { return false; } - if (typeof options == "undefined") { + if (typeof options === "undefined") { var options = {}; } // If onError or onSuccess options were provided by the user, // check that they are functions first, so we don't try to use // some other type of variable as a function later on. - ["onError", "onSuccess"].forEach(function (userFunction) { + ["onError", "onSuccess"].forEach((userFunction) => { if (typeof options[userFunction] !== "function") { options[userFunction] = null; } }); // If PID is not present - check authority with seriesId - var identifier = this.get("id"); + let identifier = this.get("id"); if (identifier == null) { identifier = this.get("seriesId"); } - //If there are alt repositories configured, find the possible authoritative + // If there are alt repositories configured, find the possible authoritative // Member Node for this DataONEObject. if (MetacatUI.appModel.get("alternateRepositories").length) { - //Get the array of possible authoritative MNs - var possibleAuthMNs = this.get("possibleAuthMNs"); + // Get the array of possible authoritative MNs + const possibleAuthMNs = this.get("possibleAuthMNs"); - //If there are no possible authoritative MNs, use the auth service URL from the AppModel + // If there are no possible authoritative MNs, use the auth service URL from the AppModel if (!possibleAuthMNs.length) { baseUrl = MetacatUI.appModel.get("authServiceUrl"); } else { - //Use the auth service URL from the top possible auth MN + // Use the auth service URL from the top possible auth MN baseUrl = possibleAuthMNs[0].authServiceUrl; } } else { - //Get the auth service URL from the AppModel + // Get the auth service URL from the AppModel baseUrl = MetacatUI.appModel.get("authServiceUrl"); } @@ -994,41 +980,41 @@ define([ return false; } - var onSuccess = - options.onSuccess || - function (data, textStatus, xhr) { - model.set("isAuthorized_" + action, true); - model.set("isAuthorized", true); - model.trigger("change:isAuthorized"); - }, - onError = - options.onError || - function (xhr, textStatus, errorThrown) { - if (errorThrown == 404) { - var possibleAuthMNs = model.get("possibleAuthMNs"); - if (possibleAuthMNs.length) { - //Remove the first MN from the array, since it didn't contain the object, so it's not the auth MN - possibleAuthMNs.shift(); - } - - //If there are no other possible auth MNs to check, trigger this model as Not Found. - if (possibleAuthMNs.length == 0 || !possibleAuthMNs) { - model.set("notFound", true); - model.trigger("notFound"); - } - //If there's more MNs to check, try again - else { - model.checkAuthority(action, options); - } - } else { - model.set("isAuthorized_" + action, false); - model.set("isAuthorized", false); + const onSuccess = + options.onSuccess || + function (data, textStatus, xhr) { + model.set(`isAuthorized_${action}`, true); + model.set("isAuthorized", true); + model.trigger("change:isAuthorized"); + }; + const onError = + options.onError || + function (xhr, textStatus, errorThrown) { + if (errorThrown == 404) { + const possibleAuthMNs = model.get("possibleAuthMNs"); + if (possibleAuthMNs.length) { + // Remove the first MN from the array, since it didn't contain the object, so it's not the auth MN + possibleAuthMNs.shift(); } - }; + + // If there are no other possible auth MNs to check, trigger this model as Not Found. + if (possibleAuthMNs.length == 0 || !possibleAuthMNs) { + model.set("notFound", true); + model.trigger("notFound"); + } + // If there's more MNs to check, try again + else { + model.checkAuthority(action, options); + } + } else { + model.set(`isAuthorized_${action}`, false); + model.set("isAuthorized", false); + } + }; var model = this; - var requestSettings = { - url: baseUrl + encodeURIComponent(identifier) + "?action=" + action, + const requestSettings = { + url: `${baseUrl + encodeURIComponent(identifier)}?action=${action}`, type: "GET", success: onSuccess, error: onError, @@ -1040,7 +1026,7 @@ define([ ), ); } catch (e) { - //Log an error to the console + // Log an error to the console console.error("Couldn't check the authority for this user: ", e); // Track this error in our analytics @@ -1051,8 +1037,8 @@ define([ true, ); - //Set the user as unauthorized - model.set("isAuthorized_" + action, false); + // Set the user as unauthorized + model.set(`isAuthorized_${action}`, false); model.set("isAuthorized", false); return false; } @@ -1062,21 +1048,21 @@ define([ * Using the attributes set on this DataONEObject model, serializes the system metadata XML * @returns {string} */ - serializeSysMeta: function () { - //Get the system metadata XML that currently exists in the system - var sysMetaXML = this.get("sysMetaXML"), // sysmeta as string - xml, // sysmeta as DOM object - accessPolicyXML, // The generated access policy XML - previousSiblingNode, // A DOM node indicating any previous sibling - rightsHolderNode, // A DOM node for the rights holder field - accessPolicyNode, // A DOM node for the access policy - replicationPolicyNode, // A DOM node for the replication policy - obsoletesNode, // A DOM node for the obsoletes field - obsoletedByNode, // A DOM node for the obsoletedBy field - fileNameNode, // A DOM node for the file name - xmlString, // The system metadata document as a string - nodeNameMap, // The map of camelCase to lowercase attributes - extension; // the file name extension for this object + serializeSysMeta() { + // Get the system metadata XML that currently exists in the system + const sysMetaXML = this.get("sysMetaXML"); // sysmeta as string + let xml; // sysmeta as DOM object + let accessPolicyXML; // The generated access policy XML + let previousSiblingNode; // A DOM node indicating any previous sibling + let rightsHolderNode; // A DOM node for the rights holder field + let accessPolicyNode; // A DOM node for the access policy + let replicationPolicyNode; // A DOM node for the replication policy + let obsoletesNode; // A DOM node for the obsoletes field + let obsoletedByNode; // A DOM node for the obsoletedBy field + let fileNameNode; // A DOM node for the file name + let xmlString; // The system metadata document as a string + let nodeNameMap; // The map of camelCase to lowercase attributes + let extension; // the file name extension for this object if (typeof sysMetaXML === "undefined" || sysMetaXML === null) { xml = this.createSysMeta(); @@ -1084,7 +1070,7 @@ define([ xml = $($.parseHTML(sysMetaXML)); } - //Update the system metadata values + // Update the system metadata values xml.find("serialversion").text(this.get("serialVersion") || "0"); xml.find("identifier").text(this.get("newPid") || this.get("id")); xml @@ -1094,53 +1080,53 @@ define([ ); xml.find("formatid").text(this.get("formatId") || this.getFormatId()); - //If there is a seriesId, add it + // If there is a seriesId, add it if (this.get("seriesId")) { - //Get the seriesId XML node - var seriesIdNode = xml.find("seriesId"); + // Get the seriesId XML node + let seriesIdNode = xml.find("seriesId"); - //If it doesn't exist, create one + // If it doesn't exist, create one if (!seriesIdNode.length) { seriesIdNode = $(document.createElement("seriesid")); xml.find("identifier").before(seriesIdNode); } - //Add the seriesId string to the XML node + // Add the seriesId string to the XML node seriesIdNode.text(this.get("seriesId")); } - //If there is no size, get it + // If there is no size, get it if (!this.get("size") && this.get("uploadFile")) { this.set("size", this.get("uploadFile").size); } - //Get the size of the file, if there is one + // Get the size of the file, if there is one if (this.get("uploadFile")) { xml.find("size").text(this.get("uploadFile").size); } - //Otherwise, use the last known size + // Otherwise, use the last known size else { xml.find("size").text(this.get("size")); } - //Save the original checksum + // Save the original checksum if (!this.get("checksum") && this.get("originalChecksum")) { xml.find("checksum").text(this.get("originalChecksum")); } - //Update the checksum and checksum algorithm + // Update the checksum and checksum algorithm else { xml.find("checksum").text(this.get("checksum")); xml.find("checksum").attr("algorithm", this.get("checksumAlgorithm")); } - //Update the rightsholder + // Update the rightsholder xml .find("rightsholder") .text( this.get("rightsHolder") || MetacatUI.appUserModel.get("username"), ); - //Write the access policy + // Write the access policy accessPolicyXML = this.get("accessPolicy").serialize(); // Get the access policy node, if it exists @@ -1154,7 +1140,7 @@ define([ previousSiblingNode.after(accessPolicyNode); } - //Replace the old access policy with the new one if it exists + // Replace the old access policy with the new one if it exists if (accessPolicyXML) { accessPolicyNode.replaceWith(accessPolicyXML); } else { @@ -1186,10 +1172,8 @@ define([ ); previousSiblingNode.after(obsoletesNode); } - } else { - if (obsoletesNode) { - obsoletesNode.remove(); - } + } else if (obsoletesNode) { + obsoletesNode.remove(); } if (obsoletesNode) { @@ -1198,7 +1182,7 @@ define([ obsoletedByNode = xml.find("obsoletedby"); - //remove the obsoletedBy node if it exists + // remove the obsoletedBy node if it exists // TODO: Verify this is what we want to do if (obsoletedByNode) { obsoletedByNode.remove(); @@ -1209,41 +1193,38 @@ define([ .find("dateuploaded") .text(this.get("dateUploaded") || new Date().toISOString()); - //Get the filename node + // Get the filename node fileNameNode = xml.find("filename"); - //If the filename node doesn't exist, then create one + // If the filename node doesn't exist, then create one if (!fileNameNode.length) { fileNameNode = $(document.createElement("filename")); xml.find("dateuploaded").after(fileNameNode); } - //Set the object file name + // Set the object file name $(fileNameNode).text(this.get("fileName")); xmlString = $(document.createElement("div")).append(xml.clone()).html(); - //Now camel case the nodes + // Now camel case the nodes nodeNameMap = this.nodeNameMap(); _.each( Object.keys(nodeNameMap), - function (name, i) { - var originalXMLString = xmlString; + (name, i) => { + const originalXMLString = xmlString; - //Camel case node names - var regEx = new RegExp("<" + name, "g"); - xmlString = xmlString.replace(regEx, "<" + nodeNameMap[name]); - var regEx = new RegExp(name + ">", "g"); - xmlString = xmlString.replace(regEx, nodeNameMap[name] + ">"); + // Camel case node names + var regEx = new RegExp(`<${name}`, "g"); + xmlString = xmlString.replace(regEx, `<${nodeNameMap[name]}`); + var regEx = new RegExp(`${name}>`, "g"); + xmlString = xmlString.replace(regEx, `${nodeNameMap[name]}>`); - //If node names haven't been changed, then find an attribute + // If node names haven't been changed, then find an attribute if (xmlString == originalXMLString) { - var regEx = new RegExp(" " + name + "=", "g"); - xmlString = xmlString.replace( - regEx, - " " + nodeNameMap[name] + "=", - ); + var regEx = new RegExp(` ${name}=`, "g"); + xmlString = xmlString.replace(regEx, ` ${nodeNameMap[name]}=`); } }, this, @@ -1257,16 +1238,16 @@ define([ /** * Get the object format identifier for this object */ - getFormatId: function () { - var formatId = "application/octet-stream", // default to untyped data - objectFormats = { - mediaTypes: [], // The list of potential formatIds based on mediaType matches - extensions: [], // The list of possible formatIds based onextension matches - }, - fileName = this.get("fileName"), // the fileName for this object - ext; // The extension of the filename for this object + getFormatId() { + let formatId = "application/octet-stream"; // default to untyped data + const objectFormats = { + mediaTypes: [], // The list of potential formatIds based on mediaType matches + extensions: [], // The list of possible formatIds based onextension matches + }; + const fileName = this.get("fileName"); // the fileName for this object + let ext; // The extension of the filename for this object - objectFormats["mediaTypes"] = MetacatUI.objectFormats.where({ + objectFormats.mediaTypes = MetacatUI.objectFormats.where({ formatId: this.get("mediaType"), }); if ( @@ -1278,17 +1259,17 @@ define([ fileName.lastIndexOf(".") + 1, fileName.length, ); - objectFormats["extensions"] = MetacatUI.objectFormats.where({ + objectFormats.extensions = MetacatUI.objectFormats.where({ extension: ext, }); } if ( - objectFormats["mediaTypes"].length > 0 && - objectFormats["extensions"].length > 0 + objectFormats.mediaTypes.length > 0 && + objectFormats.extensions.length > 0 ) { - var firstMediaType = objectFormats["mediaTypes"][0].get("formatId"); - var firstExtension = objectFormats["extensions"][0].get("formatId"); + const firstMediaType = objectFormats.mediaTypes[0].get("formatId"); + const firstExtension = objectFormats.extensions[0].get("formatId"); // Check if they're equal if (firstMediaType === firstExtension) { formatId = firstMediaType; @@ -1304,19 +1285,19 @@ define([ } } - if (objectFormats["mediaTypes"].length > 0) { - formatId = objectFormats["mediaTypes"][0].get("formatId"); + if (objectFormats.mediaTypes.length > 0) { + formatId = objectFormats.mediaTypes[0].get("formatId"); console.log("returning default mediaType"); console.log(formatId); return formatId; } - if (objectFormats["extensions"].length > 0) { - //If this is a "nc" file, assume it is a netCDF-3 file. + if (objectFormats.extensions.length > 0) { + // If this is a "nc" file, assume it is a netCDF-3 file. if (ext == "nc") { formatId = "netCDF-3"; } else { - formatId = objectFormats["extensions"][0].get("formatId"); + formatId = objectFormats.extensions[0].get("formatId"); } return formatId; } @@ -1329,8 +1310,8 @@ define([ * @returns format String * @since 2.28.0 */ - getFormat: function () { - var formatMap = { + getFormat() { + const formatMap = { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Microsoft Excel OpenXML", "application/vnd.openxmlformats-officedocument.wordprocessingml.document": @@ -1392,12 +1373,12 @@ define([ * Build a fresh system metadata document for this object when it is new * Return it as a DOM object */ - createSysMeta: function () { - var sysmetaDOM, // The DOM - sysmetaXML = []; // The document as a string array + createSysMeta() { + let sysmetaDOM; // The DOM + const sysmetaXML = []; // The document as a string array sysmetaXML.push( - //'', + // '', "', @@ -1419,30 +1400,30 @@ define([ /** * Create an access policy for this DataONEObject using the default access * policy set in the AppModel. - * * @param {Element} [accessPolicyXML] - An XML node * that contains a list of access rules. - * @return {AccessPolicy} - an AccessPolicy collection that represents the + * @returns {AccessPolicy} - an AccessPolicy collection that represents the * given XML or the default policy set in the AppModel. */ - createAccessPolicy: function (accessPolicyXML) { - //Create a new AccessPolicy collection - var accessPolicy = new AccessPolicy(); + createAccessPolicy(accessPolicyXML) { + // Create a new AccessPolicy collection + const accessPolicy = new AccessPolicy(); accessPolicy.dataONEObject = this; - //If there is no access policy XML sent, + // If there is no access policy XML sent, if (this.isNew() && !accessPolicyXML) { try { - //If the app is configured to inherit the access policy from the parent metadata, + // If the app is configured to inherit the access policy from the parent metadata, // then get the parent metadata and copy it's AccessPolicy - let scienceMetadata = this.get("isDocumentedByModels"); + const scienceMetadata = this.get("isDocumentedByModels"); if ( MetacatUI.appModel.get("inheritAccessPolicy") && scienceMetadata && scienceMetadata.length ) { - let sciMetaAccessPolicy = scienceMetadata[0].get("accessPolicy"); + const sciMetaAccessPolicy = + scienceMetadata[0].get("accessPolicy"); if (sciMetaAccessPolicy) { accessPolicy.copyAccessPolicy(sciMetaAccessPolicy); @@ -1450,7 +1431,7 @@ define([ accessPolicy.createDefaultPolicy(); } } - //Otherwise, set the default access policy using the AppModel configuration + // Otherwise, set the default access policy using the AppModel configuration else { accessPolicy.createDefaultPolicy(); } @@ -1462,12 +1443,12 @@ define([ accessPolicy.createDefaultPolicy(); } } else { - //Parse the access policy XML to create AccessRule models from the XML + // Parse the access policy XML to create AccessRule models from the XML accessPolicy.parse(accessPolicyXML); } - //Listen to changes on the collection and trigger a change on this model - var self = this; + // Listen to changes on the collection and trigger a change on this model + const self = this; this.listenTo(accessPolicy, "change update", function () { self.trigger("change"); this.addToUploadQueue(); @@ -1478,7 +1459,6 @@ define([ /** * Update identifiers for this object - * * @param {string} id - Optional identifier to update with. Generated * automatically when not given. * @@ -1491,44 +1471,42 @@ define([ * PID will be such as the case where we want to update a matching * EML entity when replacing files. */ - updateID: function (id) { + updateID(id) { // Only run once until oldPid is reset if (this.get("oldPid")) { return; } - //Save the attributes so we can reset the ID later + // Save the attributes so we can reset the ID later this.attributeCache = this.toJSON(); - //Set the old identifier - var oldPid = this.get("id"), - selfDocuments, - selfDocumentedBy, - documentedModels, - documentedModel, - index; + // Set the old identifier + const oldPid = this.get("id"); + let selfDocuments; + let selfDocumentedBy; + let documentedModels; + let documentedModel; + let index; - //Save the current id as the old pid + // Save the current id as the old pid this.set("oldPid", oldPid); - //Create a new seriesId, if there isn't one, and if this model specifies that one is required + // Create a new seriesId, if there isn't one, and if this model specifies that one is required if (!this.get("seriesId") && this.get("createSeriesId")) { - this.set("seriesId", "urn:uuid:" + uuid.v4()); + this.set("seriesId", `urn:uuid:${uuid.v4()}`); } // Check to see if the old pid documents or is documented by itself selfDocuments = _.contains(this.get("documents"), oldPid); selfDocumentedBy = _.contains(this.get("isDocumentedBy"), oldPid); - //Set the new identifier + // Set the new identifier if (id) { this.set("id", id); + } else if (this.get("type") == "DataPackage") { + this.set("id", `resource_map_urn:uuid:${uuid.v4()}`); } else { - if (this.get("type") == "DataPackage") { - this.set("id", "resource_map_urn:uuid:" + uuid.v4()); - } else { - this.set("id", "urn:uuid:" + uuid.v4()); - } + this.set("id", `urn:uuid:${uuid.v4()}`); } // Remove the old pid from the documents list if present @@ -1555,7 +1533,7 @@ define([ _.each( this.get("documents"), function (id) { - (documentedModels = MetacatUI.rootDataPackage.where({ id: id })), + (documentedModels = MetacatUI.rootDataPackage.where({ id })), documentedModel; if (documentedModels.length > 0) { @@ -1580,21 +1558,21 @@ define([ this.trigger("change:id"); - //Update the obsoletes and obsoletedBy + // Update the obsoletes and obsoletedBy this.set("obsoletes", oldPid); this.set("obsoletedBy", null); // Update the latest version of this object this.set("latestVersion", this.get("id")); - //Set the archived option to false + // Set the archived option to false this.set("archived", false); }, /** * Resets the identifier for this model. This undos all of the changes made in {DataONEObject#updateID} */ - resetID: function () { + resetID() { if (!this.attributeCache) return false; this.set("oldPid", this.attributeCache.oldPid, { silent: true }); @@ -1608,7 +1586,7 @@ define([ silent: true, }); - //Reset the attribute cache + // Reset the attribute cache this.attributeCache = {}; }, @@ -1616,28 +1594,28 @@ define([ * Checks if this system metadata XML has updates that need to be synced with the server. * @returns {boolean} */ - hasUpdates: function () { + hasUpdates() { if (this.isNew()) return true; // Compare the new system metadata XML to the old system metadata XML - //Check if there is system metadata first + // Check if there is system metadata first if (!this.get("sysMetaXML")) { return false; } - var D1ObjectClone = this.clone(), - // Make sure we are using the parse function in the DataONEObject model. - // Sometimes hasUpdates is called from extensions of the D1Object model, - // (e.g. from the portal model), and the parse function is overwritten - oldSysMetaAttrs = new DataONEObject().parse( - D1ObjectClone.get("sysMetaXML"), - ); + const D1ObjectClone = this.clone(); + // Make sure we are using the parse function in the DataONEObject model. + // Sometimes hasUpdates is called from extensions of the D1Object model, + // (e.g. from the portal model), and the parse function is overwritten + const oldSysMetaAttrs = new DataONEObject().parse( + D1ObjectClone.get("sysMetaXML"), + ); D1ObjectClone.set(oldSysMetaAttrs); - var oldSysMeta = D1ObjectClone.serializeSysMeta(); - var newSysMeta = this.serializeSysMeta(); + const oldSysMeta = D1ObjectClone.serializeSysMeta(); + const newSysMeta = this.serializeSysMeta(); if (oldSysMeta === "") return false; @@ -1650,42 +1628,42 @@ define([ @param {DataONEObject} [model] @param {object} options Furhter options for this function @property {boolean} options.force If true, a change will be handled regardless if the attribute actually changed - */ - handleChange: function (model, options) { + */ + handleChange(model, options) { if (!model) var model = this; - var sysMetaAttrs = [ - "serialVersion", - "identifier", - "formatId", - "formatType", - "size", - "checksum", - "checksumAlgorithm", - "submitter", - "rightsHolder", - "accessPolicy", - "replicationAllowed", - "replicationPolicy", - "obsoletes", - "obsoletedBy", - "archived", - "dateUploaded", - "dateSysMetadataModified", - "originMemberNode", - "authoritativeMemberNode", - "replica", - "seriesId", - "mediaType", - "fileName", - ], - nonSysMetaNonContentAttrs = _.difference( - model.get("originalAttrs"), - sysMetaAttrs, - ), - allChangedAttrs = Object.keys(model.changedAttributes()), - changedSysMetaOrContentAttrs = [], //sysmeta or content attributes that have changed - changedContentAttrs = []; // attributes from sub classes like ScienceMetadata or EML211 ... + const sysMetaAttrs = [ + "serialVersion", + "identifier", + "formatId", + "formatType", + "size", + "checksum", + "checksumAlgorithm", + "submitter", + "rightsHolder", + "accessPolicy", + "replicationAllowed", + "replicationPolicy", + "obsoletes", + "obsoletedBy", + "archived", + "dateUploaded", + "dateSysMetadataModified", + "originMemberNode", + "authoritativeMemberNode", + "replica", + "seriesId", + "mediaType", + "fileName", + ]; + const nonSysMetaNonContentAttrs = _.difference( + model.get("originalAttrs"), + sysMetaAttrs, + ); + const allChangedAttrs = Object.keys(model.changedAttributes()); + let changedSysMetaOrContentAttrs = []; // sysmeta or content attributes that have changed + let changedContentAttrs = []; // attributes from sub classes like ScienceMetadata or EML211 ... // Get a list of all changed sysmeta and content attributes changedSysMetaOrContentAttrs = _.difference( @@ -1724,19 +1702,19 @@ define([ /** * Returns true if this DataONE object is new. A DataONE object is new * if there is no upload date and it's been synced (i.e. been fetched) - * @return {boolean} + * @returns {boolean} */ - isNew: function () { - //If the model is explicitly marked as not new, return false + isNew() { + // If the model is explicitly marked as not new, return false if (this.get("isNew") === false) { return false; } - //If the model is explicitly marked as new, return true - else if (this.get("isNew") === true) { + // If the model is explicitly marked as new, return true + if (this.get("isNew") === true) { return true; } - //Check if there is an upload date that was retrieved from the server + // Check if there is an upload date that was retrieved from the server return ( this.get("dateUploaded") === this.defaults().dateUploaded && this.get("synced") @@ -1746,12 +1724,12 @@ define([ /** * Updates the upload status attribute on this model and marks the collection as changed */ - addToUploadQueue: function () { + addToUploadQueue() { if (!this.get("synced")) { return; } - //Add this item to the queue + // Add this item to the queue if ( this.get("uploadStatus") == "c" || this.get("uploadStatus") == "e" || @@ -1759,10 +1737,10 @@ define([ ) { this.set("uploadStatus", "q"); - //Mark each DataPackage collection this model is in as changed + // Mark each DataPackage collection this model is in as changed _.each( this.get("collections"), - function (collection) { + (collection) => { if (collection.packageModel) collection.packageModel.set("changed", true); }, @@ -1775,12 +1753,12 @@ define([ * Updates the progress percentage when the model is getting uploaded * @param {ProgressEvent} e - The ProgressEvent when this file is being uploaded */ - updateProgress: function (e) { + updateProgress(e) { if (e.lengthComputable) { - var max = e.total; - var current = e.loaded; + const max = e.total; + const current = e.loaded; - var Percentage = (current * 100) / max; + const Percentage = (current * 100) / max; if (Percentage >= 100) { // process completed @@ -1791,25 +1769,28 @@ define([ /** * Updates the relationships with other models when this model has been updated */ - updateRelationships: function () { + updateRelationships() { _.each( this.get("collections"), function (collection) { - //Get the old id for this model - var oldId = this.get("oldPid"); + // Get the old id for this model + const oldId = this.get("oldPid"); if (!oldId) return; - //Find references to the old id in the documents relationship - var outdatedModels = collection.filter(function (m) { - return _.contains(m.get("documents"), oldId); - }); + // Find references to the old id in the documents relationship + const outdatedModels = collection.filter((m) => + _.contains(m.get("documents"), oldId), + ); - //Update the documents array in each model + // Update the documents array in each model _.each( outdatedModels, function (model) { - var updatedDocuments = _.without(model.get("documents"), oldId); + const updatedDocuments = _.without( + model.get("documents"), + oldId, + ); updatedDocuments.push(this.get("id")); model.set("documents", updatedDocuments); @@ -1822,19 +1803,19 @@ define([ }, /** - * Finds the latest version of this object by travesing the obsolescence chain - * @param {string} [latestVersion] - The identifier of the latest known object in the version chain. + * Finds the latest version of this object by travesing the obsolescence chain + * @param {string} [latestVersion] - The identifier of the latest known object in the version chain. If not supplied, this model's `id` will be used. - * @param {string} [possiblyNewer] - The identifier of the object that obsoletes the latestVersion. It's "possibly" newer, because it may be private/inaccessible - */ - findLatestVersion: function (latestVersion, possiblyNewer) { - var baseUrl = "", - activeAltRepo = MetacatUI.appModel.getActiveAltRepo(); - //Use the meta service URL from the alt repo + * @param {string} [possiblyNewer] - The identifier of the object that obsoletes the latestVersion. It's "possibly" newer, because it may be private/inaccessible + */ + findLatestVersion(latestVersion, possiblyNewer) { + let baseUrl = ""; + const activeAltRepo = MetacatUI.appModel.getActiveAltRepo(); + // Use the meta service URL from the alt repo if (activeAltRepo) { baseUrl = activeAltRepo.metaServiceUrl; } - //If this MetacatUI deployment is pointing to a MN, use the meta service URL from the AppModel + // If this MetacatUI deployment is pointing to a MN, use the meta service URL from the AppModel else { baseUrl = MetacatUI.appModel.get("metaServiceUrl"); } @@ -1843,7 +1824,7 @@ define([ return; } - //If there is no system metadata, then retrieve it first + // If there is no system metadata, then retrieve it first if (!this.get("sysMetaXML")) { this.once("sync", this.findLatestVersion); this.once("systemMetadataSync", this.findLatestVersion); @@ -1855,47 +1836,47 @@ define([ return; } - //If no pid was supplied, use this model's id - if (!latestVersion || typeof latestVersion != "string") { + // If no pid was supplied, use this model's id + if (!latestVersion || typeof latestVersion !== "string") { var latestVersion = this.get("id"); var possiblyNewer = this.get("obsoletedBy"); } - //If this isn't obsoleted by anything, then there is no newer version - if (!possiblyNewer || typeof latestVersion != "string") { + // If this isn't obsoleted by anything, then there is no newer version + if (!possiblyNewer || typeof latestVersion !== "string") { this.set("latestVersion", latestVersion); - //Trigger an event that will fire whether or not the latestVersion + // Trigger an event that will fire whether or not the latestVersion // attribute was actually changed this.trigger("latestVersionFound", this); - //Remove the listeners now that we found the latest version + // Remove the listeners now that we found the latest version this.stopListening("sync", this.findLatestVersion); this.stopListening("systemMetadataSync", this.findLatestVersion); return; } - var model = this; + const model = this; - //Get the system metadata for the possibly newer version - var requestSettings = { + // Get the system metadata for the possibly newer version + const requestSettings = { url: baseUrl + encodeURIComponent(possiblyNewer), type: "GET", - success: function (data) { + success(data) { // the response may have an obsoletedBy element - var obsoletedBy = $(data).find("obsoletedBy").text(); + const obsoletedBy = $(data).find("obsoletedBy").text(); - //If there is an even newer version, then get it and rerun this function + // If there is an even newer version, then get it and rerun this function if (obsoletedBy) { model.findLatestVersion(possiblyNewer, obsoletedBy); } - //If there isn't a newer version, then this is it + // If there isn't a newer version, then this is it else { model.set("latestVersion", possiblyNewer); model.trigger("latestVersionFound", model); - //Remove the listeners now that we found the latest version + // Remove the listeners now that we found the latest version model.stopListening("sync", model.findLatestVersion); model.stopListening( "systemMetadataSync", @@ -1903,8 +1884,8 @@ define([ ); } }, - error: function (xhr) { - //If this newer version isn't accessible, link to the latest version that is + error(xhr) { + // If this newer version isn't accessible, link to the latest version that is if (xhr.status == "401") { model.set("latestVersion", latestVersion); model.trigger("latestVersionFound", model); @@ -1925,72 +1906,66 @@ define([ * @param {string|Element} xml - The XML to format * @returns {string} The formatted XML string */ - formatXML: function (xml) { - var nodeNameMap = this.nodeNameMap(), - xmlString = ""; + formatXML(xml) { + const nodeNameMap = this.nodeNameMap(); + let xmlString = ""; - //XML must be provided for this function + // XML must be provided for this function if (!xml) return ""; - //Support XML strings - else if (typeof xml == "string") xmlString = xml; - //Support DOMs - else if (typeof xml == "object" && xml.nodeType) { - //XML comments should be formatted with start and end carets - if (xml.nodeType == 8) xmlString = "<" + xml.nodeValue + ">"; - //XML nodes have the entire XML string available in the outerHTML attribute + // Support XML strings + if (typeof xml === "string") xmlString = xml; + // Support DOMs + else if (typeof xml === "object" && xml.nodeType) { + // XML comments should be formatted with start and end carets + if (xml.nodeType == 8) xmlString = `<${xml.nodeValue}>`; + // XML nodes have the entire XML string available in the outerHTML attribute else if (xml.nodeType == 1) xmlString = xml.outerHTML; - //Text node types are left as-is + // Text node types are left as-is else if (xml.nodeType == 3) return xml.nodeValue; } - //Return empty strings if something went wrong + // Return empty strings if something went wrong if (!xmlString) return ""; _.each( Object.keys(nodeNameMap), - function (name, i) { - var originalXMLString = xmlString; - - //Check for this node name whe it's an opening XML node, e.g. `` - var regEx = new RegExp("<" + name + ">", "g"); - xmlString = xmlString.replace(regEx, "<" + nodeNameMap[name] + ">"); - - //Check for this node name when it's an opening XML node, e.g. `` - regEx = new RegExp(":" + name + ">", "g"); - xmlString = xmlString.replace(regEx, ":" + nodeNameMap[name] + ">"); - - //Check for this node name when it's a closing XML tag, e.g. `` - regEx = new RegExp("", "g"); - xmlString = xmlString.replace( - regEx, - "", - ); + (name, i) => { + const originalXMLString = xmlString; + + // Check for this node name whe it's an opening XML node, e.g. `` + let regEx = new RegExp(`<${name}>`, "g"); + xmlString = xmlString.replace(regEx, `<${nodeNameMap[name]}>`); - //If node names haven't been changed, then find an attribute, e.g. ` name=` + // Check for this node name when it's an opening XML node, e.g. `` + regEx = new RegExp(`:${name}>`, "g"); + xmlString = xmlString.replace(regEx, `:${nodeNameMap[name]}>`); + + // Check for this node name when it's a closing XML tag, e.g. `` + regEx = new RegExp(``, "g"); + xmlString = xmlString.replace(regEx, ``); + + // If node names haven't been changed, then find an attribute, e.g. ` name=` if (xmlString == originalXMLString) { - regEx = new RegExp(" " + name + "=", "g"); - xmlString = xmlString.replace( - regEx, - " " + nodeNameMap[name] + "=", - ); + regEx = new RegExp(` ${name}=`, "g"); + xmlString = xmlString.replace(regEx, ` ${nodeNameMap[name]}=`); } }, this, ); - //Take each XML node text value and decode any XML entities - var regEx = new RegExp("&[0-9a-zA-Z]+;", "g"); - xmlString = xmlString.replace(regEx, function (match) { - return he.encode(he.decode(match)); - }); + // Take each XML node text value and decode any XML entities + const regEx = new RegExp("&[0-9a-zA-Z]+;", "g"); + xmlString = xmlString.replace(regEx, (match) => + he.encode(he.decode(match)), + ); return xmlString; }, @@ -2001,30 +1976,30 @@ define([ * @returns None * @since: 2.28.0 */ - downloadWithCredentials: function () { - //if(this.get("isPublic")) return; + downloadWithCredentials() { + // if(this.get("isPublic")) return; - //Get info about this object - var url = this.get("url"), - model = this; + // Get info about this object + const url = this.get("url"); + const model = this; - //Create an XHR - var xhr = new XMLHttpRequest(); + // Create an XHR + const xhr = new XMLHttpRequest(); - //Open and send the request with the user's auth token + // Open and send the request with the user's auth token xhr.open("GET", url); if (MetacatUI.appUserModel.get("loggedIn")) xhr.withCredentials = true; - //When the XHR is ready, create a link with the raw data (Blob) and click the link to download + // When the XHR is ready, create a link with the raw data (Blob) and click the link to download xhr.onload = function () { if (this.status == 404) { this.onerror.call(this); return; } - //Get the file name to save this file as - var filename = xhr.getResponseHeader("Content-Disposition"); + // Get the file name to save this file as + let filename = xhr.getResponseHeader("Content-Disposition"); if (!filename) { filename = @@ -2037,16 +2012,16 @@ define([ .substring(filename.indexOf("filename=") + 9) .replace(/"/g, ""); - //Replace any whitespaces + // Replace any whitespaces filename = filename.trim().replace(/ /g, "_"); - //For IE, we need to use the navigator API + // For IE, we need to use the navigator API if (navigator && navigator.msSaveOrOpenBlob) { navigator.msSaveOrOpenBlob(xhr.response, filename); } - //Other browsers can download it via a link + // Other browsers can download it via a link else { - var a = document.createElement("a"); + const a = document.createElement("a"); a.href = window.URL.createObjectURL(xhr.response); // xhr.response is a blob // Set the file name. @@ -2081,7 +2056,7 @@ define([ xhr.onprogress = function (e) { if (e.lengthComputable) { - var percent = (e.loaded / e.total) * 100; + const percent = (e.loaded / e.total) * 100; model.set("downloadPercent", percent); } }; @@ -2091,7 +2066,7 @@ define([ if (MetacatUI.appUserModel.get("loggedIn")) xhr.setRequestHeader( "Authorization", - "Bearer " + MetacatUI.appUserModel.get("token"), + `Bearer ${MetacatUI.appUserModel.get("token")}`, ); xhr.send(); @@ -2100,8 +2075,10 @@ define([ /** * Creates a file name for this DataONEObject and updates the `fileName` attribute */ - setMissingFileName: function () { - var objectFormats, filename, extension; + setMissingFileName() { + let objectFormats; + let filename; + let extension; objectFormats = MetacatUI.objectFormats.where({ formatId: this.get("formatId"), @@ -2110,28 +2087,28 @@ define([ extension = objectFormats[0].get("extension"); } - //Science metadata file names will use the title + // Science metadata file names will use the title if (this.get("type") == "Metadata") { filename = Array.isArray(this.get("title")) && this.get("title").length ? this.get("title")[0] : this.get("id"); } - //Resource maps will use a "resource_map_" prefix + // Resource maps will use a "resource_map_" prefix else if (this.get("type") == "DataPackage") { - filename = "resource_map_" + this.get("id"); + filename = `resource_map_${this.get("id")}`; extension = ".rdf.xml"; } - //All other object types will just use the id + // All other object types will just use the id else { filename = this.get("id"); } - //Replace all non-alphanumeric characters with underscores + // Replace all non-alphanumeric characters with underscores filename = filename.replace(/[^a-zA-Z0-9]/g, "_"); if (typeof extension !== "undefined") { - filename = filename + "." + extension; + filename = `${filename}.${extension}`; } this.set("fileName", filename); @@ -2139,24 +2116,22 @@ define([ /** * Creates a URL for viewing more information about this object - * @return {string} + * @returns {string} */ - createViewURL: function () { - return ( - MetacatUI.root + - "/view/" + - encodeURIComponent(this.get("seriesId") || this.get("id")) - ); + createViewURL() { + return `${MetacatUI.root}/view/${encodeURIComponent( + this.get("seriesId") || this.get("id"), + )}`; }, /** * Check if the seriesID or PID matches a DOI regex, and if so, return * a canonical IRI for the DOI. - * @return {string|null} - The canonical IRI for the DOI, or null if + * @returns {string|null} - The canonical IRI for the DOI, or null if * neither the seriesId nor the PID match a DOI regex. * @since 2.26.0 */ - getCanonicalDOIIRI: function () { + getCanonicalDOIIRI() { const id = this.get("id"); const seriesId = this.get("seriesId"); let DOI = null; @@ -2168,14 +2143,14 @@ define([ /** * Converts the identifier string to a string safe to use in an XML id attribute * @param {string} [id] - The ID string - * @return {string} - The XML-safe string + * @returns {string} - The XML-safe string */ - getXMLSafeID: function (id) { - if (typeof id == "undefined") { + getXMLSafeID(id) { + if (typeof id === "undefined") { var id = this.get("id"); } - //Replace XML id attribute invalid characters and patterns in the identifier + // Replace XML id attribute invalid characters and patterns in the identifier id = id .replace(/ -1; - }); + const programClass = _.filter( + instanceOfClass, + (className) => className.indexOf("#Program") > -1, + ); if (typeof programClass !== "undefined" && programClass.length) return "program"; - } else { - if (this.get("prov_generated").length || this.get("prov_used").length) - return "program"; - } + } else if ( + this.get("prov_generated").length || + this.get("prov_used").length + ) + return "program"; - //Determine the type via file format + // Determine the type via file format if (this.isSoftware()) return "program"; if (this.isData()) return "data"; @@ -2268,20 +2245,20 @@ define([ if (_.contains(pdfIds, this.get("formatId"))) return "PDF"; if (_.contains(annotationIds, this.get("formatId"))) return "annotation"; - else return "data"; + return "data"; }, /** * Checks the formatId of this model and determines if it is an image. * @returns {boolean} true if this data object is an image, false if it is other */ - isImage: function () { - //The list of formatIds that are images - var imageIds = ["image/gif", "image/jp2", "image/jpeg", "image/png"]; + isImage() { + // The list of formatIds that are images + const imageIds = ["image/gif", "image/jp2", "image/jpeg", "image/png"]; - //Does this data object match one of these IDs? + // Does this data object match one of these IDs? if (_.indexOf(imageIds, this.get("formatId")) == -1) return false; - else return true; + return true; }, /** @@ -2291,8 +2268,8 @@ define([ * as images {@link DataONEObject#isImage} or software {@link DataONEObject#isSoftware}. * @returns {boolean} true if this data object is a data file, false if it is other */ - isData: function () { - var dataIds = [ + isData() { + const dataIds = [ "application/atom+xml", "application/mathematica", "application/msword", @@ -2339,9 +2316,9 @@ define([ "video/x-ms-wmv", ]; - //Does this data object match one of these IDs? + // Does this data object match one of these IDs? if (_.indexOf(dataIds, this.get("formatId")) == -1) return false; - else return true; + return true; }, /** @@ -2351,9 +2328,9 @@ define([ * as images {@link DataONEObject#isImage} for display purposes. * @returns {boolean} true if this data object is a software file, false if it is other */ - isSoftware: function () { - //The list of formatIds that are programs - var softwareIds = [ + isSoftware() { + // The list of formatIds that are programs + const softwareIds = [ "text/x-python", "text/x-rsrc", "text/x-matlab", @@ -2361,22 +2338,22 @@ define([ "application/R", "application/x-ipynb+json", ]; - //Does this data object match one of these IDs? + // Does this data object match one of these IDs? if (_.indexOf(softwareIds, this.get("formatId")) == -1) return false; - else return true; + return true; }, /** * Checks the formatId of this model and determines if it a PDF. * @returns {boolean} true if this data object is a pdf, false if it is other */ - isPDF: function () { - //The list of formatIds that are images - var ids = ["application/pdf"]; + isPDF() { + // The list of formatIds that are images + const ids = ["application/pdf"]; - //Does this data object match one of these IDs? + // Does this data object match one of these IDs? if (_.indexOf(ids, this.get("formatId")) == -1) return false; - else return true; + return true; }, /** @@ -2388,7 +2365,7 @@ define([ * see https://github.com/DataONEorg/sem-prov-ontologies/blob/master/provenance/ProvONE/v1/provone.html * @param {string} className */ - setProvClass: function (className) { + setProvClass(className) { className = className.toLowerCase(); className = className.charAt(0).toUpperCase() + className.slice(1); @@ -2419,10 +2396,7 @@ define([ ) { this.set("prov_instanceOfClass", [this.PROV + className]); } else { - message = - "The given class name: " + - className + - " is not in the known ProvONE or PROV classes."; + message = `The given class name: ${className} is not in the known ProvONE or PROV classes.`; throw new Error(message); } }, @@ -2430,17 +2404,17 @@ define([ /** * Calculate a checksum for the object * @param {string} [algorithm] The algorithm to use, defaults to MD5 - * @return {string} A checksum plain JS object with value and algorithm attributes + * @returns {string} A checksum plain JS object with value and algorithm attributes */ - calculateChecksum: function (algorithm) { + calculateChecksum(algorithm) { var algorithm = algorithm || "MD5"; - var checksum = { algorithm: undefined, value: undefined }; - var hash; // The checksum hash - var file; // The file to be read by slicing - var reader; // The FileReader used to read each slice - var offset = 0; // Byte offset for reading slices - var sliceSize = Math.pow(2, 20); // 1MB slices - var model = this; + const checksum = { algorithm: undefined, value: undefined }; + let hash; // The checksum hash + let file; // The file to be read by slicing + let reader; // The FileReader used to read each slice + let offset = 0; // Byte offset for reading slices + const sliceSize = 2 ** 20; // 1MB slices + const model = this; // Do we have a file? if (this.get("uploadFile") instanceof Blob) { @@ -2448,7 +2422,7 @@ define([ reader = new FileReader(); /* Handle load errors */ reader.onerror = function (event) { - console.log("Error reading: " + event); + console.log(`Error reading: ${event}`); }; /* Show progress */ reader.onprogress = function (event) {}; @@ -2479,8 +2453,7 @@ define([ // TODO: Support SHA-1 // break; default: - message = - "The given algorithm: " + algorithm + " is not supported."; + message = `The given algorithm: ${algorithm} is not supported.`; throw new Error(message); } @@ -2488,9 +2461,12 @@ define([ * A helper function internal to calculateChecksum() used to slice * the file at the next offset by slice size */ + /** + * + */ function _seek() { - var calculated = false; - var slice; + let calculated = false; + let slice; // Digest the checksum when we're done calculating if (offset >= file.size) { hash.digest(); @@ -2506,11 +2482,10 @@ define([ /** * Checks if the pid or sid or given string is a DOI - * * @param {string} customString - Optional. An identifier string to check instead of the id and seriesId attributes on the model * @returns {boolean} True if it is a DOI */ - isDOI: function (customString) { + isDOI(customString) { return ( isDOI(customString) || isDOI(this.get("id")) || @@ -2522,58 +2497,55 @@ define([ * Creates an array of objects that represent Member Nodes that could possibly be this * object's authoritative MN. This function updates the `possibleAuthMNs` attribute on this model. */ - setPossibleAuthMNs: function () { - //Only do this for Coordinating Node MetacatUIs. + setPossibleAuthMNs() { + // Only do this for Coordinating Node MetacatUIs. if (MetacatUI.appModel.get("alternateRepositories").length) { - //Set the possibleAuthMNs attribute - var possibleAuthMNs = []; + // Set the possibleAuthMNs attribute + const possibleAuthMNs = []; - //If a datasource is already found for this Portal, move that to the top of the list of auth MNs - var datasource = this.get("datasource") || ""; + // If a datasource is already found for this Portal, move that to the top of the list of auth MNs + const datasource = this.get("datasource") || ""; if (datasource) { - //Find the MN object that matches the datasource node ID - var datasourceMN = _.findWhere( + // Find the MN object that matches the datasource node ID + const datasourceMN = _.findWhere( MetacatUI.appModel.get("alternateRepositories"), { identifier: datasource }, ); if (datasourceMN) { - //Clone the MN object and add it to the array - var clonedDatasourceMN = Object.assign({}, datasourceMN); + // Clone the MN object and add it to the array + const clonedDatasourceMN = { ...datasourceMN }; possibleAuthMNs.push(clonedDatasourceMN); } } - //If there is an active alternate repo, move that to the top of the list of auth MNs - var activeAltRepo = + // If there is an active alternate repo, move that to the top of the list of auth MNs + const activeAltRepo = MetacatUI.appModel.get("activeAlternateRepositoryId") || ""; if (activeAltRepo) { - var activeAltRepoMN = _.findWhere( + const activeAltRepoMN = _.findWhere( MetacatUI.appModel.get("alternateRepositories"), { identifier: activeAltRepo }, ); if (activeAltRepoMN) { - //Clone the MN object and add it to the array - var clonedActiveAltRepoMN = Object.assign({}, activeAltRepoMN); + // Clone the MN object and add it to the array + const clonedActiveAltRepoMN = { ...activeAltRepoMN }; possibleAuthMNs.push(clonedActiveAltRepoMN); } } - //Add all the other alternate repositories to the list of auth MNs - var otherPossibleAuthMNs = _.reject( + // Add all the other alternate repositories to the list of auth MNs + const otherPossibleAuthMNs = _.reject( MetacatUI.appModel.get("alternateRepositories"), - function (mn) { - return ( - mn.identifier == datasource || mn.identifier == activeAltRepo - ); - }, + (mn) => + mn.identifier == datasource || mn.identifier == activeAltRepo, ); - //Clone each MN object and add to the array - _.each(otherPossibleAuthMNs, function (mn) { - var clonedMN = Object.assign({}, mn); + // Clone each MN object and add to the array + _.each(otherPossibleAuthMNs, (mn) => { + const clonedMN = { ...mn }; possibleAuthMNs.push(clonedMN); }); - //Update this model + // Update this model this.set("possibleAuthMNs", possibleAuthMNs); } }, @@ -2585,13 +2557,13 @@ define([ * models were created with `id`s with new line and white space characters (e.g. `\n urn:uuid:1234...`) * @param {object} json - The Solr document as a JS Object, which will be directly altered */ - removeWhiteSpaceFromSolrFields: function (json) { - if (typeof json.resourceMap == "string") { + removeWhiteSpaceFromSolrFields(json) { + if (typeof json.resourceMap === "string") { json.resourceMap = json.resourceMap.trim(); } else if (Array.isArray(json.resourceMap)) { - let newResourceMapIds = []; - _.each(json.resourceMap, function (rMapId) { - if (typeof rMapId == "string") { + const newResourceMapIds = []; + _.each(json.resourceMap, (rMapId) => { + if (typeof rMapId === "string") { newResourceMapIds.push(rMapId.trim()); } }); @@ -2606,15 +2578,15 @@ define([ * Generate a unique identifier to be used as an XML id attribute * @returns {string} The identifier string that was generated */ - generateId: function () { - var idStr = ""; // the id to return - var length = 30; // the length of the generated string - var chars = + generateId() { + let idStr = ""; // the id to return + const length = 30; // the length of the generated string + const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".split( "", ); - for (var i = 0; i < length; i++) { + for (let i = 0; i < length; i++) { idStr += chars[Math.floor(Math.random() * chars.length)]; } return idStr; diff --git a/src/js/models/PackageModel.js b/src/js/models/PackageModel.js index 8c29e56e3..7ed421469 100644 --- a/src/js/models/PackageModel.js +++ b/src/js/models/PackageModel.js @@ -6,19 +6,19 @@ define([ "md5", "rdflib", "models/SolrResult", -], function ($, _, Backbone, uuid, md5, rdf, SolrResult) { +], ($, _, Backbone, uuid, md5, rdf, SolrResult) => { // Package Model // ------------------ var PackageModel = Backbone.Model.extend( /** @lends PackageModel.prototype */ { // This model contains information about a package/resource map - defaults: function () { + defaults() { return { - id: null, //The id of the resource map/package itself - url: null, //the URL to retrieve this package - memberId: null, //An id of a member of the data package - indexDoc: null, //A SolrResult object representation of the resource map - size: 0, //The number of items aggregated in this package + id: null, // The id of the resource map/package itself + url: null, // the URL to retrieve this package + memberId: null, // An id of a member of the data package + indexDoc: null, // A SolrResult object representation of the resource map + size: 0, // The number of items aggregated in this package totalSize: null, formattedSize: "", formatId: null, @@ -35,14 +35,14 @@ define([ derivationPackages: [], sourceDocs: [], derivationDocs: [], - relatedModels: [], //A condensed list of all SolrResult models related to this package in some way + relatedModels: [], // A condensed list of all SolrResult models related to this package in some way parentPackageMetadata: null, - //If true, when the member objects are retrieved, archived content will be included + // If true, when the member objects are retrieved, archived content will be included getArchivedMembers: false, }; }, - //Define the namespaces + // Define the namespaces namespaces: { RDF: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", FOAF: "http://xmlns.com/foaf/0.1/", @@ -82,14 +82,14 @@ define([ // The RDF graph representing this data package dataPackageGraph: null, - initialize: function (options) { + initialize(options) { this.setURL(); // Create an initial RDF graph this.dataPackageGraph = rdf.graph(); }, - setURL: function () { + setURL() { if (MetacatUI.appModel.get("packageServiceUrl")) this.set( "url", @@ -101,7 +101,7 @@ define([ /* * Set the URL for fetch */ - url: function () { + url() { return ( MetacatUI.appModel.get("objectServiceUrl") + encodeURIComponent(this.get("id")) @@ -109,39 +109,34 @@ define([ }, /* Retrieve the id of the resource map/package that this id belongs to */ - getMembersByMemberID: function (id) { + getMembersByMemberID(id) { this.pending = true; if (typeof id === "undefined" || !id) var id = this.memberId; - var model = this; - - //Get the id of the resource map for this member - var provFlList = - MetacatUI.appSearchModel.getProvFlList() + "prov_instanceOfClass,"; - var query = - "fl=resourceMap,fileName,read:read_count_i,obsoletedBy,size,formatType,formatId,id,datasource,title,origin,pubDate,dateUploaded,isPublic,isService,serviceTitle,serviceEndpoint,serviceOutput,serviceDescription," + - provFlList + - "&rows=1" + - "&q=id:%22" + - encodeURIComponent(id) + - "%22" + - "&wt=json"; - - var requestSettings = { + const model = this; + + // Get the id of the resource map for this member + const provFlList = `${MetacatUI.appSearchModel.getProvFlList()}prov_instanceOfClass,`; + const query = + `fl=resourceMap,fileName,read:read_count_i,obsoletedBy,size,formatType,formatId,id,datasource,title,origin,pubDate,dateUploaded,isPublic,isService,serviceTitle,serviceEndpoint,serviceOutput,serviceDescription,${provFlList}&rows=1` + + `&q=id:%22${encodeURIComponent(id)}%22` + + `&wt=json`; + + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (data, textStatus, xhr) { - //There should be only one response since we searched by id + success(data, textStatus, xhr) { + // There should be only one response since we searched by id if (typeof data.response.docs !== "undefined") { - var doc = data.response.docs[0]; + const doc = data.response.docs[0]; - //Is this document a resource map itself? + // Is this document a resource map itself? if (doc.formatId == "http://www.openarchives.org/ore/terms") { - model.set("id", doc.id); //this is the package model ID - model.set("members", new Array()); //Reset the member list + model.set("id", doc.id); // this is the package model ID + model.set("members", new Array()); // Reset the member list model.getMembers(); } - //If there is no resource map, then this is the only document to in this package + // If there is no resource map, then this is the only document to in this package else if ( typeof doc.resourceMap === "undefined" || !doc.resourceMap @@ -169,35 +164,33 @@ define([ /* Get all the members of a resource map/package based on the id attribute of this model. * Create a SolrResult model for each member and save it in the members[] attribute of this model. */ - getMembers: function (options) { + getMembers(options) { this.pending = true; - var model = this, - members = [], - pids = []; //Keep track of each object pid - - //*** Find all the files that are a part of this resource map and the resource map itself - var provFlList = MetacatUI.appSearchModel.getProvFlList(); - var query = - "fl=resourceMap,fileName,obsoletes,obsoletedBy,size,formatType,formatId,id,datasource," + - "rightsHolder,dateUploaded,archived,title,origin,prov_instanceOfClass,isDocumentedBy,isPublic" + - "&rows=1000" + - "&q=%28resourceMap:%22" + - encodeURIComponent(this.id) + - "%22%20OR%20id:%22" + - encodeURIComponent(this.id) + - "%22%29" + - "&wt=json"; + const model = this; + const members = []; + const pids = []; // Keep track of each object pid + + //* ** Find all the files that are a part of this resource map and the resource map itself + const provFlList = MetacatUI.appSearchModel.getProvFlList(); + let query = + `fl=resourceMap,fileName,obsoletes,obsoletedBy,size,formatType,formatId,id,datasource,` + + `rightsHolder,dateUploaded,archived,title,origin,prov_instanceOfClass,isDocumentedBy,isPublic` + + `&rows=1000` + + `&q=%28resourceMap:%22${encodeURIComponent( + this.id, + )}%22%20OR%20id:%22${encodeURIComponent(this.id)}%22%29` + + `&wt=json`; if (this.get("getArchivedMembers")) { query += "&archived=archived:*"; } - var requestSettings = { + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (data, textStatus, xhr) { - //Separate the resource maps from the data/metadata objects - _.each(data.response.docs, function (doc) { + success(data, textStatus, xhr) { + // Separate the resource maps from the data/metadata objects + _.each(data.response.docs, (doc) => { if (doc.id == model.get("id")) { model.set("indexDoc", doc); model.set(doc); @@ -211,7 +204,7 @@ define([ pids.push(doc.id); if (doc.formatType == "RESOURCE") { - var newPckg = new PackageModel(doc); + const newPckg = new PackageModel(doc); newPckg.set("parentPackage", model); members.push(newPckg); } else members.push(new SolrResult(doc)); @@ -240,12 +233,12 @@ define([ /* * Send custom options to the Backbone.Model.fetch() function */ - fetch: function (options) { + fetch(options) { if (!options) var options = {}; - var fetchOptions = _.extend({ dataType: "text" }, options); + let fetchOptions = _.extend({ dataType: "text" }, options); - //Add the authorization options + // Add the authorization options fetchOptions = _.extend( fetchOptions, MetacatUI.appUserModel.createAjaxSettings(), @@ -257,25 +250,25 @@ define([ /* * Deserialize a Package from OAI-ORE RDF XML */ - parse: function (response, options) { - //Save the raw XML in case it needs to be used later + parse(response, options) { + // Save the raw XML in case it needs to be used later this.set("objectXML", $.parseHTML(response)); - //Define the namespaces - var RDF = rdf.Namespace(this.namespaces.RDF), - FOAF = rdf.Namespace(this.namespaces.FOAF), - OWL = rdf.Namespace(this.namespaces.OWL), - DC = rdf.Namespace(this.namespaces.DC), - ORE = rdf.Namespace(this.namespaces.ORE), - DCTERMS = rdf.Namespace(this.namespaces.DCTERMS), - CITO = rdf.Namespace(this.namespaces.CITO); - - var memberStatements = [], - memberURIParts, - memberPIDStr, - memberPID, - memberModel, - models = []; // the models returned by parse() + // Define the namespaces + const RDF = rdf.Namespace(this.namespaces.RDF); + const FOAF = rdf.Namespace(this.namespaces.FOAF); + const OWL = rdf.Namespace(this.namespaces.OWL); + const DC = rdf.Namespace(this.namespaces.DC); + const ORE = rdf.Namespace(this.namespaces.ORE); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const CITO = rdf.Namespace(this.namespaces.CITO); + + let memberStatements = []; + let memberURIParts; + let memberPIDStr; + let memberPID; + let memberModel; + const models = []; // the models returned by parse() try { rdf.parse( @@ -295,15 +288,15 @@ define([ undefined, ); - var memberPIDs = [], - members = [], - currentMembers = this.get("members"), - model = this; + const memberPIDs = []; + const members = []; + const currentMembers = this.get("members"); + const model = this; // Get system metadata for each member to eval the formatId _.each( memberStatements, - function (memberStatement) { + (memberStatement) => { memberURIParts = memberStatement.object.value.split("/"); memberPIDStr = _.last(memberURIParts); memberPID = decodeURIComponent(memberPIDStr); @@ -311,16 +304,17 @@ define([ if (memberPID) { memberPIDs.push(memberPID); - //Get the current model from the member list, if it exists - var existingModel = _.find(currentMembers, function (m) { - return m.get("id") == decodeURIComponent(memberPID); - }); + // Get the current model from the member list, if it exists + const existingModel = _.find( + currentMembers, + (m) => m.get("id") == decodeURIComponent(memberPID), + ); - //Add the existing model to the new member list + // Add the existing model to the new member list if (existingModel) { members.push(existingModel); } - //Or create a new SolrResult model + // Or create a new SolrResult model else { members.push( new SolrResult({ @@ -333,32 +327,31 @@ define([ this, ); - //Get the documents relationships - var documentedByStatements = this.dataPackageGraph.statementsMatching( + // Get the documents relationships + const documentedByStatements = + this.dataPackageGraph.statementsMatching( undefined, CITO("isDocumentedBy"), undefined, undefined, - ), - metadataPids = []; + ); + const metadataPids = []; _.each( documentedByStatements, - function (statement) { - //Get the data object that is documentedBy metadata - var dataPid = decodeURIComponent( - _.last(statement.subject.value.split("/")), - ), - dataObj = _.find(members, function (m) { - return m.get("id") == dataPid; - }), - metadataPid = _.last(statement.object.value.split("/")); + (statement) => { + // Get the data object that is documentedBy metadata + const dataPid = decodeURIComponent( + _.last(statement.subject.value.split("/")), + ); + const dataObj = _.find(members, (m) => m.get("id") == dataPid); + const metadataPid = _.last(statement.object.value.split("/")); - //Save this as a metadata model + // Save this as a metadata model metadataPids.push(metadataPid); - //Set the isDocumentedBy field - var isDocBy = dataObj.get("isDocumentedBy"); + // Set the isDocumentedBy field + let isDocBy = dataObj.get("isDocumentedBy"); if (isDocBy && Array.isArray(isDocBy)) isDocBy.push(metadataPid); else if (isDocBy && !Array.isArray(isDocBy)) isDocBy = [isDocBy, metadataPid]; @@ -369,13 +362,13 @@ define([ this, ); - //Get the metadata models and mark them as metadata - var metadataModels = _.filter(members, function (m) { - return _.contains(metadataPids, m.get("id")); - }); + // Get the metadata models and mark them as metadata + const metadataModels = _.filter(members, (m) => + _.contains(metadataPids, m.get("id")), + ); _.invoke(metadataModels, "set", "formatType", "METADATA"); - //Keep the pids in the collection for easy access later + // Keep the pids in the collection for easy access later this.set("memberIds", memberPIDs); this.set("members", members); } catch (error) { @@ -387,17 +380,17 @@ define([ /* * Overwrite the Backbone.Model.save() function to set custom options */ - save: function (attrs, options) { + save(attrs, options) { if (!options) var options = {}; - //Get the system metadata first + // Get the system metadata first if (!this.get("hasSystemMetadata")) { - var model = this; + const model = this; var requestSettings = { url: MetacatUI.appModel.get("metaServiceUrl") + encodeURIComponent(this.get("id")), - success: function (response) { + success(response) { model.parseSysMeta(response); model.set("hasSystemMetadata", true); @@ -414,26 +407,26 @@ define([ return; } - //Create a new pid if we are updating the object + // Create a new pid if we are updating the object if (!options.sysMetaOnly) { - //Set a new id + // Set a new id var oldPid = this.get("id"); this.set("oldPid", oldPid); - this.set("id", "urn:uuid:" + uuid.v4()); + this.set("id", `urn:uuid:${uuid.v4()}`); this.set("obsoletes", oldPid); this.set("obsoletedBy", null); this.set("archived", false); } - //Create the system metadata - var sysMetaXML = this.serializeSysMeta(); + // Create the system metadata + const sysMetaXML = this.serializeSysMeta(); - //Send the new pid, old pid, and system metadata - var xmlBlob = new Blob([sysMetaXML], { type: "application/xml" }); - var formData = new FormData(); + // Send the new pid, old pid, and system metadata + const xmlBlob = new Blob([sysMetaXML], { type: "application/xml" }); + const formData = new FormData(); formData.append("sysmeta", xmlBlob, "sysmeta"); - //Let's try updating the system metadata for now + // Let's try updating the system metadata for now if (options.sysMetaOnly) { formData.append("pid", this.get("id")); @@ -444,8 +437,8 @@ define([ contentType: false, processData: false, data: formData, - success: function (response) {}, - error: function (data) { + success(response) {}, + error(data) { console.log("error updating system metadata"); }, }; @@ -456,20 +449,20 @@ define([ ), ); } else { - //Add the ids to the form data + // Add the ids to the form data formData.append("newPid", this.get("id")); formData.append("pid", oldPid); - //Create the resource map XML - var mapXML = this.serialize(); - var mapBlob = new Blob([mapXML], { type: "application/xml" }); + // Create the resource map XML + const mapXML = this.serialize(); + const mapBlob = new Blob([mapXML], { type: "application/xml" }); formData.append("object", mapBlob); - //Get the size of the new resource map + // Get the size of the new resource map this.set("size", mapBlob.size); - //Get the new checksum of the resource map - var checksum = md5(mapXML); + // Get the new checksum of the resource map + const checksum = md5(mapXML); this.set("checksum", checksum); var requestSettings = { @@ -479,8 +472,8 @@ define([ contentType: false, processData: false, data: formData, - success: function (response) {}, - error: function (data) { + success(response) {}, + error(data) { console.log("error udpating object"); }, }; @@ -493,15 +486,15 @@ define([ } }, - parseSysMeta: function (response) { + parseSysMeta(response) { this.set("sysMetaXML", $.parseHTML(response)); - var responseDoc = $.parseHTML(response), - systemMetadata, - prependXML = "", - appendXML = ""; + const responseDoc = $.parseHTML(response); + let systemMetadata; + const prependXML = ""; + const appendXML = ""; - for (var i = 0; i < responseDoc.length; i++) { + for (let i = 0; i < responseDoc.length; i++) { if ( responseDoc[i].nodeType == 1 && responseDoc[i].localName.indexOf("systemmetadata") > -1 @@ -509,10 +502,10 @@ define([ systemMetadata = responseDoc[i]; } - //Parse the XML to JSON - var sysMetaValues = this.toJson(systemMetadata), - camelCasedValues = {}; - //Convert the JSON to a camel-cased version, which matches Solr and is easier to work with in code + // Parse the XML to JSON + const sysMetaValues = this.toJson(systemMetadata); + const camelCasedValues = {}; + // Convert the JSON to a camel-cased version, which matches Solr and is easier to work with in code _.each( Object.keys(sysMetaValues), function (key) { @@ -521,72 +514,74 @@ define([ this, ); - //Set the values on the model + // Set the values on the model this.set(camelCasedValues); }, - serialize: function () { - //Create an RDF serializer + serialize() { + // Create an RDF serializer var serializer = rdf.Serializer(); serializer.store = this.dataPackageGraph; - //Define the namespaces - var ORE = rdf.Namespace(this.namespaces.ORE), - CITO = rdf.Namespace(this.namespaces.CITO); + // Define the namespaces + const ORE = rdf.Namespace(this.namespaces.ORE); + const CITO = rdf.Namespace(this.namespaces.CITO); - //Get the pid of this package - depends on whether we are updating or creating a resource map - var pid = this.get("id"), - oldPid = this.get("oldPid"), - updating = oldPid ? true : false; + // Get the pid of this package - depends on whether we are updating or creating a resource map + const pid = this.get("id"); + const oldPid = this.get("oldPid"); + const updating = !!oldPid; - //Update the pids in the RDF graph only if we are updating the resource map with a new pid + // Update the pids in the RDF graph only if we are updating the resource map with a new pid if (updating) { - //Find the identifier statement in the resource map - var idNode = rdf.lit(oldPid), - idStatement = this.dataPackageGraph.statementsMatching( - undefined, - undefined, - idNode, - ); + // Find the identifier statement in the resource map + const idNode = rdf.lit(oldPid); + const idStatement = this.dataPackageGraph.statementsMatching( + undefined, + undefined, + idNode, + ); - //Get the CN Resolve Service base URL from the resource map (mostly important in dev environments where it will not always be cn.dataone.org) - var cnResolveUrl = idStatement[0].subject.value.substring( + // Get the CN Resolve Service base URL from the resource map (mostly important in dev environments where it will not always be cn.dataone.org) + const cnResolveUrl = idStatement[0].subject.value.substring( 0, idStatement[0].subject.value.indexOf(oldPid), ); this.dataPackageGraph.cnResolveUrl = cnResolveUrl; - //Create variations of the resource map ID using the resolve URL so we can always find it in the RDF graph - var oldPidVariations = [ + // Create variations of the resource map ID using the resolve URL so we can always find it in the RDF graph + const oldPidVariations = [ oldPid, encodeURIComponent(oldPid), cnResolveUrl + encodeURIComponent(oldPid), ]; - //Get all the isAggregatedBy statements - var aggregationNode = rdf.sym( - cnResolveUrl + encodeURIComponent(oldPid) + "#aggregation", - ), - aggByStatements = this.dataPackageGraph.statementsMatching( - undefined, - ORE("isAggregatedBy"), - ); + // Get all the isAggregatedBy statements + const aggregationNode = rdf.sym( + `${cnResolveUrl + encodeURIComponent(oldPid)}#aggregation`, + ); + const aggByStatements = this.dataPackageGraph.statementsMatching( + undefined, + ORE("isAggregatedBy"), + ); - //Using the isAggregatedBy statements, find all the DataONE object ids in the RDF graph - var idsFromXML = []; + // Using the isAggregatedBy statements, find all the DataONE object ids in the RDF graph + const idsFromXML = []; _.each( aggByStatements, - function (statement) { - //Check if the resource map ID is the old existing id, so we don't collect ids that are not about this resource map + (statement) => { + // Check if the resource map ID is the old existing id, so we don't collect ids that are not about this resource map if ( - _.find(oldPidVariations, function (oldPidV) { - return oldPidV + "#aggregation" == statement.object.value; - }) + _.find( + oldPidVariations, + (oldPidV) => + `${oldPidV}#aggregation` == statement.object.value, + ) ) { - var statementID = statement.subject.value; + const statementID = statement.subject.value; idsFromXML.push(statementID); - //Add variations of the ID so we make sure we account for all the ways they exist in the RDF XML + // Add variations of the ID so we make sure we account for all the ways they exist in the RDF XML if (statementID.indexOf(cnResolveUrl) > -1) idsFromXML.push( statementID.substring(statementID.lastIndexOf("/") + 1), @@ -600,67 +595,66 @@ define([ this, ); - //Get all the ids from this model - var idsFromModel = _.invoke(this.get("members"), "get", "id"); + // Get all the ids from this model + const idsFromModel = _.invoke(this.get("members"), "get", "id"); - //Find the difference between the model IDs and the XML IDs to get a list of added members - var addedIds = _.without( + // Find the difference between the model IDs and the XML IDs to get a list of added members + const addedIds = _.without( _.difference(idsFromModel, idsFromXML), oldPidVariations, ); - //Create variations of all these ids too - var allMemberIds = idsFromModel; - _.each(idsFromModel, function (id) { + // Create variations of all these ids too + const allMemberIds = idsFromModel; + _.each(idsFromModel, (id) => { allMemberIds.push(cnResolveUrl + encodeURIComponent(id)); }); - //Remove any other isAggregatedBy statements that are not listed as members of this model + // Remove any other isAggregatedBy statements that are not listed as members of this model _.each( aggByStatements, function (statement) { if (!_.contains(allMemberIds, statement.subject.value)) this.removeFromAggregation(statement.subject.value); else if ( - _.find(oldPidVariations, function (oldPidV) { - return oldPidV + "#aggregation" == statement.object.value; - }) + _.find( + oldPidVariations, + (oldPidV) => + `${oldPidV}#aggregation` == statement.object.value, + ) ) - statement.object.value = - cnResolveUrl + encodeURIComponent(pid) + "#aggregation"; + statement.object.value = `${cnResolveUrl + encodeURIComponent(pid)}#aggregation`; }, this, ); - //Change all the statements in the RDF where the aggregation is the subject, to reflect the new resource map ID - var aggregationSubjStatements = + // Change all the statements in the RDF where the aggregation is the subject, to reflect the new resource map ID + const aggregationSubjStatements = this.dataPackageGraph.statementsMatching(aggregationNode); - _.each(aggregationSubjStatements, function (statement) { - statement.subject.value = - cnResolveUrl + encodeURIComponent(pid) + "#aggregation"; + _.each(aggregationSubjStatements, (statement) => { + statement.subject.value = `${cnResolveUrl + encodeURIComponent(pid)}#aggregation`; }); - //Change all the statements in the RDF where the aggregation is the object, to reflect the new resource map ID - var aggregationObjStatements = + // Change all the statements in the RDF where the aggregation is the object, to reflect the new resource map ID + const aggregationObjStatements = this.dataPackageGraph.statementsMatching( undefined, undefined, aggregationNode, ); - _.each(aggregationObjStatements, function (statement) { - statement.object.value = - cnResolveUrl + encodeURIComponent(pid) + "#aggregation"; + _.each(aggregationObjStatements, (statement) => { + statement.object.value = `${cnResolveUrl + encodeURIComponent(pid)}#aggregation`; }); - //Change all the resource map subject nodes in the RDF graph - var rMapNode = rdf.sym(cnResolveUrl + encodeURIComponent(oldPid)); - var rMapStatements = + // Change all the resource map subject nodes in the RDF graph + const rMapNode = rdf.sym(cnResolveUrl + encodeURIComponent(oldPid)); + const rMapStatements = this.dataPackageGraph.statementsMatching(rMapNode); - _.each(rMapStatements, function (statement) { + _.each(rMapStatements, (statement) => { statement.subject.value = cnResolveUrl + encodeURIComponent(pid); }); - //Change the idDescribedBy statement - var isDescribedByStatements = + // Change the idDescribedBy statement + const isDescribedByStatements = this.dataPackageGraph.statementsMatching( undefined, ORE("isDescribedBy"), @@ -669,7 +663,7 @@ define([ if (isDescribedByStatements[0]) isDescribedByStatements[0].object.value = pid; - //Add nodes for new package members + // Add nodes for new package members _.each( addedIds, function (id) { @@ -678,26 +672,26 @@ define([ this, ); - //Change all the resource map identifier literal node in the RDF graph + // Change all the resource map identifier literal node in the RDF graph if (idStatement[0]) idStatement[0].object.value = pid; } - //Now serialize the RDF XML + // Now serialize the RDF XML var serializer = rdf.Serializer(); serializer.store = this.dataPackageGraph; - var xmlString = serializer.statementsToXML( + const xmlString = serializer.statementsToXML( this.dataPackageGraph.statements, ); return xmlString; }, - serializeSysMeta: function () { - //Get the system metadata XML that currently exists in the system - var xml = $(this.get("sysMetaXML")); + serializeSysMeta() { + // Get the system metadata XML that currently exists in the system + const xml = $(this.get("sysMetaXML")); - //Update the system metadata values + // Update the system metadata values xml.find("serialversion").text(this.get("serialVersion") || "0"); xml.find("identifier").text(this.get("newPid") || this.get("id")); xml.find("formatid").text(this.get("formatId")); @@ -743,49 +737,48 @@ define([ xml.find("obsoletedby").text(this.get("obsoletedBy")); else xml.find("obsoletedby").remove(); - //Write the access policy - var accessPolicyXML = "\n"; - _.each(this.get("accesspolicy"), function (policy, policyType, all) { - var fullPolicy = all[policyType]; + // Write the access policy + let accessPolicyXML = "\n"; + _.each(this.get("accesspolicy"), (policy, policyType, all) => { + const fullPolicy = all[policyType]; - _.each(fullPolicy, function (policyPart) { - accessPolicyXML += "\t<" + policyType + ">\n"; + _.each(fullPolicy, (policyPart) => { + accessPolicyXML += `\t<${policyType}>\n`; - accessPolicyXML += - "\t\t" + policyPart.subject + "\n"; + accessPolicyXML += `\t\t${policyPart.subject}\n`; - var permissions = Array.isArray(policyPart.permission) + const permissions = Array.isArray(policyPart.permission) ? policyPart.permission : [policyPart.permission]; - _.each(permissions, function (perm) { - accessPolicyXML += "\t\t" + perm + "\n"; + _.each(permissions, (perm) => { + accessPolicyXML += `\t\t${perm}\n`; }); - accessPolicyXML += "\t\n"; + accessPolicyXML += `\t\n`; }); }); accessPolicyXML += ""; - //Replace the old access policy with the new one + // Replace the old access policy with the new one xml.find("accesspolicy").replaceWith(accessPolicyXML); - var xmlString = $(document.createElement("div")) + let xmlString = $(document.createElement("div")) .append(xml.clone()) .html(); - //Now camel case the nodes + // Now camel case the nodes _.each( Object.keys(this.sysMetaNodeMap), function (name, i, allNodeNames) { - var regEx = new RegExp("<" + name, "g"); + var regEx = new RegExp(`<${name}`, "g"); xmlString = xmlString.replace( regEx, - "<" + this.sysMetaNodeMap[name], + `<${this.sysMetaNodeMap[name]}`, ); - var regEx = new RegExp(name + ">", "g"); + var regEx = new RegExp(`${name}>`, "g"); xmlString = xmlString.replace( regEx, - this.sysMetaNodeMap[name] + ">", + `${this.sysMetaNodeMap[name]}>`, ); }, this, @@ -796,8 +789,8 @@ define([ return xmlString; }, - //Adds a new object to the resource map RDF graph - addToAggregation: function (id) { + // Adds a new object to the resource map RDF graph + addToAggregation(id) { if (id.indexOf(this.dataPackageGraph.cnResolveUrl) < 0) var fullID = this.dataPackageGraph.cnResolveUrl + encodeURIComponent(id); @@ -808,80 +801,78 @@ define([ ); } - //Initialize the namespaces - var ORE = rdf.Namespace(this.namespaces.ORE), - DCTERMS = rdf.Namespace(this.namespaces.DCTERMS), - XML = rdf.Namespace(this.namespaces.XML), - CITO = rdf.Namespace(this.namespaces.CITO); - - //Create a node for this object, the identifier, the resource map, and the aggregation - var objectNode = rdf.sym(fullID), - mapNode = rdf.sym( - this.dataPackageGraph.cnResolveUrl + - encodeURIComponent(this.get("id")), - ), - aggNode = rdf.sym( + // Initialize the namespaces + const ORE = rdf.Namespace(this.namespaces.ORE); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const XML = rdf.Namespace(this.namespaces.XML); + const CITO = rdf.Namespace(this.namespaces.CITO); + + // Create a node for this object, the identifier, the resource map, and the aggregation + const objectNode = rdf.sym(fullID); + const mapNode = rdf.sym( + this.dataPackageGraph.cnResolveUrl + + encodeURIComponent(this.get("id")), + ); + const aggNode = rdf.sym( + `${ this.dataPackageGraph.cnResolveUrl + - encodeURIComponent(this.get("id")) + - "#aggregation", - ), - idNode = rdf.literal(id, undefined, XML("string")); + encodeURIComponent(this.get("id")) + }#aggregation`, + ); + const idNode = rdf.literal(id, undefined, XML("string")); - //Add the statement: this object isAggregatedBy the resource map aggregation + // Add the statement: this object isAggregatedBy the resource map aggregation this.dataPackageGraph.addStatement( rdf.st(objectNode, ORE("isAggregatedBy"), aggNode), ); - //Add the statement: The resource map aggregation aggregates this object + // Add the statement: The resource map aggregation aggregates this object this.dataPackageGraph.addStatement( rdf.st(aggNode, ORE("aggregates"), objectNode), ); - //Add the statement: This object has the identifier {id} + // Add the statement: This object has the identifier {id} this.dataPackageGraph.addStatement( rdf.st(objectNode, DCTERMS("identifier"), idNode), ); - //Find the metadata doc that describes this object - var model = _.find(this.get("members"), function (m) { - return m.get("id") == id; - }), - isDocBy = model.get("isDocumentedBy"); + // Find the metadata doc that describes this object + const model = _.find(this.get("members"), (m) => m.get("id") == id); + const isDocBy = model.get("isDocumentedBy"); - //If this object is documented by any metadata... + // If this object is documented by any metadata... if (isDocBy) { - //Get the ids of all the metadata objects in this package - var metadataInPackage = _.compact( - _.map(this.get("members"), function (m) { + // Get the ids of all the metadata objects in this package + const metadataInPackage = _.compact( + _.map(this.get("members"), (m) => { if (m.get("formatType") == "METADATA") return m.get("id"); }), ); - //Find the metadata IDs that are in this package that also documents this data object - var metadataIds = Array.isArray(isDocBy) + // Find the metadata IDs that are in this package that also documents this data object + const metadataIds = Array.isArray(isDocBy) ? _.intersection(metadataInPackage, isDocBy) : _.intersection(metadataInPackage, [isDocBy]); - //For each metadata that documents this object, add a CITO:isDocumentedBy and CITO:documents statement + // For each metadata that documents this object, add a CITO:isDocumentedBy and CITO:documents statement _.each( metadataIds, function (metaId) { - //Create the named nodes and statements - var memberNode = rdf.sym( - this.dataPackageGraph.cnResolveUrl + encodeURIComponent(id), - ), - metadataNode = rdf.sym( - this.dataPackageGraph.cnResolveUrl + - encodeURIComponent(metaId), - ), - isDocByStatement = rdf.st( - memberNode, - CITO("isDocumentedBy"), - metadataNode, - ), - documentsStatement = rdf.st( - metadataNode, - CITO("documents"), - memberNode, - ); - //Add the statements + // Create the named nodes and statements + const memberNode = rdf.sym( + this.dataPackageGraph.cnResolveUrl + encodeURIComponent(id), + ); + const metadataNode = rdf.sym( + this.dataPackageGraph.cnResolveUrl + encodeURIComponent(metaId), + ); + const isDocByStatement = rdf.st( + memberNode, + CITO("isDocumentedBy"), + metadataNode, + ); + const documentsStatement = rdf.st( + metadataNode, + CITO("documents"), + memberNode, + ); + // Add the statements this.dataPackageGraph.addStatement(isDocByStatement); this.dataPackageGraph.addStatement(documentsStatement); }, @@ -890,108 +881,108 @@ define([ } }, - removeFromAggregation: function (id) { + removeFromAggregation(id) { if (!id.indexOf(this.dataPackageGraph.cnResolveUrl)) id = this.dataPackageGraph.cnResolveUrl + encodeURIComponent(id); - var removedObjNode = rdf.sym(id), - statements = _.union( - this.dataPackageGraph.statementsMatching( - undefined, - undefined, - removedObjNode, - ), - this.dataPackageGraph.statementsMatching(removedObjNode), - ); + const removedObjNode = rdf.sym(id); + const statements = _.union( + this.dataPackageGraph.statementsMatching( + undefined, + undefined, + removedObjNode, + ), + this.dataPackageGraph.statementsMatching(removedObjNode), + ); this.dataPackageGraph.removeStatements(statements); }, - getParentMetadata: function () { - var rMapIds = this.get("resourceMap"); + getParentMetadata() { + const rMapIds = this.get("resourceMap"); - //Create a query that searches for any resourceMap with an id matching one of the parents OR an id that matches one of the parents. - //This will return all members of the parent resource maps AND the parent resource maps themselves - var rMapQuery = "", - idQuery = ""; + // Create a query that searches for any resourceMap with an id matching one of the parents OR an id that matches one of the parents. + // This will return all members of the parent resource maps AND the parent resource maps themselves + let rMapQuery = ""; + let idQuery = ""; if (Array.isArray(rMapIds) && rMapIds.length > 1) { - _.each(rMapIds, function (id, i, ids) { - //At the begininng of the list of ids + _.each(rMapIds, (id, i, ids) => { + // At the begininng of the list of ids if (rMapQuery.length == 0) { rMapQuery += "resourceMap:("; idQuery += "id:("; } - //The id - rMapQuery += "%22" + encodeURIComponent(id) + "%22"; - idQuery += "%22" + encodeURIComponent(id) + "%22"; + // The id + rMapQuery += `%22${encodeURIComponent(id)}%22`; + idQuery += `%22${encodeURIComponent(id)}%22`; - //At the end of the list of ids + // At the end of the list of ids if (i + 1 == ids.length) { rMapQuery += ")"; idQuery += ")"; } - //In-between each id + // In-between each id else { rMapQuery += " OR "; idQuery += " OR "; } }); } else { - //When there is just one parent, the query is simple - var rMapId = Array.isArray(rMapIds) ? rMapIds[0] : rMapIds; - rMapQuery += "resourceMap:%22" + encodeURIComponent(rMapId) + "%22"; - idQuery += "id:%22" + encodeURIComponent(rMapId) + "%22"; + // When there is just one parent, the query is simple + const rMapId = Array.isArray(rMapIds) ? rMapIds[0] : rMapIds; + rMapQuery += `resourceMap:%22${encodeURIComponent(rMapId)}%22`; + idQuery += `id:%22${encodeURIComponent(rMapId)}%22`; } - var query = - "fl=title,id,obsoletedBy,resourceMap" + - "&wt=json" + - "&group=true&group.field=formatType&group.limit=-1" + - "&q=((formatType:METADATA AND " + - rMapQuery + - ") OR " + - idQuery + - ")"; - - var model = this; - var requestSettings = { + const query = + `fl=title,id,obsoletedBy,resourceMap` + + `&wt=json` + + `&group=true&group.field=formatType&group.limit=-1` + + `&q=((formatType:METADATA AND ${rMapQuery}) OR ${idQuery})`; + + const model = this; + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (data, textStatus, xhr) { - var results = data.grouped.formatType.groups, - resourceMapGroup = _.where(results, { - groupValue: "RESOURCE", - })[0], - rMapList = resourceMapGroup ? resourceMapGroup.doclist : null, - rMaps = rMapList ? rMapList.docs : [], - rMapIds = _.pluck(rMaps, "id"), - parents = [], - parentIds = []; - - //As long as this map isn't obsoleted by another map in our results list, we will show it - _.each(rMaps, function (map) { + success(data, textStatus, xhr) { + const results = data.grouped.formatType.groups; + const resourceMapGroup = _.where(results, { + groupValue: "RESOURCE", + })[0]; + const rMapList = resourceMapGroup ? resourceMapGroup.doclist : null; + const rMaps = rMapList ? rMapList.docs : []; + const rMapIds = _.pluck(rMaps, "id"); + const parents = []; + const parentIds = []; + + // As long as this map isn't obsoleted by another map in our results list, we will show it + _.each(rMaps, (map) => { if (!(map.obsoletedBy && _.contains(rMapIds, map.obsoletedBy))) { parents.push(map); parentIds.push(map.id); } }); - var metadataList = _.where(results, { groupValue: "METADATA" })[0], - metadata = - metadataList && metadataList.doclist - ? metadataList.doclist.docs - : [], - metadataModels = []; - - //As long as this map isn't obsoleted by another map in our results list, we will show it - _.each(metadata, function (m) { - //Find the metadata doc that obsoletes this one - var isObsoletedBy = _.findWhere(metadata, { id: m.obsoletedBy }); - - //If one isn't found, then this metadata doc is the most recent - if (typeof isObsoletedBy == "undefined") { - //If this metadata doc is in one of the filtered parent resource maps + const metadataList = _.where(results, { + groupValue: "METADATA", + })[0]; + const metadata = + metadataList && metadataList.doclist + ? metadataList.doclist.docs + : []; + const metadataModels = []; + + // As long as this map isn't obsoleted by another map in our results list, we will show it + _.each(metadata, (m) => { + // Find the metadata doc that obsoletes this one + const isObsoletedBy = _.findWhere(metadata, { + id: m.obsoletedBy, + }); + + // If one isn't found, then this metadata doc is the most recent + if (typeof isObsoletedBy === "undefined") { + // If this metadata doc is in one of the filtered parent resource maps if (_.intersection(parentIds, m.resourceMap).length) { - //Create a SolrResult model and add to an array + // Create a SolrResult model and add to an array metadataModels.push(new SolrResult(m)); } } @@ -1010,27 +1001,28 @@ define([ ); }, - //Create the URL string that is used to download this package - getURL: function () { - var url = null; + // Create the URL string that is used to download this package + getURL() { + let url = null; - //If we haven't set a packageServiceURL upon app initialization and we are querying a CN, then the packageServiceURL is dependent on the MN this package is from + // If we haven't set a packageServiceURL upon app initialization and we are querying a CN, then the packageServiceURL is dependent on the MN this package is from if ( MetacatUI.appModel.get("d1Service").toLowerCase().indexOf("cn/") > -1 && MetacatUI.nodeModel.get("members").length ) { - var source = this.get("datasource"), - node = _.find(MetacatUI.nodeModel.get("members"), { - identifier: source, - }); + const source = this.get("datasource"); + const node = _.find(MetacatUI.nodeModel.get("members"), { + identifier: source, + }); - //If this node has MNRead v2 services... + // If this node has MNRead v2 services... if (node && node.readv2) - url = - node.baseURL + - "/v2/packages/application%2Fbagit-097/" + - encodeURIComponent(this.get("id")); + url = `${ + node.baseURL + }/v2/packages/application%2Fbagit-097/${encodeURIComponent( + this.get("id"), + )}`; } else if (MetacatUI.appModel.get("packageServiceUrl")) url = MetacatUI.appModel.get("packageServiceUrl") + @@ -1040,60 +1032,59 @@ define([ return url; }, - createNestedPackages: function () { - var parentPackage = this, - nestedPackages = this.getNestedPackages(), - numNestedPackages = nestedPackages.length, - numComplete = 0; + createNestedPackages() { + const parentPackage = this; + const nestedPackages = this.getNestedPackages(); + const numNestedPackages = nestedPackages.length; + let numComplete = 0; - _.each(nestedPackages, function (nestedPackage, i, nestedPackages) { - //Flag the parent model as complete when all the nested package info is ready - nestedPackage.on("complete", function () { + _.each(nestedPackages, (nestedPackage, i, nestedPackages) => { + // Flag the parent model as complete when all the nested package info is ready + nestedPackage.on("complete", () => { numComplete++; - //This is the last package in this package - finish up details and flag as complete + // This is the last package in this package - finish up details and flag as complete if (numNestedPackages == numComplete) { - var sorted = _.sortBy(parentPackage.get("members"), function (p) { - return p.get("id"); - }); + const sorted = _.sortBy(parentPackage.get("members"), (p) => + p.get("id"), + ); parentPackage.set("members", sorted); parentPackage.flagComplete(); } }); - //Only look one-level deep at all times to avoid going down a rabbit hole + // Only look one-level deep at all times to avoid going down a rabbit hole if ( nestedPackage.get("parentPackage") && nestedPackage.get("parentPackage").get("parentPackage") ) { nestedPackage.flagComplete(); - return; } else { - //Get the members of this nested package + // Get the members of this nested package nestedPackage.getMembers(); } }); }, - getNestedPackages: function () { + getNestedPackages() { return _.where(this.get("members"), { type: "Package" }); }, - getMemberNames: function () { - var metadata = this.getMetadata(); + getMemberNames() { + const metadata = this.getMetadata(); if (!metadata) return false; - //Load the rendered metadata from the view service - var viewService = + // Load the rendered metadata from the view service + const viewService = MetacatUI.appModel.get("viewServiceUrl") + encodeURIComponent(metadata.get("id")); - var requestSettings = { + const requestSettings = { url: viewService, - success: function (data, response, xhr) { + success(data, response, xhr) { if (solrResult.get("formatType") == "METADATA") entityName = solrResult.get("title"); else { - var container = viewRef.findEntityDetailsContainer( + const container = viewRef.findEntityDetailsContainer( solrResult.get("id"), ); if (container && container.length > 0) { @@ -1125,40 +1116,36 @@ define([ * Will query for the derivations of this package, and sort all entities in the prov trace * into sources and derivations. */ - getProvTrace: function () { - var model = this; + getProvTrace() { + const model = this; - //See if there are any prov fields in our index before continuing + // See if there are any prov fields in our index before continuing if (!MetacatUI.appSearchModel.getProvFields()) return this; - //Start keeping track of the sources and derivations - var sources = new Array(), - derivations = new Array(); + // Start keeping track of the sources and derivations + let sources = new Array(); + let derivations = new Array(); - //Search for derivations of this package - var derivationsQuery = - MetacatUI.appSearchModel.getGroupedQuery( - "prov_wasDerivedFrom", - _.map(this.get("members"), function (m) { - return m.get("id"); - }), - "OR", - ) + "%20-obsoletedBy:*"; + // Search for derivations of this package + const derivationsQuery = `${MetacatUI.appSearchModel.getGroupedQuery( + "prov_wasDerivedFrom", + _.map(this.get("members"), (m) => m.get("id")), + "OR", + )}%20-obsoletedBy:*`; - var requestSettings = { + const requestSettings = { url: - MetacatUI.appModel.get("queryServiceUrl") + - "&q=" + - derivationsQuery + - "&wt=json&rows=1000" + - "&fl=id,resourceMap,documents,isDocumentedBy,prov_wasDerivedFrom", - success: function (data) { - _.each(data.response.docs, function (result) { + `${MetacatUI.appModel.get( + "queryServiceUrl", + )}&q=${derivationsQuery}&wt=json&rows=1000` + + `&fl=id,resourceMap,documents,isDocumentedBy,prov_wasDerivedFrom`, + success(data) { + _.each(data.response.docs, (result) => { derivations.push(result.id); }); - //Make arrays of unique IDs of objects that are sources or derivations of this package. - _.each(model.get("members"), function (member, i) { + // Make arrays of unique IDs of objects that are sources or derivations of this package. + _.each(model.get("members"), (member, i) => { if (member.type == "Package") return; if (member.hasProvTrace()) { @@ -1167,11 +1154,11 @@ define([ } }); - //Save the arrays of sources and derivations + // Save the arrays of sources and derivations model.set("sources", sources); model.set("derivations", derivations); - //Now get metadata about all the entities in the prov trace not in this package + // Now get metadata about all the entities in the prov trace not in this package model.getExternalProvTrace(); }, }; @@ -1183,97 +1170,88 @@ define([ ); }, - getExternalProvTrace: function () { - var model = this; + getExternalProvTrace() { + const model = this; - //Compact our list of ids that are in the prov trace by combining the sources and derivations and removing ids of members of this package - var externalProvEntities = _.difference( + // Compact our list of ids that are in the prov trace by combining the sources and derivations and removing ids of members of this package + const externalProvEntities = _.difference( _.union(this.get("sources"), this.get("derivations")), this.get("memberIds"), ); - //If there are no sources or derivations, then we do not need to find resource map ids for anything + // If there are no sources or derivations, then we do not need to find resource map ids for anything if (!externalProvEntities.length) { - //Save this prov trace on a package-member/document/object level. + // Save this prov trace on a package-member/document/object level. if (this.get("sources").length || this.get("derivations").length) this.setMemberProvTrace(); - //Flag that the provenance trace is complete + // Flag that the provenance trace is complete this.set("provenanceFlag", "complete"); return this; - } else { - //Create a query where we retrieve the ID of the resource map of each source and derivation - var idQuery = MetacatUI.appSearchModel.getGroupedQuery( - "id", - externalProvEntities, - "OR", - ); - - //Create a query where we retrieve the metadata for each source and derivation - var metadataQuery = MetacatUI.appSearchModel.getGroupedQuery( - "documents", - externalProvEntities, - "OR", - ); } + // Create a query where we retrieve the ID of the resource map of each source and derivation + const idQuery = MetacatUI.appSearchModel.getGroupedQuery( + "id", + externalProvEntities, + "OR", + ); + + // Create a query where we retrieve the metadata for each source and derivation + const metadataQuery = MetacatUI.appSearchModel.getGroupedQuery( + "documents", + externalProvEntities, + "OR", + ); - //TODO: Find the products of programs/executions + // TODO: Find the products of programs/executions - //Make a comma-separated list of the provenance field names - var provFieldList = ""; + // Make a comma-separated list of the provenance field names + let provFieldList = ""; _.each( MetacatUI.appSearchModel.getProvFields(), - function (fieldName, i, list) { + (fieldName, i, list) => { provFieldList += fieldName; if (i < list.length - 1) provFieldList += ","; }, ); - //Combine the two queries with an OR operator + // Combine the two queries with an OR operator if (idQuery.length && metadataQuery.length) - var combinedQuery = idQuery + "%20OR%20" + metadataQuery; + var combinedQuery = `${idQuery}%20OR%20${metadataQuery}`; else return this; - //the full and final query in Solr syntax - var query = - "q=" + - combinedQuery + - "&fl=id,resourceMap,documents,isDocumentedBy,formatType,formatId,dateUploaded,rightsHolder,datasource,prov_instanceOfClass," + - provFieldList + - "&rows=100&wt=json"; + // the full and final query in Solr syntax + const query = `q=${combinedQuery}&fl=id,resourceMap,documents,isDocumentedBy,formatType,formatId,dateUploaded,rightsHolder,datasource,prov_instanceOfClass,${provFieldList}&rows=100&wt=json`; - //Send the query to the query service - var requestSettings = { + // Send the query to the query service + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (data, textStatus, xhr) { - //Do any of our docs have multiple resource maps? - var hasMultipleMaps = _.filter(data.response.docs, function (doc) { - return ( + success(data, textStatus, xhr) { + // Do any of our docs have multiple resource maps? + const hasMultipleMaps = _.filter( + data.response.docs, + (doc) => typeof doc.resourceMap !== "undefined" && - doc.resourceMap.length > 1 - ); - }); - //If so, we want to find the latest version of each resource map and only represent that one in the Prov Chart + doc.resourceMap.length > 1, + ); + // If so, we want to find the latest version of each resource map and only represent that one in the Prov Chart if (typeof hasMultipleMaps !== "undefined") { - var allMapIDs = _.uniq( + const allMapIDs = _.uniq( _.flatten(_.pluck(hasMultipleMaps, "resourceMap")), ); if (allMapIDs.length) { - var query = - "q=+-obsoletedBy:*+" + - MetacatUI.appSearchModel.getGroupedQuery( + const query = + `q=+-obsoletedBy:*+${MetacatUI.appSearchModel.getGroupedQuery( "id", allMapIDs, "OR", - ) + - "&fl=obsoletes,id" + - "&wt=json"; - var requestSettings = { + )}&fl=obsoletes,id` + `&wt=json`; + const requestSettings = { url: MetacatUI.appModel.get("queryServiceUrl") + query, - success: function (mapData, textStatus, xhr) { - //Create a list of resource maps that are not obsoleted by any other resource map retrieved - var resourceMaps = mapData.response.docs; + success(mapData, textStatus, xhr) { + // Create a list of resource maps that are not obsoleted by any other resource map retrieved + const resourceMaps = mapData.response.docs; model.obsoletedResourceMaps = _.pluck( resourceMaps, @@ -1308,21 +1286,21 @@ define([ return this; }, - sortProvTrace: function (docs) { - var model = this; + sortProvTrace(docs) { + const model = this; - //Start an array to hold the packages in the prov trace - var sourcePackages = new Array(), - derPackages = new Array(), - sourceDocs = new Array(), - derDocs = new Array(), - sourceIDs = this.get("sources"), - derivationIDs = this.get("derivations"); + // Start an array to hold the packages in the prov trace + const sourcePackages = new Array(); + const derPackages = new Array(); + const sourceDocs = new Array(); + const derDocs = new Array(); + const sourceIDs = this.get("sources"); + const derivationIDs = this.get("derivations"); - //Separate the results into derivations and sources and group by their resource map. - _.each(docs, function (doc, i) { - var docModel = new SolrResult(doc), - mapIds = docModel.get("resourceMap"); + // Separate the results into derivations and sources and group by their resource map. + _.each(docs, (doc, i) => { + const docModel = new SolrResult(doc); + const mapIds = docModel.get("resourceMap"); if ( (typeof mapIds === "undefined" || !mapIds) && @@ -1330,7 +1308,7 @@ define([ (typeof docModel.get("isDocumentedBy") === "undefined" || !docModel.get("isDocumentedBy")) ) { - //If this object is not in a resource map and does not have metadata, it is a "naked" data doc, so save it by itself + // If this object is not in a resource map and does not have metadata, it is a "naked" data doc, so save it by itself if (_.contains(sourceIDs, doc.id)) sourceDocs.push(docModel); if (_.contains(derivationIDs, doc.id)) derDocs.push(docModel); } else if ( @@ -1338,23 +1316,24 @@ define([ docModel.get("formatType") == "DATA" && docModel.get("isDocumentedBy") ) { - //If this data doc does not have a resource map but has a metadata doc that documents it, create a blank package model and save it - var p = new PackageModel({ + // If this data doc does not have a resource map but has a metadata doc that documents it, create a blank package model and save it + const p = new PackageModel({ members: new Array(docModel), }); - //Add this package model to the sources and/or derivations packages list + // Add this package model to the sources and/or derivations packages list if (_.contains(sourceIDs, docModel.get("id"))) sourcePackages[docModel.get("id")] = p; if (_.contains(derivationIDs, docModel.get("id"))) derPackages[docModel.get("id")] = p; } else if (mapIds.length) { - //If this doc has a resource map, create a package model and SolrResult model and store it - var id = docModel.get("id"); + // If this doc has a resource map, create a package model and SolrResult model and store it + const id = docModel.get("id"); - //Some of these objects may have multiple resource maps - _.each(mapIds, function (mapId, i, list) { + // Some of these objects may have multiple resource maps + _.each(mapIds, (mapId, i, list) => { if (!_.contains(model.obsoletedResourceMaps, mapId)) { - var documentsSource, documentsDerivation; + let documentsSource; + let documentsDerivation; if (docModel.get("formatType") == "METADATA") { if ( _.intersection(docModel.get("documents"), sourceIDs).length @@ -1367,19 +1346,19 @@ define([ documentsDerivation = true; } - //Is this a source object or a metadata doc of a source object? + // Is this a source object or a metadata doc of a source object? if (_.contains(sourceIDs, id) || documentsSource) { - //Have we encountered this source package yet? + // Have we encountered this source package yet? if (!sourcePackages[mapId] && mapId != model.get("id")) { - //Now make a new package model for it + // Now make a new package model for it var p = new PackageModel({ id: mapId, members: new Array(docModel), }); - //Add to the array of source packages + // Add to the array of source packages sourcePackages[mapId] = p; } - //If so, add this member to its package model + // If so, add this member to its package model else if (mapId != model.get("id")) { var memberList = sourcePackages[mapId].get("members"); memberList.push(docModel); @@ -1387,19 +1366,19 @@ define([ } } - //Is this a derivation object or a metadata doc of a derivation object? + // Is this a derivation object or a metadata doc of a derivation object? if (_.contains(derivationIDs, id) || documentsDerivation) { - //Have we encountered this derivation package yet? + // Have we encountered this derivation package yet? if (!derPackages[mapId] && mapId != model.get("id")) { - //Now make a new package model for it + // Now make a new package model for it var p = new PackageModel({ id: mapId, members: new Array(docModel), }); - //Add to the array of source packages + // Add to the array of source packages derPackages[mapId] = p; } - //If so, add this member to its package model + // If so, add this member to its package model else if (mapId != model.get("id")) { var memberList = derPackages[mapId].get("members"); memberList.push(docModel); @@ -1411,13 +1390,13 @@ define([ } }); - //Transform our associative array (Object) of packages into an array - var newArrays = new Array(); + // Transform our associative array (Object) of packages into an array + const newArrays = new Array(); _.each( new Array(sourcePackages, derPackages, sourceDocs, derDocs), - function (provObject) { - var newArray = new Array(), - key; + (provObject) => { + const newArray = new Array(); + let key; for (key in provObject) { newArray.push(provObject[key]); } @@ -1425,114 +1404,114 @@ define([ }, ); - //We now have an array of source packages and an array of derivation packages. + // We now have an array of source packages and an array of derivation packages. model.set("sourcePackages", newArrays[0]); model.set("derivationPackages", newArrays[1]); model.set("sourceDocs", newArrays[2]); model.set("derivationDocs", newArrays[3]); - //Save this prov trace on a package-member/document/object level. + // Save this prov trace on a package-member/document/object level. model.setMemberProvTrace(); - //Flag that the provenance trace is complete + // Flag that the provenance trace is complete model.set("provenanceFlag", "complete"); }, - setMemberProvTrace: function () { - var model = this, - relatedModels = this.get("relatedModels"), - relatedModelIDs = new Array(); + setMemberProvTrace() { + const model = this; + const relatedModels = this.get("relatedModels"); + const relatedModelIDs = new Array(); - //Now for each doc, we want to find which member it is related to - _.each(this.get("members"), function (member, i, members) { + // Now for each doc, we want to find which member it is related to + _.each(this.get("members"), (member, i, members) => { if (member.type == "Package") return; - //Get the sources and derivations of this member - var memberSourceIDs = member.getSources(); - var memberDerIDs = member.getDerivations(); + // Get the sources and derivations of this member + const memberSourceIDs = member.getSources(); + const memberDerIDs = member.getDerivations(); - //Look through each source package, derivation package, source doc, and derivation doc. - _.each(model.get("sourcePackages"), function (pkg, i) { - _.each(pkg.get("members"), function (sourcePkgMember, i) { - //Is this package member a direct source of this package member? + // Look through each source package, derivation package, source doc, and derivation doc. + _.each(model.get("sourcePackages"), (pkg, i) => { + _.each(pkg.get("members"), (sourcePkgMember, i) => { + // Is this package member a direct source of this package member? if (_.contains(memberSourceIDs, sourcePkgMember.get("id"))) - //Save this source package member as a source of this member + // Save this source package member as a source of this member member.set( "provSources", _.union(member.get("provSources"), [sourcePkgMember]), ); - //Save this in the list of related models + // Save this in the list of related models if (!_.contains(relatedModelIDs, sourcePkgMember.get("id"))) { relatedModels.push(sourcePkgMember); relatedModelIDs.push(sourcePkgMember.get("id")); } }); }); - _.each(model.get("derivationPackages"), function (pkg, i) { - _.each(pkg.get("members"), function (derPkgMember, i) { - //Is this package member a direct source of this package member? + _.each(model.get("derivationPackages"), (pkg, i) => { + _.each(pkg.get("members"), (derPkgMember, i) => { + // Is this package member a direct source of this package member? if (_.contains(memberDerIDs, derPkgMember.get("id"))) - //Save this derivation package member as a derivation of this member + // Save this derivation package member as a derivation of this member member.set( "provDerivations", _.union(member.get("provDerivations"), [derPkgMember]), ); - //Save this in the list of related models + // Save this in the list of related models if (!_.contains(relatedModelIDs, derPkgMember.get("id"))) { relatedModels.push(derPkgMember); relatedModelIDs.push(derPkgMember.get("id")); } }); }); - _.each(model.get("sourceDocs"), function (doc, i) { - //Is this package member a direct source of this package member? + _.each(model.get("sourceDocs"), (doc, i) => { + // Is this package member a direct source of this package member? if (_.contains(memberSourceIDs, doc.get("id"))) - //Save this source package member as a source of this member + // Save this source package member as a source of this member member.set( "provSources", _.union(member.get("provSources"), [doc]), ); - //Save this in the list of related models + // Save this in the list of related models if (!_.contains(relatedModelIDs, doc.get("id"))) { relatedModels.push(doc); relatedModelIDs.push(doc.get("id")); } }); - _.each(model.get("derivationDocs"), function (doc, i) { - //Is this package member a direct derivation of this package member? + _.each(model.get("derivationDocs"), (doc, i) => { + // Is this package member a direct derivation of this package member? if (_.contains(memberDerIDs, doc.get("id"))) - //Save this derivation package member as a derivation of this member + // Save this derivation package member as a derivation of this member member.set( "provDerivations", _.union(member.get("provDerivations"), [doc]), ); - //Save this in the list of related models + // Save this in the list of related models if (!_.contains(relatedModelIDs, doc.get("id"))) { relatedModels.push(doc); relatedModelIDs.push(doc.get("id")); } }); - _.each(members, function (otherMember, i) { - //Is this other package member a direct derivation of this package member? + _.each(members, (otherMember, i) => { + // Is this other package member a direct derivation of this package member? if (_.contains(memberDerIDs, otherMember.get("id"))) - //Save this other derivation package member as a derivation of this member + // Save this other derivation package member as a derivation of this member member.set( "provDerivations", _.union(member.get("provDerivations"), [otherMember]), ); - //Is this other package member a direct source of this package member? + // Is this other package member a direct source of this package member? if (_.contains(memberSourceIDs, otherMember.get("id"))) - //Save this other source package member as a source of this member + // Save this other source package member as a source of this member member.set( "provSources", _.union(member.get("provSources"), [otherMember]), ); - //Is this other package member an indirect source or derivation? + // Is this other package member an indirect source or derivation? if ( otherMember.get("type") == "program" && _.contains( @@ -1540,17 +1519,15 @@ define([ otherMember.get("id"), ) ) { - var indirectSources = _.filter(members, function (m) { - return _.contains(otherMember.getInputs(), m.get("id")); - }); - indirectSourcesIds = _.each(indirectSources, function (m) { - return m.get("id"); - }); + const indirectSources = _.filter(members, (m) => + _.contains(otherMember.getInputs(), m.get("id")), + ); + indirectSourcesIds = _.each(indirectSources, (m) => m.get("id")); member.set( "prov_wasDerivedFrom", _.union(member.get("prov_wasDerivedFrom"), indirectSourcesIds), ); - //otherMember.set("prov_hasDerivations", _.union(otherMember.get("prov_hasDerivations"), [member.get("id")])); + // otherMember.set("prov_hasDerivations", _.union(otherMember.get("prov_hasDerivations"), [member.get("id")])); member.set( "provSources", _.union(member.get("provSources"), indirectSources), @@ -1563,14 +1540,11 @@ define([ otherMember.get("id"), ) ) { - var indirectDerivations = _.filter(members, function (m) { - return _.contains(otherMember.getOutputs(), m.get("id")); - }); - indirectDerivationsIds = _.each( - indirectDerivations, - function (m) { - return m.get("id"); - }, + const indirectDerivations = _.filter(members, (m) => + _.contains(otherMember.getOutputs(), m.get("id")), + ); + indirectDerivationsIds = _.each(indirectDerivations, (m) => + m.get("id"), ); member.set( "prov_hasDerivations", @@ -1579,7 +1553,7 @@ define([ indirectDerivationsIds, ), ); - //otherMember.set("prov_wasDerivedFrom", _.union(otherMember.get("prov_wasDerivedFrom"), [member.get("id")])); + // otherMember.set("prov_wasDerivedFrom", _.union(otherMember.get("prov_wasDerivedFrom"), [member.get("id")])); member.set( "provDerivations", _.union(member.get("provDerivations"), indirectDerivationsIds), @@ -1587,41 +1561,41 @@ define([ } }); - //Add this member to the list of related models + // Add this member to the list of related models if (!_.contains(relatedModelIDs, member.get("id"))) { relatedModels.push(member); relatedModelIDs.push(member.get("id")); } - //Clear out any duplicates + // Clear out any duplicates member.set("provSources", _.uniq(member.get("provSources"))); member.set("provDerivations", _.uniq(member.get("provDerivations"))); }); - //Update the list of related models + // Update the list of related models this.set("relatedModels", relatedModels); }, - downloadWithCredentials: function () { - //Get info about this object - var url = this.get("url"), - model = this; + downloadWithCredentials() { + // Get info about this object + const url = this.get("url"); + const model = this; - //Create an XHR - var xhr = new XMLHttpRequest(); + // Create an XHR + const xhr = new XMLHttpRequest(); xhr.withCredentials = true; - //When the XHR is ready, create a link with the raw data (Blob) and click the link to download + // When the XHR is ready, create a link with the raw data (Blob) and click the link to download xhr.onload = function () { - //Get the file name from the Content-Disposition header - var filename = xhr.getResponseHeader("Content-Disposition"); + // Get the file name from the Content-Disposition header + let filename = xhr.getResponseHeader("Content-Disposition"); - //As a backup, use the system metadata file name or the id + // As a backup, use the system metadata file name or the id if (!filename) { filename = model.get("filename") || model.get("id"); } - //Add a ".zip" extension if it doesn't exist + // Add a ".zip" extension if it doesn't exist if ( filename.indexOf(".zip") < 0 || filename.indexOf(".zip") != filename.length - 4 @@ -1629,11 +1603,11 @@ define([ filename += ".zip"; } - //For IE, we need to use the navigator API + // For IE, we need to use the navigator API if (navigator && navigator.msSaveOrOpenBlob) { navigator.msSaveOrOpenBlob(xhr.response, filename); } else { - var a = document.createElement("a"); + const a = document.createElement("a"); a.href = window.URL.createObjectURL(xhr.response); // xhr.response is a blob a.download = filename; // Set the file name. a.style.display = "none"; @@ -1654,7 +1628,7 @@ define([ xhr.onprogress = function (e) { if (e.lengthComputable) { - var percent = (e.loaded / e.total) * 100; + const percent = (e.loaded / e.total) * 100; model.set("downloadPercent", percent); } }; @@ -1669,48 +1643,47 @@ define([ model.get("id"), ); }; - //Open and send the request with the user's auth token + // Open and send the request with the user's auth token xhr.open("GET", url); xhr.responseType = "blob"; xhr.setRequestHeader( "Authorization", - "Bearer " + MetacatUI.appUserModel.get("token"), + `Bearer ${MetacatUI.appUserModel.get("token")}`, ); xhr.send(); }, /* Returns the SolrResult that represents the metadata doc */ - getMetadata: function () { - var members = this.get("members"); - for (var i = 0; i < members.length; i++) { + getMetadata() { + const members = this.get("members"); + for (let i = 0; i < members.length; i++) { if (members[i].get("formatType") == "METADATA") return members[i]; } - //If there are no metadata objects in this package, make sure we have searched for them already + // If there are no metadata objects in this package, make sure we have searched for them already if (!this.complete && !this.pending) this.getMembers(); return false; }, - //Check authority of the Metadata SolrResult model instead - checkAuthority: function () { - //Call the auth service - var authServiceUrl = MetacatUI.appModel.get("authServiceUrl"); + // Check authority of the Metadata SolrResult model instead + checkAuthority() { + // Call the auth service + const authServiceUrl = MetacatUI.appModel.get("authServiceUrl"); if (!authServiceUrl) return false; - var model = this; + const model = this; - var requestSettings = { - url: - authServiceUrl + - encodeURIComponent(this.get("id")) + - "?action=write", + const requestSettings = { + url: `${ + authServiceUrl + encodeURIComponent(this.get("id")) + }?action=write`, type: "GET", - success: function (data, textStatus, xhr) { + success(data, textStatus, xhr) { model.set("isAuthorized", true); model.trigger("change:isAuthorized"); }, - error: function (xhr, textStatus, errorThrown) { + error(xhr, textStatus, errorThrown) { model.set("isAuthorized", false); }, }; @@ -1722,7 +1695,7 @@ define([ ); }, - flagComplete: function () { + flagComplete() { this.complete = true; this.pending = false; this.trigger("complete", this); @@ -1734,42 +1707,42 @@ define([ * @param xml {DOM Element} - An XML or HTML DOM element to convert to json * @returns {object} - A literal JS object that represents the given XML */ - toJson: function (xml) { + toJson(xml) { // Create the return object - var obj = {}; + let obj = {}; // do children if (xml.hasChildNodes()) { - for (var i = 0; i < xml.childNodes.length; i++) { - var item = xml.childNodes.item(i); + for (let i = 0; i < xml.childNodes.length; i++) { + const item = xml.childNodes.item(i); - //If it's an empty text node, skip it + // If it's an empty text node, skip it if (item.nodeType == 3 && !item.nodeValue.trim()) continue; - //Get the node name - var nodeName = item.localName; + // Get the node name + const nodeName = item.localName; - //If it's a new container node, convert it to JSON and add as a new object attribute - if (typeof obj[nodeName] == "undefined" && item.nodeType == 1) { + // If it's a new container node, convert it to JSON and add as a new object attribute + if (typeof obj[nodeName] === "undefined" && item.nodeType == 1) { obj[nodeName] = this.toJson(item); } - //If it's a new text node, just store the text value and add as a new object attribute + // If it's a new text node, just store the text value and add as a new object attribute else if ( - typeof obj[nodeName] == "undefined" && + typeof obj[nodeName] === "undefined" && item.nodeType == 3 ) { obj = item.nodeValue; } - //If this node name is already stored as an object attribute... - else if (typeof obj[nodeName] != "undefined") { - //Cache what we have now - var old = obj[nodeName]; + // If this node name is already stored as an object attribute... + else if (typeof obj[nodeName] !== "undefined") { + // Cache what we have now + let old = obj[nodeName]; if (!Array.isArray(old)) old = [old]; - //Create a new object to store this node info + // Create a new object to store this node info var newNode = {}; - //Add the new node info to the existing array we have now + // Add the new node info to the existing array we have now if (item.nodeType == 1) { newNode = this.toJson(item); var newArray = old.concat(newNode); @@ -1778,36 +1751,36 @@ define([ var newArray = old.concat(newNode); } - //Store the attributes for this node - _.each(item.attributes, function (attr) { + // Store the attributes for this node + _.each(item.attributes, (attr) => { newNode[attr.localName] = attr.nodeValue; }); - //Replace the old array with the updated one + // Replace the old array with the updated one obj[nodeName] = newArray; - //Exit + // Exit continue; } - //Store the attributes for this node - /*_.each(item.attributes, function(attr){ + // Store the attributes for this node + /* _.each(item.attributes, function(attr){ obj[nodeName][attr.localName] = attr.nodeValue; - });*/ + }); */ } } return obj; }, - //Sums up the byte size of each member - getTotalSize: function () { + // Sums up the byte size of each member + getTotalSize() { if (this.get("totalSize")) return this.get("totalSize"); if (this.get("members").length == 1) { var totalSize = this.get("members")[0].get("size"); } else { - var totalSize = _.reduce(this.get("members"), function (sum, member) { - if (typeof sum == "object") sum = sum.get("size"); + var totalSize = _.reduce(this.get("members"), (sum, member) => { + if (typeof sum === "object") sum = sum.get("size"); return sum + member.get("size"); }); From d9fda45902be29df79156cf5642a17b46d4dd681 Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Thu, 29 Aug 2024 14:38:57 -0700 Subject: [PATCH 4/6] Fix Linting and tests #2484 --- src/js/common/Utilities.js | 37 +++++++++--------- test/js/specs/unit/common/Utilities.spec.js | 40 ++++++++++++++++++++ test/js/specs/unit/models/SolrResult.spec.js | 40 -------------------- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/js/common/Utilities.js b/src/js/common/Utilities.js index 3e5798c0d..3fefcc052 100644 --- a/src/js/common/Utilities.js +++ b/src/js/common/Utilities.js @@ -164,30 +164,31 @@ define([], () => { /** * Convert number of bytes into human readable format - * * @param integer bytes Number of bytes to convert * @param integer precision Number of digits after the decimal separator - * @return string + * @param bytes + * @param precision + * @returns string */ - bytesToSize: function (bytes, precision = 0) { - if (typeof bytes === "undefined") return; + bytesToSize(bytes, precision = 0) { + if (typeof bytes === "undefined") return `0 B`; if (bytes >= 0 && bytes < KIBIBYTE) { - return bytes + " B"; - } else if (bytes >= KIBIBYTE && bytes < MEBIBYTE) { - return (bytes / KIBIBYTE).toFixed(precision) + " KiB"; - } else if (bytes >= MEBIBYTE && bytes < GIBIBYTE) { - precision = 2; - return (bytes / MEBIBYTE).toFixed(precision) + " MiB"; - } else if (bytes >= GIBIBYTE && bytes < TEBIBYTE) { - precision = 2; - return (bytes / GIBIBYTE).toFixed(precision) + " GiB"; - } else if (bytes >= TEBIBYTE) { - precision = 2; - return (bytes / TEBIBYTE).toFixed(precision) + " TiB"; - } else { - return bytes + " B"; + return `${bytes} B`; + } + if (bytes >= KIBIBYTE && bytes < MEBIBYTE) { + return `${(bytes / KIBIBYTE).toFixed(precision)} KiB`; + } + if (bytes >= MEBIBYTE && bytes < GIBIBYTE) { + return `${(bytes / MEBIBYTE).toFixed(precision)} MiB`; + } + if (bytes >= GIBIBYTE && bytes < TEBIBYTE) { + return `${(bytes / GIBIBYTE).toFixed(precision)} GiB`; + } + if (bytes >= TEBIBYTE) { + return `${(bytes / TEBIBYTE).toFixed(precision)} TiB`; } + return `${bytes} B`; }, }; diff --git a/test/js/specs/unit/common/Utilities.spec.js b/test/js/specs/unit/common/Utilities.spec.js index a926652dc..13c1bb009 100644 --- a/test/js/specs/unit/common/Utilities.spec.js +++ b/test/js/specs/unit/common/Utilities.spec.js @@ -51,4 +51,44 @@ define(["../../../../../../src/js/common/Utilities"], function (EntityUtils) { }); }); }); + + describe("Converting bytes to human-readable size", function () { + it("should handle undefined bytes", function () { + const result = EntityUtils.bytesToSize(undefined, 2); + expect(result).to.equal("0 B"); + }); + + it("should handle bytes less than 1 KiB", function () { + const result = EntityUtils.bytesToSize(512, 2); + expect(result).to.equal("512 B"); + }); + + it("should convert bytes to KiB with precision", function () { + const result = EntityUtils.bytesToSize(2048, 2); + expect(result).to.equal("2.00 KiB"); + }); + + it("should convert bytes to MiB with precision", function () { + const result = EntityUtils.bytesToSize(2 * 1024 * 1024, 3); + expect(result).to.equal("2.000 MiB"); + }); + + it("should convert bytes to GiB with precision", function () { + const result = EntityUtils.bytesToSize(2 * 1024 * 1024 * 1024, 4); + expect(result).to.equal("2.0000 GiB"); + }); + + it("should convert bytes to TiB with precision", function () { + const result = EntityUtils.bytesToSize(2 * 1024 * 1024 * 1024 * 1024, 5); + expect(result).to.equal("2.00000 TiB"); + }); + + it("should handle very large bytes", function () { + const result = EntityUtils.bytesToSize( + 2 * 1024 * 1024 * 1024 * 1024 * 1024, + 2, + ); + expect(result).to.equal("2048.00 TiB"); + }); + }); }); diff --git a/test/js/specs/unit/models/SolrResult.spec.js b/test/js/specs/unit/models/SolrResult.spec.js index d67113870..b67f00a03 100644 --- a/test/js/specs/unit/models/SolrResult.spec.js +++ b/test/js/specs/unit/models/SolrResult.spec.js @@ -22,45 +22,5 @@ define(["../../../../../../../../src/js/models/SolrResult"], function ( solrResult.should.be.instanceof(SolrResult); }); }); - - describe("Converting bytes to human-readable size", function () { - it("should handle undefined bytes", function () { - const result = solrResult.bytesToSize(undefined, 2); - expect(result).to.equal("0 B"); - }); - - it("should handle bytes less than 1 KiB", function () { - const result = solrResult.bytesToSize(512, 2); - expect(result).to.equal("512 B"); - }); - - it("should convert bytes to KiB with precision", function () { - const result = solrResult.bytesToSize(2048, 2); - expect(result).to.equal("2.00 KiB"); - }); - - it("should convert bytes to MiB with precision", function () { - const result = solrResult.bytesToSize(2 * 1024 * 1024, 3); - expect(result).to.equal("2.000 MiB"); - }); - - it("should convert bytes to GiB with precision", function () { - const result = solrResult.bytesToSize(2 * 1024 * 1024 * 1024, 4); - expect(result).to.equal("2.0000 GiB"); - }); - - it("should convert bytes to TiB with precision", function () { - const result = solrResult.bytesToSize(2 * 1024 * 1024 * 1024 * 1024, 5); - expect(result).to.equal("2.00000 TiB"); - }); - - it("should handle very large bytes", function () { - const result = solrResult.bytesToSize( - 2 * 1024 * 1024 * 1024 * 1024 * 1024, - 2, - ); - expect(result).to.equal("2048.00 TiB"); - }); - }); }); }); From 4072928ec948b0cb674718aa869052b7a4553e8f Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Wed, 4 Sep 2024 07:30:23 -0700 Subject: [PATCH 5/6] Add ES Lint fixes for the DataPackage collection Add ES Lint fixes for the DataPackage collection --- src/js/collections/DataPackage.js | 1279 ++++++++++++++--------------- 1 file changed, 628 insertions(+), 651 deletions(-) diff --git a/src/js/collections/DataPackage.js b/src/js/collections/DataPackage.js index 405b9a9f1..858fbf47e 100644 --- a/src/js/collections/DataPackage.js +++ b/src/js/collections/DataPackage.js @@ -12,7 +12,7 @@ define([ "models/DataONEObject", "models/metadata/ScienceMetadata", "models/metadata/eml211/EML211", -], function ( +], ( $, _, Backbone, @@ -24,17 +24,17 @@ define([ DataONEObject, ScienceMetadata, EML211, -) { +) => { /** - * @class DataPackage - * @classdesc A DataPackage represents a hierarchical collection of + * @class DataPackage + * @classdesc A DataPackage represents a hierarchical collection of packages, metadata, and data objects, modeling an OAI-ORE RDF graph. TODO: incorporate Backbone.UniqueModel - * @classcategory Collections - * @name DataPackage - * @extends Backbone.Collection - * @constructor - */ + * @classcategory Collections + * @name DataPackage + * @augments Backbone.Collection + * @class + */ var DataPackage = Backbone.Collection.extend( /** @lends DataPackage.prototype */ { /** @@ -58,7 +58,8 @@ define([ */ transferQueue: [], - /** A flag ued for the package's edit status. Can be + /** + * A flag ued for the package's edit status. Can be * set to false to 'lock' the package * @type {boolean} */ @@ -76,7 +77,8 @@ define([ */ packageModel: null, - /** The science data identifiers associated with this + /** + * The science data identifiers associated with this * data package (from cito:documents), mapped to the science metadata * identifier that documents it * Not to be changed after initial fetch - this is to keep track of the relationships in their original state @@ -84,7 +86,8 @@ define([ */ originalIsDocBy: {}, - /** An array of ids that are aggregated in the resource map on the server. + /** + * An array of ids that are aggregated in the resource map on the server. * Taken from the original RDF XML that was fetched from the server. * Used for comparing the original aggregation with the aggregation of this collection. * @type {string[]} @@ -123,7 +126,8 @@ define([ */ filterModel: null, - /** Define the namespaces used in the RDF XML + /** + * Define the namespaces used in the RDF XML * @type {object} */ namespaces: { @@ -159,8 +163,8 @@ define([ numSaves: 0, // Constructor: Initialize a new DataPackage - initialize: function (models, options) { - if (typeof options == "undefined") var options = {}; + initialize (models, options) { + if (typeof options === "undefined") var options = {}; // Create an rdflib reference this.rdf = rdf; @@ -168,10 +172,10 @@ define([ // Create an initial RDF graph this.dataPackageGraph = this.rdf.graph(); - //Set the id or create a new one - this.id = options.id || "resource_map_urn:uuid:" + uuid.v4(); + // Set the id or create a new one + this.id = options.id || `resource_map_urn:uuid:${ uuid.v4()}`; - let packageModelAttrs = options.packageModelAttrs || {}; + const packageModelAttrs = options.packageModelAttrs || {}; if (typeof options.packageModel !== "undefined") { // use the given package model @@ -192,13 +196,13 @@ define([ this.id = this.packageModel.id; - //Create a Filter for this DataPackage using the id + // Create a Filter for this DataPackage using the id this.filterModel = new Filter({ fields: ["resourceMap"], values: [this.id], matchSubstring: false, }); - //If the id is ever changed, update the id in the Filter + // If the id is ever changed, update the id in the Filter this.listenTo(this.packageModel, "change:id", function () { this.filterModel.set("values", [this.packageModel.get("id")]); }); @@ -212,27 +216,27 @@ define([ // Build the DataPackage URL based on the MetacatUI.appModel.objectServiceUrl // and id or seriesid - url: function (options) { + url (options) { if (options && options.update) { return ( MetacatUI.appModel.get("objectServiceUrl") + (encodeURIComponent(this.packageModel.get("oldPid")) || encodeURIComponent(this.packageModel.get("seriesid"))) ); - } else { - //URL encode the id or seriesId - var encodedId = + } + // URL encode the id or seriesId + const encodedId = encodeURIComponent(this.packageModel.get("id")) || encodeURIComponent(this.packageModel.get("seriesid")); - //Use the object service URL if it is available (when pointing to a MN) + // Use the object service URL if it is available (when pointing to a MN) if (MetacatUI.appModel.get("objectServiceUrl")) { return MetacatUI.appModel.get("objectServiceUrl") + encodedId; } - //Otherwise, use the resolve service URL (when pointing to a CN) - else { + // Otherwise, use the resolve service URL (when pointing to a CN) + return MetacatUI.appModel.get("resolveServiceUrl") + encodedId; - } - } + + }, /* @@ -240,6 +244,7 @@ define([ * DataONEObjects, including Metadata and Data objects. * Return the correct model based on the type */ + // eslint-disable-next-line object-shorthand model: function (attrs, options) { switch (attrs.formatid) { case "http://www.openarchives.org/ore/terms": @@ -414,19 +419,18 @@ define([ /** * Overload fetch calls for a DataPackage - * - * @param {Object} [options] - Optional options for this fetch that get sent with the XHR request + * @param {object} [options] - Optional options for this fetch that get sent with the XHR request * @property {boolean} fetchModels - If false, this fetch will not fetch * each model in the collection. It will only get the resource map object. * @property {boolean} fromIndex - If true, the collection will be fetched from Solr rather than * fetching the system metadata of each model. Useful when you only need to retrieve limited information about * each package member. Set query-specific parameters on the `solrResults` SolrResults set on this collection. */ - fetch: function (options) { + fetch (options) { // Fetch the system metadata for this resource map this.packageModel.fetch(); - if (typeof options == "object") { + if (typeof options === "object") { // If the fetchModels property is set to false, if (options.fetchModels === false) { // Save the property to the Collection itself so it is accessible in other functions @@ -443,14 +447,14 @@ define([ } // Set some custom fetch options - var fetchOptions = _.extend({ dataType: "text" }, options); + const fetchOptions = _.extend({ dataType: "text" }, options); - var thisPackage = this; + const thisPackage = this; // Function to retry fetching with user login details if the initial fetch fails - var retryFetch = function () { + const retryFetch = function () { // Add the authorization options - var authFetchOptions = _.extend( + const authFetchOptions = _.extend( fetchOptions, MetacatUI.appUserModel.createAjaxSettings(), ); @@ -458,7 +462,7 @@ define([ // Fetch the resource map RDF XML with user login details return Backbone.Collection.prototype.fetch .call(thisPackage, authFetchOptions) - .fail(function () { + .fail(() => { // trigger failure() console.log("Fetch failed"); @@ -469,50 +473,50 @@ define([ // Fetch the resource map RDF XML return Backbone.Collection.prototype.fetch .call(this, fetchOptions) - .fail(function () { + .fail(() => // If the initial fetch fails, retry with user login details - return retryFetch(); - }); + retryFetch() + ); }, /* * Deserialize a Package from OAI-ORE RDF XML */ - parse: function (response, options) { - //Save the raw XML in case it needs to be used later + parse (response, options) { + // Save the raw XML in case it needs to be used later this.objectXML = response; - var RDF = this.rdf.Namespace(this.namespaces.RDF), - FOAF = this.rdf.Namespace(this.namespaces.FOAF), - OWL = this.rdf.Namespace(this.namespaces.OWL), - DC = this.rdf.Namespace(this.namespaces.DC), - ORE = this.rdf.Namespace(this.namespaces.ORE), - DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS), - CITO = this.rdf.Namespace(this.namespaces.CITO), - PROV = this.rdf.Namespace(this.namespaces.PROV), - XSD = this.rdf.Namespace(this.namespaces.XSD); - - var memberStatements = [], - atLocationStatements = [], // array to store atLocation statements - memberURIParts, - memberPIDStr, - memberPID, - memberPIDs = [], - memberModel, - documentsStatements, - objectParts, - objectPIDStr, - objectPID, - objectAtLocationValue, - scimetaID, // documentor - scidataID, // documentee - models = []; // the models returned by parse() + const RDF = this.rdf.Namespace(this.namespaces.RDF); + const FOAF = this.rdf.Namespace(this.namespaces.FOAF); + const OWL = this.rdf.Namespace(this.namespaces.OWL); + const DC = this.rdf.Namespace(this.namespaces.DC); + const ORE = this.rdf.Namespace(this.namespaces.ORE); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const CITO = this.rdf.Namespace(this.namespaces.CITO); + const PROV = this.rdf.Namespace(this.namespaces.PROV); + const XSD = this.rdf.Namespace(this.namespaces.XSD); + + let memberStatements = []; + let atLocationStatements = []; // array to store atLocation statements + let memberURIParts; + let memberPIDStr; + let memberPID; + let memberPIDs = []; + let memberModel; + let documentsStatements; + let objectParts; + let objectPIDStr; + let objectPID; + let objectAtLocationValue; + let scimetaID; // documentor + let scidataID; // documentee + const models = []; // the models returned by parse() try { - //First, make sure we are only using one CN Base URL in the RDF or the RDF parsing will fail. - var cnResolveUrl = MetacatUI.appModel.get("resolveServiceUrl"); + // First, make sure we are only using one CN Base URL in the RDF or the RDF parsing will fail. + const cnResolveUrl = MetacatUI.appModel.get("resolveServiceUrl"); - var cnURLs = _.uniq( + const cnURLs = _.uniq( response.match( /cn\S+\.test\.dataone\.org\/cn\/v\d\/resolve|cn\.dataone\.org\/cn\/v\d\/resolve/g, ), @@ -549,8 +553,8 @@ define([ if (memberPID) memberPIDs.push(memberPID); - //TODO: Test passing merge:true when adding a model and this if statement may not be necessary - //Create a DataONEObject model to represent this collection member and add to the collection + // TODO: Test passing merge:true when adding a model and this if statement may not be necessary + // Create a DataONEObject model to represent this collection member and add to the collection if (!_.contains(this.pluck("id"), memberPID)) { memberModel = new DataONEObject({ id: memberPID, @@ -560,12 +564,12 @@ define([ models.push(memberModel); } - //If the model already exists, add this resource map ID to it's list of resource maps + // If the model already exists, add this resource map ID to it's list of resource maps else { memberModel = this.get(memberPID); models.push(memberModel); - var rMaps = memberModel.get("resourceMap"); + let rMaps = memberModel.get("resourceMap"); if ( rMaps && Array.isArray(rMaps) && @@ -580,7 +584,7 @@ define([ this, ); - //Save the list of original ids + // Save the list of original ids this.originalMembers = memberPIDs; // Get the isDocumentedBy relationships @@ -591,7 +595,7 @@ define([ undefined, ); - var sciMetaPids = []; + const sciMetaPids = []; _.each( documentsStatements, @@ -609,7 +613,7 @@ define([ ); // Store the isDocumentedBy relationship - if (typeof this.originalIsDocBy[scidataID] == "undefined") + if (typeof this.originalIsDocBy[scidataID] === "undefined") this.originalIsDocBy[scidataID] = [scimetaID]; else if ( Array.isArray(this.originalIsDocBy[scidataID]) && @@ -622,15 +626,13 @@ define([ scimetaID, ]); - //Find the model in this collection for this data object - //var dataObj = this.get(scidataID); - var dataObj = _.find(models, function (m) { - return m.get("id") == scidataID; - }); + // Find the model in this collection for this data object + // var dataObj = this.get(scidataID); + const dataObj = _.find(models, (m) => m.get("id") == scidataID); if (dataObj) { - //Get the isDocumentedBy field - var isDocBy = dataObj.get("isDocumentedBy"); + // Get the isDocumentedBy field + let isDocBy = dataObj.get("isDocumentedBy"); if ( isDocBy && Array.isArray(isDocBy) && @@ -641,18 +643,18 @@ define([ isDocBy = [isDocBy, scimetaID]; else isDocBy = [scimetaID]; - //Set the isDocumentedBy field + // Set the isDocumentedBy field dataObj.set("isDocumentedBy", isDocBy); } }, this, ); - //Save the list of science metadata pids + // Save the list of science metadata pids this.sciMetaPids = sciMetaPids; // Parse atLocation - var atLocationObject = {}; + const atLocationObject = {}; atLocationStatements = this.dataPackageGraph.statementsMatching( undefined, PROV("atLocation"), @@ -665,7 +667,7 @@ define([ // Get atLocation information for each statement in the resourceMap _.each( atLocationStatements, - function (atLocationStatement) { + (atLocationStatement) => { objectParts = atLocationStatement.subject.value.split("/"); objectPIDStr = _.last(objectParts); objectPID = decodeURIComponent(objectPIDStr); @@ -680,46 +682,46 @@ define([ this.atLocationObject = atLocationObject; - //Put the science metadata pids first + // Put the science metadata pids first memberPIDs = _.difference(memberPIDs, sciMetaPids); - _.each(_.uniq(sciMetaPids), function (id) { + _.each(_.uniq(sciMetaPids), (id) => { memberPIDs.unshift(id); }); - //Don't fetch each member model if the fetchModels property on this Collection is set to false + // Don't fetch each member model if the fetchModels property on this Collection is set to false if (this.fetchModels !== false) { - //Add the models to the collection now, silently - //this.add(models, {silent: true}); + // Add the models to the collection now, silently + // this.add(models, {silent: true}); - //Retrieve the model for each member + // Retrieve the model for each member _.each( models, function (memberModel) { - var collection = this; + const collection = this; memberModel.fetch(); - memberModel.once("sync", function (oldModel) { - //Get the right model type based on the model values - var newModel = collection.getMember(oldModel); + memberModel.once("sync", (oldModel) => { + // Get the right model type based on the model values + const newModel = collection.getMember(oldModel); - //If the model type has changed, then mark the model as unsynced, since there may be custom fetch() options for the new model + // If the model type has changed, then mark the model as unsynced, since there may be custom fetch() options for the new model if (oldModel.type != newModel.type) { // DataPackages shouldn't be fetched until we support nested packages better in the UI if (newModel.type == "DataPackage") { - //Trigger a replace event so other parts of the app know when a model has been replaced with a different type + // Trigger a replace event so other parts of the app know when a model has been replaced with a different type oldModel.trigger("replace", newModel); } else { newModel.set("synced", false); newModel.fetch(); - newModel.once("sync", function (fetchedModel) { + newModel.once("sync", (fetchedModel) => { fetchedModel.set("synced", true); - //Remove the model from the collection and add it back + // Remove the model from the collection and add it back collection.remove(oldModel); collection.add(fetchedModel); - //Trigger a replace event so other parts of the app know when a model has been replaced with a different type + // Trigger a replace event so other parts of the app know when a model has been replaced with a different type oldModel.trigger("replace", newModel); if (newModel.type == "EML") @@ -753,12 +755,12 @@ define([ /* Parse the provenance relationships from the RDF graph, after all DataPackage members have been fetched, as the prov info will be stored in them. */ - parseProv: function () { + parseProv () { try { /* Now run the SPARQL queries for the provenance relationships */ - var provQueries = []; + const provQueries = []; /* result: pidValue, wasDerivedFromValue (prov_wasDerivedFrom) */ - provQueries["prov_wasDerivedFrom"] = + provQueries.prov_wasDerivedFrom = " \n" + "PREFIX rdfs: \n" + @@ -776,7 +778,7 @@ define([ "]]>"; /* result: pidValue, generatedValue (prov_generated) */ - provQueries["prov_generated"] = + provQueries.prov_generated = " \n" + "PREFIX rdfs: \n" + @@ -796,7 +798,7 @@ define([ "]]>"; /* result: pidValue, wasInformedByValue (prov_wasInformedBy) */ - provQueries["prov_wasInformedBy"] = + provQueries.prov_wasInformedBy = " \n" + "PREFIX rdfs: \n" + @@ -814,7 +816,7 @@ define([ "]]> \n"; /* result: pidValue, usedValue (prov_used) */ - provQueries["prov_used"] = + provQueries.prov_used = " \n" + "PREFIX rdfs: \n" + @@ -834,7 +836,7 @@ define([ "]]> \n"; /* result: pidValue, programPidValue (prov_generatesByProgram) */ - provQueries["prov_generatedByProgram"] = + provQueries.prov_generatedByProgram = " \n" + "PREFIX rdfs: \n" + @@ -854,7 +856,7 @@ define([ "]]> \n"; /* result: pidValue, executionPidValue */ - provQueries["prov_generatedByExecution"] = + provQueries.prov_generatedByExecution = " \n" + "PREFIX rdfs: \n" + @@ -872,7 +874,7 @@ define([ "]]> \n"; /* result: pidValue, pid (prov_generatedByProgram) */ - provQueries["prov_generatedByUser"] = + provQueries.prov_generatedByUser = " \n" + "PREFIX rdfs: \n" + @@ -891,7 +893,7 @@ define([ "]]> \n"; /* results: pidValue, programPidValue (prov_usedByProgram) */ - provQueries["prov_usedByProgram"] = + provQueries.prov_usedByProgram = " \n" + "PREFIX rdfs: \n" + @@ -911,7 +913,7 @@ define([ "]]> \n"; /* results: pidValue, executionIdValue (prov_usedByExecution) */ - provQueries["prov_usedByExecution"] = + provQueries.prov_usedByExecution = " \n" + "PREFIX rdfs: \n" + @@ -929,7 +931,7 @@ define([ "]]> \n"; /* results: pidValue, pid (prov_usedByUser) */ - provQueries["prov_usedByUser"] = + provQueries.prov_usedByUser = " \n" + "PREFIX rdfs: \n" + @@ -947,7 +949,7 @@ define([ "} \n" + "]]> \n"; /* results: pidValue, executionIdValue (prov_wasExecutedByExecution) */ - provQueries["prov_wasExecutedByExecution"] = + provQueries.prov_wasExecutedByExecution = " \n" + "PREFIX rdfs: \n" + @@ -966,7 +968,7 @@ define([ "]]> \n"; /* results: pidValue, pid (prov_wasExecutedByUser) */ - provQueries["prov_wasExecutedByUser"] = + provQueries.prov_wasExecutedByUser = " \n" + "PREFIX rdfs: \n" + @@ -985,7 +987,7 @@ define([ "]]> \n"; /* results: pidValue, derivedDataPidValue (prov_hasDerivations) */ - provQueries["prov_hasDerivations"] = + provQueries.prov_hasDerivations = " \n" + "PREFIX rdfs: \n" + @@ -1004,7 +1006,7 @@ define([ "]]> \n"; /* results: pidValue, pid (prov_instanceOfClass) */ - provQueries["prov_instanceOfClass"] = + provQueries.prov_instanceOfClass = " \n" + "PREFIX rdfs: \n" + @@ -1041,10 +1043,10 @@ define([ ]; // Process each SPARQL query - var keys = Object.keys(provQueries); + const keys = Object.keys(provQueries); this.queriesToRun = keys.length; - //Bind the onResult and onDone functions to the model so they can be called out of context + // Bind the onResult and onDone functions to the model so they can be called out of context this.onResult = _.bind(this.onResult, this); this.onDone = _.bind(this.onDone, this); @@ -1053,8 +1055,8 @@ define([ to the 'onResult' function. When each query has completed, the 'onDone' function is called for that query. */ - for (var iquery = 0; iquery < keys.length; iquery++) { - var eq = rdf.SPARQLToQuery( + for (let iquery = 0; iquery < keys.length; iquery++) { + const eq = rdf.SPARQLToQuery( provQueries[keys[iquery]], false, this.dataPackageGraph, @@ -1072,12 +1074,12 @@ define([ }, // The return values have to be extracted from the result. - getValue: function (result, name) { - var res = result[name]; + getValue (result, name) { + const res = result[name]; // The result is of type 'NamedNode', just return the string value if (res) { return res.value; - } else return " "; + } return " "; }, /* This callback is called for every query solution of the SPARQL queries. One @@ -1093,24 +1095,22 @@ define([ ?primary_data : t {termType: "NamedNode", value: "https://cn-stage.test.dataone.org/cn/v2/resolve/urn%3Auuid%3Aaae9d025-a331-4c3a-b399-a8ca0a2826ef"} ?prov_wasDerivedFrom : t {termType: "Literal", value: "urn:uuid:aae9d025-a331-4c3a-b399-a8ca0a2826ef", datatype: t}] */ - onResult: function (result) { - var currentPid = this.getValue(result, "?pid"); - var resval; - var provFieldResult; + onResult (result) { + const currentPid = this.getValue(result, "?pid"); + let resval; + let provFieldResult; var provFieldValues; // If there is a solution for this query, assign the value // to the prov field attribute (e.g. "prov_generated") of the package member (a DataONEObject) // with id = '?pid' if (typeof currentPid !== "undefined" && currentPid !== " ") { - var currentMember = null; + let currentMember = null; var provFieldValues; - var fieldName = null; - var vals = []; - var resultMember = null; - currentMember = this.find(function (model) { - return model.get("id") === currentPid; - }); + let fieldName = null; + let vals = []; + let resultMember = null; + currentMember = this.find((model) => model.get("id") === currentPid); if (typeof currentMember === "undefined") { return; @@ -1121,9 +1121,9 @@ define([ // Note: dataPackage.provSources and dataPackage.provDerivations are accumulators for // the entire DataPackage. member.sources and member.derivations are accumulators for // each package member, and are used by functions such as ProvChartView(). - for (var iFld = 0; iFld < this.provFields.length; iFld++) { + for (let iFld = 0; iFld < this.provFields.length; iFld++) { fieldName = this.provFields[iFld]; - resval = "?" + fieldName; + resval = `?${ fieldName}`; // The pid corresponding to the object of the RDF triple, with the predicate // of 'prov_generated', 'prov_used', etc. // getValue returns a string value. @@ -1133,9 +1133,7 @@ define([ // prov_* value to it. This is the package member that is the 'subject' of the // prov relationship. // The 'resultMember' could be in the current package, or could be in another 'related' package. - resultMember = this.find(function (model) { - return model.get("id") === provFieldResult; - }); + resultMember = this.find((model) => model.get("id") === provFieldResult); if (typeof resultMember !== "undefined") { // If this prov field is a 'source' field, add it to 'sources' @@ -1189,10 +1187,10 @@ define([ currentMember.set(fieldName, vals); } - //provFieldValues = _.uniq(provFieldValues); + // provFieldValues = _.uniq(provFieldValues); // Add the current prov valid (a pid) to the current value in the member - //currentMember.set(fieldName, provFieldValues); - //this.add(currentMember, { merge: true }); + // currentMember.set(fieldName, provFieldValues); + // this.add(currentMember, { merge: true }); } else { // The query result field is not the identifier of a packge member, so it may be the identifier // of another 'related' package, or it may be a string value that is the object of a prov relationship, @@ -1210,7 +1208,7 @@ define([ }, /* This callback is called when all queries have finished. */ - onDone: function () { + onDone () { if (this.queriesToRun > 1) { this.queriesToRun--; } else { @@ -1223,33 +1221,33 @@ define([ /* * Use the DataONEObject parseSysMeta() function */ - parseSysMeta: function () { + parseSysMeta () { return DataONEObject.parseSysMeta.call(this, arguments[0]); }, /** * Overwrite the Backbone.Collection.sync() function to set custom options - * @param {Object} [options] - Options for this DataPackage save - * @param {Boolean} [options.sysMetaOnly] - If true, only the system metadata of this Package will be saved. - * @param {Boolean} [options.resourceMapOnly] - If true, only the Resource Map/Package object will be saved. Metadata and Data objects aggregated by the package will be skipped. + * @param {object} [options] - Options for this DataPackage save + * @param {boolean} [options.sysMetaOnly] - If true, only the system metadata of this Package will be saved. + * @param {boolean} [options.resourceMapOnly] - If true, only the Resource Map/Package object will be saved. Metadata and Data objects aggregated by the package will be skipped. */ - save: function (options) { + save (options) { if (!options) var options = {}; this.packageModel.set("uploadStatus", "p"); - //Get the system metadata first if we haven't retrieved it yet + // Get the system metadata first if we haven't retrieved it yet if (!this.packageModel.get("sysMetaXML")) { var collection = this; this.packageModel.fetch({ - success: function () { + success () { collection.save(options); }, }); return; } - //If we want to update the system metadata only, + // If we want to update the system metadata only, // then update via the DataONEObject model and exit if (options.sysMetaOnly) { this.packageModel.save(null, options); @@ -1257,20 +1255,18 @@ define([ } if (options.resourceMapOnly !== true) { - //Sort the models in the collection so the metadata is saved first - var metadataModels = this.where({ type: "Metadata" }); - var dataModels = _.difference(this.models, metadataModels); - var sortedModels = _.union(metadataModels, dataModels); - var modelsInProgress = _.filter(sortedModels, function (m) { - return ( + // Sort the models in the collection so the metadata is saved first + const metadataModels = this.where({ type: "Metadata" }); + const dataModels = _.difference(this.models, metadataModels); + const sortedModels = _.union(metadataModels, dataModels); + const modelsInProgress = _.filter(sortedModels, (m) => ( m.get("uploadStatus") == "p" || m.get("sysMetaUploadStatus") == "p" - ); - }); - var modelsToBeSaved = _.filter(sortedModels, function (m) { - //Models should be saved if they are in the save queue, had an error saving earlier, - //or they are Science Metadata model that is NOT already in progress - return ( + )); + const modelsToBeSaved = _.filter(sortedModels, (m) => + // Models should be saved if they are in the save queue, had an error saving earlier, + // or they are Science Metadata model that is NOT already in progress + ( (m.get("type") == "Metadata" && m.get("uploadStatus") == "q") || (m.get("type") == "Data" && m.get("hasContentChanges") && @@ -1282,29 +1278,29 @@ define([ m.get("uploadStatus") != "c" && m.get("uploadStatus") != "e" && m.get("uploadStatus") !== null) - ); - }); - //Get an array of data objects whose system metadata should be updated. - var sysMetaToUpdate = _.reject(dataModels, function (m) { + ) + ); + // Get an array of data objects whose system metadata should be updated. + var sysMetaToUpdate = _.reject(dataModels, (m) => // Find models that don't have any content changes to save, // and whose system metadata is not already saving - return ( + ( !m.hasUpdates() || m.get("hasContentChanges") || m.get("sysMetaUploadStatus") == "p" || m.get("sysMetaUploadStatus") == "c" || m.get("sysMetaUploadStatus") == "e" - ); - }); + ) + ); - //First quickly validate all the models before attempting to save any - var allValid = _.every(modelsToBeSaved, function (m) { + // First quickly validate all the models before attempting to save any + const allValid = _.every(modelsToBeSaved, (m) => { if (m.isValid()) { m.trigger("valid"); return true; - } else { + } return false; - } + }); // If at least once model to be saved is invalid, @@ -1312,9 +1308,7 @@ define([ if ( !allValid || _.contains( - _.map(metadataModels, function (model) { - return model.get("uploadStatus"); - }), + _.map(metadataModels, (model) => model.get("uploadStatus")), "e", ) ) { @@ -1324,53 +1318,53 @@ define([ return; } - //If we are saving at least one model in this package, then serialize the Resource Map RDF XML + // If we are saving at least one model in this package, then serialize the Resource Map RDF XML if (modelsToBeSaved.length) { try { - //Set a new id and keep our old id + // Set a new id and keep our old id if (!this.packageModel.isNew()) { - //Update the identifier for this object + // Update the identifier for this object this.packageModel.updateID(); } - //Create the resource map XML + // Create the resource map XML var mapXML = this.serialize(); } catch (serializationException) { - //If serialization failed, revert back to our old id + // If serialization failed, revert back to our old id this.packageModel.resetID(); - //Cancel the save and show an error message + // Cancel the save and show an error message this.packageModel.set("changed", false); this.packageModel.set("uploadStatus", "q"); this.trigger( "errorSaving", - "There was a Javascript error during the serialization process: " + - serializationException, + `There was a Javascript error during the serialization process: ${ + serializationException}`, ); return; } } - //First save all the models of the collection, if needed + // First save all the models of the collection, if needed _.each( modelsToBeSaved, function (model) { - //If the model is saved successfully, start this save function again + // If the model is saved successfully, start this save function again this.stopListening(model, "successSaving", this.save); this.listenToOnce(model, "successSaving", this.save); - //If the model fails to save, start this save function + // If the model fails to save, start this save function this.stopListening(model, "errorSaving", this.save); this.listenToOnce(model, "errorSaving", this.save); - //If the model fails to save, start this save function + // If the model fails to save, start this save function this.stopListening(model, "cancelSave", this.save); this.listenToOnce(model, "cancelSave", this.save); - //Save the model and watch for fails + // Save the model and watch for fails model.save(); - //Add it to the list of models in progress + // Add it to the list of models in progress modelsInProgress.push(model); this.numSaves++; @@ -1378,56 +1372,56 @@ define([ this, ); - //Save the system metadata of all the Data objects + // Save the system metadata of all the Data objects _.each( sysMetaToUpdate, function (dataModel) { - //When the sytem metadata has been saved, save this resource map + // When the sytem metadata has been saved, save this resource map this.listenTo(dataModel, "change:sysMetaUploadStatus", this.save); - //Update the system metadata + // Update the system metadata dataModel.updateSysMeta(); - //Add it to the list of models in progress + // Add it to the list of models in progress modelsInProgress.push(dataModel); this.numSaves++; }, this, ); - //If there are still models in progress of uploading, then exit. (We will return when they are synced to upload the resource map) + // If there are still models in progress of uploading, then exit. (We will return when they are synced to upload the resource map) if (modelsInProgress.length) return; } - //If we are saving the resource map object only, and there are changes to save, serialize the RDF XML + // If we are saving the resource map object only, and there are changes to save, serialize the RDF XML else if (this.needsUpdate()) { try { - //Set a new id and keep our old id + // Set a new id and keep our old id if (!this.packageModel.isNew()) { - //Update the identifier for this object + // Update the identifier for this object this.packageModel.updateID(); } - //Create the resource map XML + // Create the resource map XML var mapXML = this.serialize(); } catch (serializationException) { - //If serialization failed, revert back to our old id + // If serialization failed, revert back to our old id this.packageModel.resetID(); - //Cancel the save and show an error message + // Cancel the save and show an error message this.packageModel.set("changed", false); this.packageModel.set("uploadStatus", "q"); this.trigger( "errorSaving", - "There was a Javascript error during the serialization process: " + - serializationException, + `There was a Javascript error during the serialization process: ${ + serializationException}`, ); return; } } - //If we are saving the resource map object only, and there are no changes to save, exit the function + // If we are saving the resource map object only, and there are no changes to save, exit the function else if (!this.needsUpdate()) { return; } - //If no models were saved and this package has no changes, we can exit without saving the resource map + // If no models were saved and this package has no changes, we can exit without saving the resource map if (this.numSaves < 1 && !this.needsUpdate()) { this.numSaves = 0; this.packageModel.set( @@ -1438,78 +1432,78 @@ define([ return; } - //Reset the number of models saved since they should all be completed by now + // Reset the number of models saved since they should all be completed by now this.numSaves = 0; - //Determine the HTTP request type - var requestType; + // Determine the HTTP request type + let requestType; if (this.packageModel.isNew()) { requestType = "POST"; } else { requestType = "PUT"; } - //Create a FormData object to send data with the XHR - var formData = new FormData(); + // Create a FormData object to send data with the XHR + const formData = new FormData(); - //Add the identifier to the XHR data + // Add the identifier to the XHR data if (this.packageModel.isNew()) { formData.append("pid", this.packageModel.get("id")); } else { - //Add the ids to the form data + // Add the ids to the form data formData.append("newPid", this.packageModel.get("id")); formData.append("pid", this.packageModel.get("oldPid")); } - //Do a fresh re-serialization of the RDF XML, in case any pids in the package have changed. - //The hope is that any errors during the serialization process have already been caught during the first serialization above + // Do a fresh re-serialization of the RDF XML, in case any pids in the package have changed. + // The hope is that any errors during the serialization process have already been caught during the first serialization above try { var mapXML = this.serialize(); } catch (serializationException) { - //Cancel the save and show an error message + // Cancel the save and show an error message this.packageModel.set("changed", false); this.packageModel.set("uploadStatus", "q"); this.trigger( "errorSaving", - "There was a Javascript error during the serialization process: " + - serializationException, + `There was a Javascript error during the serialization process: ${ + serializationException}`, ); return; } - //Make a Blob object from the serialized RDF XML - var mapBlob = new Blob([mapXML], { type: "application/xml" }); + // Make a Blob object from the serialized RDF XML + const mapBlob = new Blob([mapXML], { type: "application/xml" }); - //Get the size of the new resource map + // Get the size of the new resource map this.packageModel.set("size", mapBlob.size); - //Get the new checksum of the resource map - var checksum = md5(mapXML); + // Get the new checksum of the resource map + const checksum = md5(mapXML); this.packageModel.set("checksum", checksum); this.packageModel.set("checksumAlgorithm", "MD5"); - //Set the file name based on the id + // Set the file name based on the id this.packageModel.set( "fileName", - this.packageModel.get("id").replace(/[^a-zA-Z0-9]/g, "_") + - ".rdf.xml", + `${this.packageModel.get("id").replace(/[^a-zA-Z0-9]/g, "_") + }.rdf.xml`, ); - //Create the system metadata - var sysMetaXML = this.packageModel.serializeSysMeta(); + // Create the system metadata + const sysMetaXML = this.packageModel.serializeSysMeta(); - //Send the system metadata - var xmlBlob = new Blob([sysMetaXML], { + // Send the system metadata + const xmlBlob = new Blob([sysMetaXML], { type: "application/xml", }); - //Add the object XML and System Metadata XML to the form data - //Append the system metadata first, so we can take advantage of Metacat's streaming multipart handler + // Add the object XML and System Metadata XML to the form data + // Append the system metadata first, so we can take advantage of Metacat's streaming multipart handler formData.append("sysmeta", xmlBlob, "sysmeta"); formData.append("object", mapBlob); var collection = this; - var requestSettings = { + const requestSettings = { url: this.packageModel.isNew() ? this.url() : this.url({ update: true }), @@ -1518,16 +1512,16 @@ define([ contentType: false, processData: false, data: formData, - success: function (response) { - //Update the object XML + success (response) { + // Update the object XML collection.objectXML = mapXML; collection.packageModel.set( "sysMetaXML", collection.packageModel.serializeSysMeta(), ); - //Reset the upload status for all members - _.each(collection.where({ uploadStatus: "c" }), function (m) { + // Reset the upload status for all members + _.each(collection.where({ uploadStatus: "c" }), (m) => { m.set("uploadStatus", m.defaults().uploadStatus); }); @@ -1535,7 +1529,7 @@ define([ // in the future collection.packageModel.set("oldPid", null); - //Reset the upload status for the package + // Reset the upload status for the package collection.packageModel.set( "uploadStatus", collection.packageModel.defaults().uploadStatus, @@ -1551,20 +1545,20 @@ define([ collection.packageModel.fetch({ merge: true }); - _.each(sysMetaToUpdate, function (dataModel) { + _.each(sysMetaToUpdate, (dataModel) => { dataModel.set("sysMetaUploadStatus", "c"); }); }, - error: function (data) { - //Reset the id back to its original state + error (data) { + // Reset the id back to its original state collection.packageModel.resetID(); - //Reset the upload status for all members - _.each(collection.where({ uploadStatus: "c" }), function (m) { + // Reset the upload status for all members + _.each(collection.where({ uploadStatus: "c" }), (m) => { m.set("uploadStatus", m.defaults().uploadStatus); }); - //When there is no network connection (status == 0), there will be no response text + // When there is no network connection (status == 0), there will be no response text if (data.status == 408 || data.status == 0) { var parsedResponse = "There was a network issue that prevented this file from uploading. " + @@ -1575,10 +1569,10 @@ define([ .text(); } - //Save the error message in the model + // Save the error message in the model collection.packageModel.set("errorMessage", parsedResponse); - //Reset the upload status for the package + // Reset the upload status for the package collection.packageModel.set("uploadStatus", "e"); collection.trigger("errorSaving", parsedResponse); @@ -1603,8 +1597,8 @@ define([ * When a data package member updates, we evaluate it for its formatid, * and update it appropriately if it is not a data object only */ - getMember: function (context, args) { - var memberModel = {}; + getMember (context, args) { + let memberModel = {}; switch (context.get("formatId")) { case "http://www.openarchives.org/ore/terms": @@ -1909,8 +1903,8 @@ define([ return memberModel; }, - triggerComplete: function (model) { - //If the last fetch did not fetch the models of the collection, then mark as complete now. + triggerComplete (model) { + // If the last fetch did not fetch the models of the collection, then mark as complete now. if (this.fetchModels === false) { // Delete the fetchModels property since it is set only once per fetch. delete this.fetchModels; @@ -1920,17 +1914,15 @@ define([ return; } - //Check if the collection is done being retrieved - var notSynced = this.reject(function (m) { - return m.get("synced") || m.get("id") == model.get("id"); - }); + // Check if the collection is done being retrieved + const notSynced = this.reject((m) => m.get("synced") || m.get("id") == model.get("id")); - //If there are any models that are not synced yet, the collection is not complete + // If there are any models that are not synced yet, the collection is not complete if (notSynced.length > 0) { return; } - //If the number of models in this collection does not equal the number of objects referenced in the RDF XML, the collection is not complete + // If the number of models in this collection does not equal the number of objects referenced in the RDF XML, the collection is not complete if (this.originalMembers.length > this.length) return; this.sort(); @@ -1940,23 +1932,21 @@ define([ /* Accumulate edits that are made to the provenance relationships via the ProvChartView. these edits are accumulated here so that they are available to any package member or view. */ - recordProvEdit: function (operation, subject, predicate, object) { + recordProvEdit (operation, subject, predicate, object) { if (!this.provEdits.length) { this.provEdits = [[operation, subject, predicate, object]]; } else { // First check if the edit already exists in the list. If yes, then // don't add it again! This could occur if an edit icon was clicked rapidly // before it is dismissed. - var editFound = _.find(this.provEdits, function (edit) { - return ( + const editFound = _.find(this.provEdits, (edit) => ( edit[0] == operation && edit[1] == subject && edit[2] == predicate && edit[3] == object - ); - }); + )); - if (typeof editFound != "undefined") { + if (typeof editFound !== "undefined") { return; } @@ -1964,14 +1954,14 @@ define([ // is in the edit list (i.e. the user may have changed their mind, and // they just want to cancel an edit). If yes, then just delete the // matching add edit request - var editListSize = this.provEdits.length; - var oppositeOp = operation == "delete" ? "add" : "delete"; - - this.provEdits = _.reject(this.provEdits, function (edit) { - var editOperation = edit[0]; - var editSubjectId = edit[1]; - var editPredicate = edit[2]; - var editObject = edit[3]; + const editListSize = this.provEdits.length; + const oppositeOp = operation == "delete" ? "add" : "delete"; + + this.provEdits = _.reject(this.provEdits, (edit) => { + const editOperation = edit[0]; + const editSubjectId = edit[1]; + const editPredicate = edit[2]; + const editObject = edit[3]; if ( editOperation == oppositeOp && editSubjectId == subject && @@ -1992,7 +1982,7 @@ define([ }, // Return true if the prov edits list is not empty - provEditsPending: function () { + provEditsPending () { if (this.provEdits.length) return true; return false; }, @@ -2000,22 +1990,22 @@ define([ /* If provenance relationships have been modified by the provenance editor (in ProvChartView), then update the ORE Resource Map and save it to the server. */ - saveProv: function () { - var rdf = this.rdf; - var graph = this.dataPackageGraph; + saveProv () { + const {rdf} = this; + const graph = this.dataPackageGraph; - var provEdits = this.provEdits; + const {provEdits} = this; if (!provEdits.length) { return; } - var RDF = rdf.Namespace(this.namespaces.RDF), - PROV = rdf.Namespace(this.namespaces.PROV), - PROVONE = rdf.Namespace(this.namespaces.PROVONE), - DCTERMS = rdf.Namespace(this.namespaces.DCTERMS), - CITO = rdf.Namespace(this.namespaces.CITO), - XSD = rdf.Namespace(this.namespaces.XSD); + const RDF = rdf.Namespace(this.namespaces.RDF); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const CITO = rdf.Namespace(this.namespaces.CITO); + const XSD = rdf.Namespace(this.namespaces.XSD); - var cnResolveUrl = this.getCnURI(); + const cnResolveUrl = this.getCnURI(); /* Check if this package member had provenance relationships added or deleted by the provenance editor functionality of the ProvChartView @@ -2023,8 +2013,8 @@ define([ _.each( provEdits, function (edit) { - var operation, subject, predicate, object; - var provStatements; + let operation; let subject; let predicate; let object; + let provStatements; operation = edit[0]; subject = edit[1]; predicate = edit[2]; @@ -2041,16 +2031,16 @@ define([ // inputs and outputs and is connected to a program via a prov:association. We must 'expand' the // simplified provenance updates recorded by the editor into the fully detailed representation // of the actual model. - var executionId, executionURI, executionNode; - var programId, programURI, programNode; - var dataId, dataURI, dataNode; - var derivedDataURI, derivedDataNode; - var lastRef = false; - //var graph = this.dataPackageGraph; + let executionId; let executionURI; let executionNode; + let programId; let programURI; let programNode; + let dataId; let dataURI; let dataNode; + let derivedDataURI; let derivedDataNode; + const lastRef = false; + // var graph = this.dataPackageGraph; - //Create a node for the subject and object - var subjectNode = rdf.sym(this.getURIFromRDF(subject)), - objectNode = rdf.sym(this.getURIFromRDF(object)); + // Create a node for the subject and object + const subjectNode = rdf.sym(this.getURIFromRDF(subject)); + const objectNode = rdf.sym(this.getURIFromRDF(object)); switch (predicate) { case "prov_wasDerivedFrom": @@ -2094,7 +2084,7 @@ define([ // 'subject' is the program id, which is a simplification of the PROVONE model for display. // In the PROVONE model, execution 'uses' and input, and is associated with a program. executionId = this.addProgramToGraph(programId); - //executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); + // executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); executionNode = this.getExecutionNode(executionId); this.addToGraph(dataNode, RDF("type"), PROVONE("Data")); this.addToGraph( @@ -2126,7 +2116,7 @@ define([ // 'subject' is the program id, which is a simplification of the PROVONE model for display. // In the PROVONE model, execution 'uses' and input, and is associated with a program. executionId = this.addProgramToGraph(programId); - //executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); + // executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); executionNode = this.getExecutionNode(executionId); this.addToGraph(dataNode, RDF("type"), PROVONE("Data")); this.addToGraph(executionNode, PROV("used"), dataNode); @@ -2202,9 +2192,9 @@ define([ /* Add the specified relationship to the RDF graph only if it has not already been added. */ - addToGraph: function (subject, predicate, object) { - var graph = this.dataPackageGraph; - var statements = graph.statementsMatching(subject, predicate, object); + addToGraph (subject, predicate, object) { + const graph = this.dataPackageGraph; + const statements = graph.statementsMatching(subject, predicate, object); if (!statements.length) { graph.add(subject, predicate, object); @@ -2218,15 +2208,15 @@ define([ Also don't remove it if the subject is in any other prov statement, meaning it still references another prov object. */ - removeIfLastProvRef: function (subjectNode, predicateNode, objectNode) { - var graph = this.dataPackageGraph; - var stillUsed = false; - var PROV = rdf.Namespace(this.namespaces.PROV); - var PROVONE = rdf.Namespace(this.namespaces.PROVONE); + removeIfLastProvRef (subjectNode, predicateNode, objectNode) { + const graph = this.dataPackageGraph; + const stillUsed = false; + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); // PROV namespace value, used to identify PROV statements - var provStr = PROV("").value; + const provStr = PROV("").value; // PROVONE namespace value, used to identify PROVONE statements - var provoneStr = PROVONE("").value; + const provoneStr = PROVONE("").value; // Get the statements from the RDF graph that reference the subject of the // statement to remove. var statements = graph.statementsMatching( @@ -2235,9 +2225,9 @@ define([ subjectNode, ); - var found = _.find( + let found = _.find( statements, - function (statement) { + (statement) => { if ( statement.subject == subjectNode && statement.predicate == predicateNode && @@ -2245,7 +2235,7 @@ define([ ) return false; - var pVal = statement.predicate.value; + const pVal = statement.predicate.value; // Now check if the subject is referenced in a prov statement // There is another statement that references the subject of the @@ -2259,7 +2249,7 @@ define([ ); // IF not found in the first test, keep looking. - if (typeof found == "undefined") { + if (typeof found === "undefined") { // Get the statements from the RDF where var statements = graph.statementsMatching( subjectNode, @@ -2269,14 +2259,14 @@ define([ found = _.find( statements, - function (statement) { + (statement) => { if ( statement.subject == subjectNode && statement.predicate == predicateNode && statement.object == objectNode ) return false; - var pVal = statement.predicate.value; + const pVal = statement.predicate.value; // Now check if the subject is referenced in a prov statement if (pVal.indexOf(provStr) != -1) return true; @@ -2291,7 +2281,7 @@ define([ } // The specified statement term isn't being used for prov, so remove it. - if (typeof found == "undefined") { + if (typeof found === "undefined") { graph.removeMatches( subjectNode, predicateNode, @@ -2314,13 +2304,13 @@ define([ * Should be called during a call to serialize() and mutates * this.dataPackageGraph directly as a side-effect. */ - removeOrphanedBlankNodes: function () { + removeOrphanedBlankNodes () { if (!this.dataPackageGraph || !this.dataPackageGraph.statements) { return; } // Collect an array of statements to be removed - var toRemove = []; + const toRemove = []; _.each( this.dataPackageGraph.statements, @@ -2330,9 +2320,9 @@ define([ } // For this statement, look for other statments about it - var matches = 0; + let matches = 0; - _.each(this.dataPackageGraph.statements, function (other) { + _.each(this.dataPackageGraph.statements, (other) => { if ( other.subject.termType === "BlankNode" && other.subject.id === statement.object.id @@ -2364,22 +2354,22 @@ define([ for the program script, or available by tracing backward in the RDF graph from the program node, through the assocation to the related execution. */ - getExecutionId: function (programId) { - var rdf = this.rdf; - var graph = this.dataPackageGraph; - var stmts = null; - var cnResolveUrl = this.getCnURI(); - var RDF = rdf.Namespace(this.namespaces.RDF), - DCTERMS = rdf.Namespace(this.namespaces.DCTERMS), - PROV = rdf.Namespace(this.namespaces.PROV), - PROVONE = rdf.Namespace(this.namespaces.PROVONE); - - var member = this.get(programId); - var executionId = member.get("prov_wasExecutedByExecution"); + getExecutionId (programId) { + const {rdf} = this; + const graph = this.dataPackageGraph; + let stmts = null; + const cnResolveUrl = this.getCnURI(); + const RDF = rdf.Namespace(this.namespaces.RDF); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + + const member = this.get(programId); + const executionId = member.get("prov_wasExecutedByExecution"); if (executionId.length > 0) { return executionId[0]; - } else { - var programNode = rdf.sym(this.getURIFromRDF(programId)); + } + const programNode = rdf.sym(this.getURIFromRDF(programId)); // Get the executionId from the RDF graph // There can be only one plan for an association stmts = graph.statementsMatching( @@ -2387,17 +2377,17 @@ define([ PROV("hadPlan"), programNode, ); - if (typeof stmts == "undefined") return null; - var associationNode = stmts[0].subject; + if (typeof stmts === "undefined") return null; + const associationNode = stmts[0].subject; // There should be only one execution for this assocation. stmts = graph.statementsMatching( undefined, PROV("qualifiedAssociation"), associationNode, ); - if (typeof stmts == "undefined") return null; + if (typeof stmts === "undefined") return null; return stmts[0].subject; - } + }, /* Get the RDF node for an execution that is associated with the execution identifier. @@ -2405,12 +2395,12 @@ define([ (no resolveURI), or as a resolve URL, so check for both until the id is found. */ - getExecutionNode: function (executionId) { - var rdf = this.rdf; - var graph = this.dataPackageGraph; - var stmts = null; - var testNode = null; - var cnResolveUrl = this.getCnURI(); + getExecutionNode (executionId) { + const {rdf} = this; + const graph = this.dataPackageGraph; + let stmts = null; + let testNode = null; + const cnResolveUrl = this.getCnURI(); // First see if the execution exists in the RDF graph as a 'bare' idenfier, i.e. // a 'urn:uuid'. @@ -2419,7 +2409,7 @@ define([ undefined, undefined, ); - if (typeof stmts == "undefined" || !stmts.length) { + if (typeof stmts === "undefined" || !stmts.length) { // The execution node as urn was not found, look for fully qualified version. testNode = rdf.sym(this.getURIFromRDF(executionId)); stmts = graph.statementsMatching( @@ -2427,54 +2417,54 @@ define([ undefined, undefined, ); - if (typeof stmts == "undefined") { + if (typeof stmts === "undefined") { // Couldn't find the execution, return the standard RDF node value executionNode = rdf.sym(this.getURIFromRDF(executionId)); return executionNode; - } else { + } return testNode; - } - } else { + + } // The executionNode was found in the RDF graph as a urn var executionNode = stmts[0].subject; return executionNode; - } + }, - addProgramToGraph: function (programId) { - var rdf = this.rdf; - var graph = this.dataPackageGraph; - var RDF = rdf.Namespace(this.namespaces.RDF), - DCTERMS = rdf.Namespace(this.namespaces.DCTERMS), - PROV = rdf.Namespace(this.namespaces.PROV), - PROVONE = rdf.Namespace(this.namespaces.PROVONE), - XSD = rdf.Namespace(this.namespaces.XSD); - var member = this.get(programId); - var executionId = member.get("prov_wasExecutedByExecution"); - var executionNode = null; - var programNode = null; - var associationId = null; - var associationNode = null; - var cnResolveUrl = this.getCnURI(); + addProgramToGraph (programId) { + const {rdf} = this; + const graph = this.dataPackageGraph; + const RDF = rdf.Namespace(this.namespaces.RDF); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + const XSD = rdf.Namespace(this.namespaces.XSD); + const member = this.get(programId); + let executionId = member.get("prov_wasExecutedByExecution"); + let executionNode = null; + let programNode = null; + const associationId = null; + let associationNode = null; + const cnResolveUrl = this.getCnURI(); if (!executionId.length) { // This is a new execution, so create new execution and association ids - executionId = "urn:uuid:" + uuid.v4(); + executionId = `urn:uuid:${ uuid.v4()}`; member.set("prov_wasExecutedByExecution", [executionId]); // Blank node id. RDF validator doesn't like ':' so don't use in the id - //executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); + // executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); executionNode = this.getExecutionNode(executionId); - //associationId = "_" + uuid.v4(); + // associationId = "_" + uuid.v4(); associationNode = graph.bnode(); } else { executionId = executionId[0]; // Check if an association exists in the RDF graph for this execution id - //executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); + // executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); executionNode = this.getExecutionNode(executionId); // Check if there is an association id for this execution. // If this execution is newly created (via the editor (existing would // be parsed from the resmap), then create a new association id. - var stmts = graph.statementsMatching( + const stmts = graph.statementsMatching( executionNode, PROV("qualifiedAssociation"), undefined, @@ -2483,14 +2473,14 @@ define([ // (Associations aren't stored in the ) if (stmts.length) { associationNode = stmts[0].object; - //associationId = stmts[0].object.value; + // associationId = stmts[0].object.value; } else { - //associationId = "_" + uuid.v4(); + // associationId = "_" + uuid.v4(); associationNode = graph.bnode(); } } - //associationNode = graph.bnode(associationId); - //associationNode = graph.bnode(); + // associationNode = graph.bnode(associationId); + // associationNode = graph.bnode(); programNode = rdf.sym(this.getURIFromRDF(programId)); try { this.addToGraph( @@ -2515,24 +2505,24 @@ define([ // Remove a program identifier from the RDF graph and remove associated // linkage between the program id and the exection, if the execution is not // being used by any other statements. - removeProgramFromGraph: function (programId) { - var graph = this.dataPackageGraph; - var rdf = this.rdf; - var stmts = null; - var cnResolveUrl = this.getCnURI(); - var RDF = rdf.Namespace(this.namespaces.RDF), - DCTERMS = rdf.Namespace(this.namespaces.DCTERMS), - PROV = rdf.Namespace(this.namespaces.PROV), - PROVONE = rdf.Namespace(this.namespaces.PROVONE), - XSD = rdf.Namespace(this.namespaces.XSD); - var associationNode = null; - - var executionId = this.getExecutionId(programId); + removeProgramFromGraph (programId) { + const graph = this.dataPackageGraph; + const {rdf} = this; + let stmts = null; + const cnResolveUrl = this.getCnURI(); + const RDF = rdf.Namespace(this.namespaces.RDF); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + const XSD = rdf.Namespace(this.namespaces.XSD); + let associationNode = null; + + const executionId = this.getExecutionId(programId); if (executionId == null) return false; - //var executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); - var executionNode = this.getExecutionNode(executionId); - var programNode = rdf.sym(this.getURIFromRDF(programId)); + // var executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); + const executionNode = this.getExecutionNode(executionId); + const programNode = rdf.sym(this.getURIFromRDF(programId)); // In order to remove this program from the graph, we have to first determine that // nothing else is using the execution that is associated with the program (the plan). @@ -2543,7 +2533,7 @@ define([ // Is the program in the graph? If the program is not in the graph, then // we don't know how to remove the proper execution and assocation. stmts = graph.statementsMatching(undefined, undefined, programNode); - if (typeof stmts == "undefined" || !stmts.length) return false; + if (typeof stmts === "undefined" || !stmts.length) return false; // Is anything else linked to this execution? stmts = graph.statementsMatching(executionNode, PROV("used")); @@ -2614,37 +2604,37 @@ define([ /* * Serialize the DataPackage to OAI-ORE RDF XML */ - serialize: function () { - //Create an RDF serializer - var serializer = this.rdf.Serializer(), - oldPidVariations, - modifiedDate, - subjectClone, - predicateClone, - objectClone; + serialize () { + // Create an RDF serializer + const serializer = this.rdf.Serializer(); + let oldPidVariations; + let modifiedDate; + let subjectClone; + let predicateClone; + let objectClone; serializer.store = this.dataPackageGraph; - //Define the namespaces - var ORE = this.rdf.Namespace(this.namespaces.ORE), - CITO = this.rdf.Namespace(this.namespaces.CITO), - DC = this.rdf.Namespace(this.namespaces.DC), - DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS), - FOAF = this.rdf.Namespace(this.namespaces.FOAF), - RDF = this.rdf.Namespace(this.namespaces.RDF), - XSD = this.rdf.Namespace(this.namespaces.XSD); - - //Get the pid of this package - depends on whether we are updating or creating a resource map - var pid = this.packageModel.get("id"), - oldPid = this.packageModel.get("oldPid"), - cnResolveUrl = this.getCnURI(); - - //Get a list of the model pids that should be aggregated by this package - var idsFromModel = []; - this.each(function (packageMember) { - //If this object isn't done uploading, don't aggregate it. - //Or if it failed to upload, don't aggregate it. - //But if the system metadata failed to update, it can still be aggregated. + // Define the namespaces + const ORE = this.rdf.Namespace(this.namespaces.ORE); + const CITO = this.rdf.Namespace(this.namespaces.CITO); + const DC = this.rdf.Namespace(this.namespaces.DC); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const FOAF = this.rdf.Namespace(this.namespaces.FOAF); + const RDF = this.rdf.Namespace(this.namespaces.RDF); + const XSD = this.rdf.Namespace(this.namespaces.XSD); + + // Get the pid of this package - depends on whether we are updating or creating a resource map + const pid = this.packageModel.get("id"); + const oldPid = this.packageModel.get("oldPid"); + let cnResolveUrl = this.getCnURI(); + + // Get a list of the model pids that should be aggregated by this package + let idsFromModel = []; + this.each((packageMember) => { + // If this object isn't done uploading, don't aggregate it. + // Or if it failed to upload, don't aggregate it. + // But if the system metadata failed to update, it can still be aggregated. if ( packageMember.get("uploadStatus") !== "p" || packageMember.get("uploadStatus") !== "e" || @@ -2656,7 +2646,7 @@ define([ this.idsToAggregate = idsFromModel; - //Update the pids in the RDF graph only if we are updating the resource map with a new pid + // Update the pids in the RDF graph only if we are updating the resource map with a new pid if (!this.packageModel.isNew()) { // Remove all describes/isDescribedBy statements (they'll be rebuilt) this.dataPackageGraph.removeMany( @@ -2674,7 +2664,7 @@ define([ undefined, ); - //Create variations of the resource map ID using the resolve URL so we can always find it in the RDF graph + // Create variations of the resource map ID using the resolve URL so we can always find it in the RDF graph oldPidVariations = [ oldPid, encodeURIComponent(oldPid), @@ -2683,17 +2673,17 @@ define([ this.getURIFromRDF(oldPid), ]; - //Using the isAggregatedBy statements, find all the DataONE object ids in the RDF graph - var idsFromXML = []; + // Using the isAggregatedBy statements, find all the DataONE object ids in the RDF graph + const idsFromXML = []; - var identifierStatements = this.dataPackageGraph.statementsMatching( + const identifierStatements = this.dataPackageGraph.statementsMatching( undefined, DCTERMS("identifier"), undefined, ); _.each( identifierStatements, - function (statement) { + (statement) => { idsFromXML.push( statement.object.value, encodeURIComponent(statement.object.value), @@ -2704,23 +2694,23 @@ define([ this, ); - //Get all the child package ids - var childPackages = this.packageModel.get("childPackages"); - if (typeof childPackages == "object") { + // Get all the child package ids + const childPackages = this.packageModel.get("childPackages"); + if (typeof childPackages === "object") { idsFromModel = _.union(idsFromModel, Object.keys(childPackages)); } - //Find the difference between the model IDs and the XML IDs to get a list of added members - var addedIds = _.without( + // Find the difference between the model IDs and the XML IDs to get a list of added members + const addedIds = _.without( _.difference(idsFromModel, idsFromXML), oldPidVariations, ); - //Start an array to track all the member id variations - var allMemberIds = idsFromModel; + // Start an array to track all the member id variations + const allMemberIds = idsFromModel; - //Add the ids with the CN Resolve URLs - _.each(idsFromModel, function (id) { + // Add the ids with the CN Resolve URLs + _.each(idsFromModel, (id) => { allMemberIds.push( cnResolveUrl + encodeURIComponent(id), cnResolveUrl + id, @@ -2728,27 +2718,27 @@ define([ ); }); - //Find the identifier statement in the resource map - var idNode = this.rdf.lit(oldPid); - var idStatements = this.dataPackageGraph.statementsMatching( + // Find the identifier statement in the resource map + const idNode = this.rdf.lit(oldPid); + const idStatements = this.dataPackageGraph.statementsMatching( undefined, undefined, idNode, ); - //Change all the resource map identifier literal node in the RDF graph + // Change all the resource map identifier literal node in the RDF graph if (idStatements.length) { - var idStatement = idStatements[0]; + const idStatement = idStatements[0]; - //Remove the identifier statement + // Remove the identifier statement try { this.dataPackageGraph.remove(idStatement); } catch (error) { console.log(error); } - //Replace the id in the subject URI with the new id - var newRMapURI = ""; + // Replace the id in the subject URI with the new id + let newRMapURI = ""; if (idStatement.subject.value.indexOf(oldPid) > -1) { newRMapURI = idStatement.subject.value.replace(oldPid, pid); } else if ( @@ -2760,10 +2750,10 @@ define([ ); } - //Create resource map nodes for the subject and object - var rMapNode = this.rdf.sym(newRMapURI), - rMapIdNode = this.rdf.lit(pid); - //Add the triple for the resource map id + // Create resource map nodes for the subject and object + var rMapNode = this.rdf.sym(newRMapURI); + const rMapIdNode = this.rdf.lit(pid); + // Add the triple for the resource map id this.dataPackageGraph.add( rMapNode, DCTERMS("identifier"), @@ -2771,8 +2761,8 @@ define([ ); } - //Get all the isAggregatedBy statements - var aggByStatements = $.extend( + // Get all the isAggregatedBy statements + const aggByStatements = $.extend( true, [], this.dataPackageGraph.statementsMatching( @@ -2797,16 +2787,16 @@ define([ _.each( oldPidVariations, function (oldPid) { - //Create a node for the old aggregation using this pid variation - aggregationNode = this.rdf.sym(oldPid + "#aggregation"); - var aggregationLitNode = this.rdf.lit( - oldPid + "#aggregation", + // Create a node for the old aggregation using this pid variation + aggregationNode = this.rdf.sym(`${oldPid }#aggregation`); + const aggregationLitNode = this.rdf.lit( + `${oldPid }#aggregation`, "", XSD("anyURI"), ); - //Get all the triples where the old aggregation is the subject - var aggregationSubjStatements = _.union( + // Get all the triples where the old aggregation is the subject + const aggregationSubjStatements = _.union( this.dataPackageGraph.statementsMatching(aggregationNode), this.dataPackageGraph.statementsMatching(aggregationLitNode), ); @@ -2815,18 +2805,18 @@ define([ _.each( aggregationSubjStatements, function (statement) { - //Clone the subject + // Clone the subject subjectClone = this.cloneNode(statement.subject); - //Clone the predicate + // Clone the predicate predicateClone = this.cloneNode(statement.predicate); - //Clone the object + // Clone the object objectClone = this.cloneNode(statement.object); - //Set the subject value to the new aggregation id + // Set the subject value to the new aggregation id subjectClone.value = - this.getURIFromRDF(pid) + "#aggregation"; + `${this.getURIFromRDF(pid) }#aggregation`; - //Add a new statement with the new aggregation subject but the same predicate and object + // Add a new statement with the new aggregation subject but the same predicate and object this.dataPackageGraph.add( subjectClone, predicateClone, @@ -2836,12 +2826,12 @@ define([ this, ); - //Remove the old aggregation statements from the graph + // Remove the old aggregation statements from the graph this.dataPackageGraph.removeMany(aggregationNode); } // Change all the statements in the RDF where the aggregation is the object, to reflect the new resource map ID - var aggregationObjStatements = _.union( + const aggregationObjStatements = _.union( this.dataPackageGraph.statementsMatching( undefined, undefined, @@ -2858,16 +2848,16 @@ define([ _.each( aggregationObjStatements, function (statement) { - //Clone the subject, object, and predicate + // Clone the subject, object, and predicate subjectClone = this.cloneNode(statement.subject); predicateClone = this.cloneNode(statement.predicate); objectClone = this.cloneNode(statement.object); - //Set the object to the new aggregation pid + // Set the object to the new aggregation pid objectClone.value = - this.getURIFromRDF(pid) + "#aggregation"; + `${this.getURIFromRDF(pid) }#aggregation`; - //Add the statement with the old subject and predicate but new aggregation object + // Add the statement with the old subject and predicate but new aggregation object this.dataPackageGraph.add( subjectClone, predicateClone, @@ -2877,7 +2867,7 @@ define([ this, ); - //Remove all the old aggregation statements from the graph + // Remove all the old aggregation statements from the graph this.dataPackageGraph.removeMany( undefined, undefined, @@ -2886,8 +2876,8 @@ define([ } // Change all the resource map subject nodes in the RDF graph - var rMapNode = this.rdf.sym(this.getURIFromRDF(oldPid)); - var rMapStatements = $.extend( + const rMapNode = this.rdf.sym(this.getURIFromRDF(oldPid)); + const rMapStatements = $.extend( true, [], this.dataPackageGraph.statementsMatching(rMapNode), @@ -2906,13 +2896,13 @@ define([ objectClone.value = new Date().toISOString(); } - //Update the subject to the new pid + // Update the subject to the new pid subjectClone.value = this.getURIFromRDF(pid); - //Remove the old resource map statement + // Remove the old resource map statement this.dataPackageGraph.remove(statement); - //Add the statement with the new subject pid, but the same predicate and object + // Add the statement with the new subject pid, but the same predicate and object this.dataPackageGraph.add( subjectClone, predicateClone, @@ -2929,15 +2919,15 @@ define([ this.dataPackageGraph.add( this.rdf.sym(this.getURIFromRDF(pid)), ORE("describes"), - this.rdf.sym(this.getURIFromRDF(pid) + "#aggregation"), + this.rdf.sym(`${this.getURIFromRDF(pid) }#aggregation`), ); this.dataPackageGraph.add( - this.rdf.sym(this.getURIFromRDF(pid) + "#aggregation"), + this.rdf.sym(`${this.getURIFromRDF(pid) }#aggregation`), ORE("isDescribedBy"), this.rdf.sym(this.getURIFromRDF(pid)), ); - //Add nodes for new package members + // Add nodes for new package members _.each( addedIds, function (id) { @@ -2950,19 +2940,19 @@ define([ this.dataPackageGraph = this.rdf.graph(); cnResolveUrl = this.getCnURI(); - //Create a resource map node + // Create a resource map node var rMapNode = this.rdf.sym(this.getURIFromRDF(this.packageModel.id)); - //Create an aggregation node + // Create an aggregation node var aggregationNode = this.rdf.sym( - this.getURIFromRDF(this.packageModel.id) + "#aggregation", + `${this.getURIFromRDF(this.packageModel.id) }#aggregation`, ); // Describe the resource map with a Creator - var creatorNode = this.rdf.blankNode(); - var creatorName = this.rdf.lit( - (MetacatUI.appUserModel.get("firstName") || "") + - " " + - (MetacatUI.appUserModel.get("lastName") || ""), + const creatorNode = this.rdf.blankNode(); + const creatorName = this.rdf.lit( + `${MetacatUI.appUserModel.get("firstName") || "" + } ${ + MetacatUI.appUserModel.get("lastName") || ""}`, "", XSD("string"), ); @@ -2988,7 +2978,7 @@ define([ ORE("describes"), aggregationNode, ); - var idLiteral = this.rdf.lit(this.packageModel.id, "", XSD("string")); + const idLiteral = this.rdf.lit(this.packageModel.id, "", XSD("string")); this.dataPackageGraph.add(rMapNode, DCTERMS("identifier"), idLiteral); // Describe the aggregation @@ -3014,7 +3004,7 @@ define([ // is not the subject of any other statements. this.removeOrphanedBlankNodes(); - var xmlString = serializer.statementsToXML( + const xmlString = serializer.statementsToXML( this.dataPackageGraph.statements, ); @@ -3023,7 +3013,7 @@ define([ // Clone an rdflib.js Node by creaing a new node based on the // original node RDF term type and data type. - cloneNode: function (nodeToClone) { + cloneNode (nodeToClone) { switch (nodeToClone.termType) { case "NamedNode": return this.rdf.sym(nodeToClone.value); @@ -3036,13 +3026,13 @@ define([ undefined, nodeToClone.datatype, ); - } else { + } return this.rdf.literal(nodeToClone.value); - } + break; case "BlankNode": - //Blank nodes don't need to be cloned - return nodeToClone; //(this.rdf.blankNode(nodeToClone.value)); + // Blank nodes don't need to be cloned + return nodeToClone; // (this.rdf.blankNode(nodeToClone.value)); break; case "Collection": // TODO: construct a list of nodes for this term type. @@ -3050,30 +3040,30 @@ define([ break; default: console.log( - "ERROR: unknown node type to clone: " + nodeToClone.termType, + `ERROR: unknown node type to clone: ${ nodeToClone.termType}`, ); } }, // Adds a new object to the resource map RDF graph - addToAggregation: function (id) { + addToAggregation (id) { // Initialize the namespaces - var ORE = this.rdf.Namespace(this.namespaces.ORE), - DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS), - XSD = this.rdf.Namespace(this.namespaces.XSD), - CITO = this.rdf.Namespace(this.namespaces.CITO); + const ORE = this.rdf.Namespace(this.namespaces.ORE); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const XSD = this.rdf.Namespace(this.namespaces.XSD); + const CITO = this.rdf.Namespace(this.namespaces.CITO); // Create a node for this object, the identifier, the resource map, and the aggregation - var objectNode = this.rdf.sym(this.getURIFromRDF(id)), - rMapURI = this.getURIFromRDF(this.packageModel.get("id")), - mapNode = this.rdf.sym(rMapURI), - aggNode = this.rdf.sym(rMapURI + "#aggregation"), - idNode = this.rdf.literal(id, undefined, XSD("string")), - idStatements = [], - aggStatements = [], - aggByStatements = [], - documentsStatements = [], - isDocumentedByStatements = []; + const objectNode = this.rdf.sym(this.getURIFromRDF(id)); + const rMapURI = this.getURIFromRDF(this.packageModel.get("id")); + const mapNode = this.rdf.sym(rMapURI); + const aggNode = this.rdf.sym(`${rMapURI }#aggregation`); + const idNode = this.rdf.literal(id, undefined, XSD("string")); + let idStatements = []; + let aggStatements = []; + let aggByStatements = []; + let documentsStatements = []; + let isDocumentedByStatements = []; // Add the statement: this object isAggregatedBy the resource map aggregation aggByStatements = this.dataPackageGraph.statementsMatching( @@ -3106,9 +3096,9 @@ define([ } // Find the metadata doc that describes this object - var model = this.findWhere({ id: id }), - isDocBy = model.get("isDocumentedBy"), - documents = model.get("documents"); + const model = this.findWhere({ id }); + const isDocBy = model.get("isDocumentedBy"); + const documents = model.get("documents"); // Deal with Solr indexing bug where metadata-only packages must "document" themselves if (isDocBy.length === 0 && documents.length === 0) { @@ -3118,17 +3108,15 @@ define([ // If this object is documented by any metadata... if (isDocBy && isDocBy.length) { // Get the ids of all the metadata objects in this package - var metadataInPackage = _.compact( - _.map(this.models, function (m) { + const metadataInPackage = _.compact( + _.map(this.models, (m) => { if (m.get("formatType") == "METADATA") return m; }), - ), - metadataInPackageIDs = _.each(metadataInPackage, function (m) { - return m.get("id"); - }); + ); + const metadataInPackageIDs = _.each(metadataInPackage, (m) => m.get("id")); // Find the metadata IDs that are in this package that also documents this data object - var metadataIds = Array.isArray(isDocBy) + let metadataIds = Array.isArray(isDocBy) ? _.intersection(metadataInPackageIDs, isDocBy) : _.intersection(metadataInPackageIDs, [isDocBy]); @@ -3136,13 +3124,13 @@ define([ // then we should check if it's documented by an obsoleted pid. If so, // we'll want to change that so it's documented by a current metadata. if (metadataIds.length == 0) { - for (var i = 0; i < metadataInPackage.length; i++) { - //If the previous version of this metadata documents this data, + for (let i = 0; i < metadataInPackage.length; i++) { + // If the previous version of this metadata documents this data, if (_.contains(isDocBy, metadataInPackage[i].get("obsoletes"))) { - //Save the metadata id for serialization + // Save the metadata id for serialization metadataIds = [metadataInPackage[i].get("id")]; - //Exit the for loop + // Exit the for loop break; } } @@ -3152,15 +3140,15 @@ define([ _.each( metadataIds, function (metaId) { - //Create the named nodes and statements - var dataNode = this.rdf.sym(this.getURIFromRDF(id)), - metadataNode = this.rdf.sym(this.getURIFromRDF(metaId)), - isDocByStatement = this.rdf.st( + // Create the named nodes and statements + const dataNode = this.rdf.sym(this.getURIFromRDF(id)); + const metadataNode = this.rdf.sym(this.getURIFromRDF(metaId)); + const isDocByStatement = this.rdf.st( dataNode, CITO("isDocumentedBy"), metadataNode, - ), - documentsStatement = this.rdf.st( + ); + const documentsStatement = this.rdf.st( metadataNode, CITO("documents"), dataNode, @@ -3192,20 +3180,20 @@ define([ // If this object documents a data object if (documents && documents.length) { // Create a literal node for it - var metadataNode = this.rdf.sym(this.getURIFromRDF(id)); + const metadataNode = this.rdf.sym(this.getURIFromRDF(id)); _.each( documents, function (dataID) { - //Make sure the id is one that will be aggregated + // Make sure the id is one that will be aggregated if (_.contains(this.idsToAggregate, dataID)) { - //Find the identifier statement for this data object - var dataURI = this.getURIFromRDF(dataID); + // Find the identifier statement for this data object + const dataURI = this.getURIFromRDF(dataID); - //Create a data node using the exact way the identifier URI is written - var dataNode = this.rdf.sym(dataURI); + // Create a data node using the exact way the identifier URI is written + const dataNode = this.rdf.sym(dataURI); - //Get the statements for data isDocumentedBy metadata + // Get the statements for data isDocumentedBy metadata isDocumentedByStatements = this.dataPackageGraph.statementsMatching( dataNode, @@ -3213,34 +3201,34 @@ define([ metadataNode, ); - //If that statement is not in the RDF already... + // If that statement is not in the RDF already... if (isDocumentedByStatements.length < 1) { // Create a statement: This data is documented by this metadata - var isDocByStatement = this.rdf.st( + const isDocByStatement = this.rdf.st( dataNode, CITO("isDocumentedBy"), metadataNode, ); - //Add the "isDocumentedBy" statement + // Add the "isDocumentedBy" statement this.dataPackageGraph.add(isDocByStatement); } - //Get the statements for metadata documents data + // Get the statements for metadata documents data documentsStatements = this.dataPackageGraph.statementsMatching( metadataNode, CITO("documents"), dataNode, ); - //If that statement is not in the RDF already... + // If that statement is not in the RDF already... if (documentsStatements.length < 1) { // Create a statement: This metadata documents data - var documentsStatement = this.rdf.st( + const documentsStatement = this.rdf.st( metadataNode, CITO("documents"), dataNode, ); - //Add the "isDocumentedBy" statement + // Add the "isDocumentedBy" statement this.dataPackageGraph.add(documentsStatement); } } @@ -3253,15 +3241,15 @@ define([ /* * Removes an object from the aggregation in the RDF graph */ - removeFromAggregation: function (id) { + removeFromAggregation (id) { if (id.indexOf(this.dataPackageGraph.cnResolveUrl) == -1) { id = this.getURIFromRDF(id); } // Create a literal node for the removed object - var removedObjNode = this.rdf.sym(id), + const removedObjNode = this.rdf.sym(id); // Get the statements from the RDF where the removed object is the subject or object - statements = $.extend( + const statements = $.extend( true, [], _.union( @@ -3286,56 +3274,54 @@ define([ * Finds the given identifier in the RDF graph and returns the subject * URI of that statement. This is useful when adding additional statements * to the RDF graph for an object that already exists in that graph. - * * @param {string} id - The identifier to search for - * @return {string} - The full URI for the given id as it exists in the RDF. + * @returns {string} - The full URI for the given id as it exists in the RDF. */ - getURIFromRDF: function (id) { - //Exit if no id was given + getURIFromRDF (id) { + // Exit if no id was given if (!id) return ""; - //Create a literal node with the identifier as the value - var XSD = this.rdf.Namespace(this.namespaces.XSD), - DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS), - idNode = this.rdf.literal(id, undefined, XSD("string")), - //Find the identifier statements for the given id - idStatements = this.dataPackageGraph.statementsMatching( + // Create a literal node with the identifier as the value + const XSD = this.rdf.Namespace(this.namespaces.XSD); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const idNode = this.rdf.literal(id, undefined, XSD("string")); + // Find the identifier statements for the given id + const idStatements = this.dataPackageGraph.statementsMatching( undefined, DCTERMS("identifier"), idNode, ); - //If this object has an identifier statement, + // If this object has an identifier statement, if (idStatements.length > 0) { - //Return the subject of the statement + // Return the subject of the statement return idStatements[0].subject.value; - } else { + } return this.getCnURI() + encodeURIComponent(id); - } + }, /** * Parses out the CN Resolve URL from the existing statements in the RDF * or if not found in the RDF, from the app configuration. - * - * @return {string} - The CN resolve URL + * @returns {string} - The CN resolve URL */ - getCnURI: function () { - //If the CN resolve URL was already found, return it + getCnURI () { + // If the CN resolve URL was already found, return it if (this.dataPackageGraph.cnResolveUrl) { return this.dataPackageGraph.cnResolveUrl; - } else if (this.packageModel.get("oldPid")) { - //Find the identifier statement for the resource map in the RDF graph - var idNode = this.rdf.lit(this.packageModel.get("oldPid")), - idStatements = this.dataPackageGraph.statementsMatching( + } if (this.packageModel.get("oldPid")) { + // Find the identifier statement for the resource map in the RDF graph + const idNode = this.rdf.lit(this.packageModel.get("oldPid")); + const idStatements = this.dataPackageGraph.statementsMatching( undefined, undefined, idNode, - ), - idStatement = idStatements.length ? idStatements[0] : null; + ); + const idStatement = idStatements.length ? idStatements[0] : null; if (idStatement) { - //Parse the CN resolve URL from the statement subject URI + // Parse the CN resolve URL from the statement subject URI this.dataPackageGraph.cnResolveUrl = idStatement.subject.value.substring( 0, @@ -3358,16 +3344,16 @@ define([ MetacatUI.appModel.get("resolveServiceUrl"); } - //Return the CN resolve URL + // Return the CN resolve URL return this.dataPackageGraph.cnResolveUrl; }, /** * Checks if this resource map has had any changes that requires an update */ - needsUpdate: function () { - //Check for changes to the list of aggregated members - var ids = this.pluck("id"); + needsUpdate () { + // Check for changes to the list of aggregated members + const ids = this.pluck("id"); if ( this.originalMembers.length != ids.length || _.intersection(this.originalMembers, ids).length != ids.length @@ -3377,18 +3363,18 @@ define([ // If the provenance relationships have been updated, then the resource map // needs to be updated. if (this.provEdits.length) return true; - //Check for changes to the isDocumentedBy relationships - var isDifferent = false, - i = 0; + // Check for changes to the isDocumentedBy relationships + let isDifferent = false; + let i = 0; - //Keep going until we find a difference + // Keep going until we find a difference while (!isDifferent && i < this.length) { - //Get the original isDocBy relationships from the resource map, and the new isDocBy relationships from the models - var isDocBy = this.models[i].get("isDocumentedBy"), - id = this.models[i].get("id"), - origIsDocBy = this.originalIsDocBy[id]; + // Get the original isDocBy relationships from the resource map, and the new isDocBy relationships from the models + let isDocBy = this.models[i].get("isDocumentedBy"); + const id = this.models[i].get("id"); + let origIsDocBy = this.originalIsDocBy[id]; - //Make sure they are both formatted as arrays for these checks + // Make sure they are both formatted as arrays for these checks isDocBy = _.uniq( _.flatten(_.compact(Array.isArray(isDocBy) ? isDocBy : [isDocBy])), ); @@ -3400,18 +3386,18 @@ define([ ), ); - //Remove the id of this object so metadata can not be "isDocumentedBy" itself + // Remove the id of this object so metadata can not be "isDocumentedBy" itself isDocBy = _.without(isDocBy, id); origIsDocBy = _.without(origIsDocBy, id); - //Simply check if they are the same + // Simply check if they are the same if (origIsDocBy === isDocBy) { i++; continue; } - //Are the number of relationships different? + // Are the number of relationships different? else if (isDocBy.length != origIsDocBy.length) isDifferent = true; - //Are the arrays the same? + // Are the arrays the same? else if ( _.intersection(isDocBy, origIsDocBy).length != origIsDocBy.length ) @@ -3426,39 +3412,33 @@ define([ /* * Returns an array of the models that are in the queue or in progress of uploading */ - getQueue: function () { - return this.filter(function (m) { - return m.get("uploadStatus") == "q" || m.get("uploadStatus") == "p"; - }); + getQueue () { + return this.filter((m) => m.get("uploadStatus") == "q" || m.get("uploadStatus") == "p"); }, /* * Adds a DataONEObject model to this DataPackage collection */ - addNewModel: function (model) { - //Check that this collection doesn't already contain this model + addNewModel (model) { + // Check that this collection doesn't already contain this model if (!this.contains(model)) { this.add(model); - //Mark this data package as changed + // Mark this data package as changed this.packageModel.set("changed", true); this.packageModel.trigger("change:changed"); } }, - handleAdd: function (dataONEObject) { - var metadataModel = this.find(function (m) { - return m.get("type") == "Metadata"; - }); + handleAdd (dataONEObject) { + const metadataModel = this.find((m) => m.get("type") == "Metadata"); // Append to or create a new documents list if (metadataModel) { if (!Array.isArray(metadataModel.get("documents"))) { metadataModel.set("documents", [dataONEObject.id]); - } else { - if (!_.contains(metadataModel.get("documents"), dataONEObject.id)) + } else if (!_.contains(metadataModel.get("documents"), dataONEObject.id)) metadataModel.get("documents").push(dataONEObject.id); - } // Create an EML Entity for this DataONE Object if there isn't one already if ( @@ -3475,7 +3455,7 @@ define([ this.setLoadingFiles(dataONEObject); - //Save a reference to this DataPackage + // Save a reference to this DataPackage // If the collections attribute is an array /* if( Array.isArray(dataONEObject.get("collections")) ){ //Add this DataPackage to the collections list if it's not already in the array @@ -3508,25 +3488,25 @@ define([ * Fetches this DataPackage from the Solr index by using a SolrResults collection * and merging the models in. */ - fetchFromIndex: function () { - if (typeof this.solrResults == "undefined" || !this.solrResults) { + fetchFromIndex () { + if (typeof this.solrResults === "undefined" || !this.solrResults) { this.solrResults = new SolrResults(); } - //If no query is set yet, use the FilterModel associated with this DataPackage + // If no query is set yet, use the FilterModel associated with this DataPackage if (!this.solrResults.currentquery.length) { this.solrResults.currentquery = this.filterModel.getQuery(); } this.listenToOnce(this.solrResults, "reset", function (solrResults) { - //Merge the SolrResults into this collection + // Merge the SolrResults into this collection this.mergeModels(solrResults.models); - //Trigger the fetch as complete + // Trigger the fetch as complete this.trigger("complete"); }); - //Query the index for this data package + // Query the index for this data package this.solrResults.query(); }, @@ -3534,14 +3514,13 @@ define([ * Merge the attributes of other models into the corresponding models in this collection. * This should be used when merging models of other types (e.g. SolrResult) that represent the same * object that the DataONEObject models in the collection represent. - * * @param {Backbone.Model[]} otherModels - the other models to merge with the models in this collection * @param {string[]} [fieldsToMerge] - If specified, only these fields will be extracted from the otherModels */ - mergeModels: function (otherModels, fieldsToMerge) { - //If no otherModels are given, exit the function since there is nothing to merge + mergeModels (otherModels, fieldsToMerge) { + // If no otherModels are given, exit the function since there is nothing to merge if ( - typeof otherModels == "undefined" || + typeof otherModels === "undefined" || !otherModels || !otherModels.length ) { @@ -3551,43 +3530,43 @@ define([ _.each( otherModels, function (otherModel) { - //Get the model from this collection that matches ids with the other model - var modelInDataPackage = this.findWhere({ + // Get the model from this collection that matches ids with the other model + const modelInDataPackage = this.findWhere({ id: otherModel.get("id"), }); - //If a match is found, + // If a match is found, if (modelInDataPackage) { - var valuesFromOtherModel; + let valuesFromOtherModel; - //If specific fields to merge are given, get the values for those from the other model + // If specific fields to merge are given, get the values for those from the other model if (fieldsToMerge && fieldsToMerge.length) { valuesFromOtherModel = _.pick( otherModel.toJSON(), fieldsToMerge, ); } - //If no specific fields are given, merge (almost) all others + // If no specific fields are given, merge (almost) all others else { - //Get the default values for this model type - var otherModelDefaults = otherModel.defaults, - //Get a JSON object of all the attributes on this model - otherModelAttr = otherModel.toJSON(), - //Start an array of attributes to omit during the merge - omitKeys = []; - - _.each(otherModelAttr, function (val, key) { - //If this model's attribute is the default, don't set it on our DataONEObject model + // Get the default values for this model type + const otherModelDefaults = otherModel.defaults; + // Get a JSON object of all the attributes on this model + const otherModelAttr = otherModel.toJSON(); + // Start an array of attributes to omit during the merge + const omitKeys = []; + + _.each(otherModelAttr, (val, key) => { + // If this model's attribute is the default, don't set it on our DataONEObject model // because whatever value is in the DataONEObject model is better information than the default // value of the other model. if (otherModelDefaults[key] === val) omitKeys.push(key); }); - //Remove the properties that are still the default value + // Remove the properties that are still the default value valuesFromOtherModel = _.omit(otherModelAttr, omitKeys); } - //Set the values from the other model on the model in this collection + // Set the values from the other model on the model in this collection modelInDataPackage.set(valuesFromOtherModel); } }, @@ -3598,24 +3577,24 @@ define([ /** * Update the relationships in this resource map when its been udpated */ - updateRelationships: function () { - //Get the old id - var oldId = this.packageModel.get("oldPid"); + updateRelationships () { + // Get the old id + const oldId = this.packageModel.get("oldPid"); if (!oldId) return; - //Update the resource map list + // Update the resource map list this.each(function (m) { - var updateRMaps = _.without(m.get("resourceMap"), oldId); + const updateRMaps = _.without(m.get("resourceMap"), oldId); updateRMaps.push(this.packageModel.get("id")); m.set("resourceMap", updateRMaps); }, this); }, - saveReference: function (model) { - //Save a reference to this collection in the model - var currentCollections = model.get("collections"); + saveReference (model) { + // Save a reference to this collection in the model + const currentCollections = model.get("collections"); if (currentCollections.length > 0) { currentCollections.push(this); model.set("collections", _.uniq(currentCollections)); @@ -3630,16 +3609,15 @@ define([ * How this works is likely to change in the future. * * Closely tied to the AccessPolicyView.broadcast property. - * * @param {AccessPolicy} accessPolicy - The accessPolicy to * broadcast */ - broadcastAccessPolicy: function (accessPolicy) { + broadcastAccessPolicy (accessPolicy) { if (!accessPolicy) { return; } - var policy = _.clone(accessPolicy); + const policy = _.clone(accessPolicy); this.packageModel.set("accessPolicy", policy); // Stop now if the package is new because we don't want force @@ -3648,12 +3626,12 @@ define([ return; } - this.packageModel.on("sysMetaUpdateError", function (e) { + this.packageModel.on("sysMetaUpdateError", (e) => { // Show a generic error. Any errors at this point are things the // user can't really recover from. i.e., we've already checked // that the user has changePermission perms and we've already // re-tried the request a few times - var message = + const message = "There was an error sharing your dataset. Not all of your changes were applied."; // TODO: Is this really the right way to hook into the editor's @@ -3671,30 +3649,30 @@ define([ * the number of loading files will be calcualted and set on the packageModel. * @since 2.17.1 */ - setLoadingFiles: function (dataONEObject) { - //Set the number of loading files and the isLoadingFiles flag - let numLoadingFiles = + setLoadingFiles (dataONEObject) { + // Set the number of loading files and the isLoadingFiles flag + const numLoadingFiles = this.where({ uploadStatus: "l" }).length + this.where({ uploadStatus: "p" }).length; this.packageModel.set({ isLoadingFiles: numLoadingFiles > 0, - numLoadingFiles: numLoadingFiles, + numLoadingFiles, }); if (dataONEObject) { - //Listen to the upload status to update the flag + // Listen to the upload status to update the flag this.listenTo(dataONEObject, "change:uploadStatus", function () { - //If the object is done being successfully saved + // If the object is done being successfully saved if (dataONEObject.get("uploadStatus") == "c") { - let numLoadingFiles = + const numLoadingFiles = this.where({ uploadStatus: "l" }).length + this.where({ uploadStatus: "p" }).length; - //If all models in this DataPackage have finished loading, then mark the loading as complete + // If all models in this DataPackage have finished loading, then mark the loading as complete if (!numLoadingFiles) { this.packageModel.set({ isLoadingFiles: false, - numLoadingFiles: numLoadingFiles, + numLoadingFiles, }); } else { this.packageModel.set("numLoadingFiles", numLoadingFiles); @@ -3710,13 +3688,12 @@ define([ * @returns object with PIDs as key and atLocation paths as values * @since 2.28.0 */ - getAtLocation: function () { + getAtLocation () { return this.atLocationObject; }, /** * Get the absolute path from a relative path, handling '~', '..', and '.'. - * * @param {string} relativePath - The relative path to be converted to an absolute path. * @returns {string} - The absolute path after processing '~', '..', and '.'. * If the result is empty, returns '/'. From 47b150261bd5fa195a77948f140db1c766b64777 Mon Sep 17 00:00:00 2001 From: robyngit Date: Mon, 9 Sep 2024 09:46:47 -0400 Subject: [PATCH 6/6] Fix formatting in DataPackage --- src/js/collections/DataPackage.js | 563 +++++++++++++++--------------- 1 file changed, 291 insertions(+), 272 deletions(-) diff --git a/src/js/collections/DataPackage.js b/src/js/collections/DataPackage.js index 858fbf47e..e0911e30d 100644 --- a/src/js/collections/DataPackage.js +++ b/src/js/collections/DataPackage.js @@ -163,7 +163,7 @@ define([ numSaves: 0, // Constructor: Initialize a new DataPackage - initialize (models, options) { + initialize(models, options) { if (typeof options === "undefined") var options = {}; // Create an rdflib reference @@ -173,7 +173,7 @@ define([ this.dataPackageGraph = this.rdf.graph(); // Set the id or create a new one - this.id = options.id || `resource_map_urn:uuid:${ uuid.v4()}`; + this.id = options.id || `resource_map_urn:uuid:${uuid.v4()}`; const packageModelAttrs = options.packageModelAttrs || {}; @@ -216,27 +216,25 @@ define([ // Build the DataPackage URL based on the MetacatUI.appModel.objectServiceUrl // and id or seriesid - url (options) { + url(options) { if (options && options.update) { return ( MetacatUI.appModel.get("objectServiceUrl") + (encodeURIComponent(this.packageModel.get("oldPid")) || encodeURIComponent(this.packageModel.get("seriesid"))) ); - } - // URL encode the id or seriesId - const encodedId = - encodeURIComponent(this.packageModel.get("id")) || - encodeURIComponent(this.packageModel.get("seriesid")); - // Use the object service URL if it is available (when pointing to a MN) - if (MetacatUI.appModel.get("objectServiceUrl")) { - return MetacatUI.appModel.get("objectServiceUrl") + encodedId; - } - // Otherwise, use the resolve service URL (when pointing to a CN) - - return MetacatUI.appModel.get("resolveServiceUrl") + encodedId; - - + } + // URL encode the id or seriesId + const encodedId = + encodeURIComponent(this.packageModel.get("id")) || + encodeURIComponent(this.packageModel.get("seriesid")); + // Use the object service URL if it is available (when pointing to a MN) + if (MetacatUI.appModel.get("objectServiceUrl")) { + return MetacatUI.appModel.get("objectServiceUrl") + encodedId; + } + // Otherwise, use the resolve service URL (when pointing to a CN) + + return MetacatUI.appModel.get("resolveServiceUrl") + encodedId; }, /* @@ -426,7 +424,7 @@ define([ * fetching the system metadata of each model. Useful when you only need to retrieve limited information about * each package member. Set query-specific parameters on the `solrResults` SolrResults set on this collection. */ - fetch (options) { + fetch(options) { // Fetch the system metadata for this resource map this.packageModel.fetch(); @@ -473,44 +471,44 @@ define([ // Fetch the resource map RDF XML return Backbone.Collection.prototype.fetch .call(this, fetchOptions) - .fail(() => + .fail(() => // If the initial fetch fails, retry with user login details - retryFetch() + retryFetch(), ); }, /* * Deserialize a Package from OAI-ORE RDF XML */ - parse (response, options) { + parse(response, options) { // Save the raw XML in case it needs to be used later this.objectXML = response; const RDF = this.rdf.Namespace(this.namespaces.RDF); - const FOAF = this.rdf.Namespace(this.namespaces.FOAF); - const OWL = this.rdf.Namespace(this.namespaces.OWL); - const DC = this.rdf.Namespace(this.namespaces.DC); - const ORE = this.rdf.Namespace(this.namespaces.ORE); - const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); - const CITO = this.rdf.Namespace(this.namespaces.CITO); - const PROV = this.rdf.Namespace(this.namespaces.PROV); - const XSD = this.rdf.Namespace(this.namespaces.XSD); + const FOAF = this.rdf.Namespace(this.namespaces.FOAF); + const OWL = this.rdf.Namespace(this.namespaces.OWL); + const DC = this.rdf.Namespace(this.namespaces.DC); + const ORE = this.rdf.Namespace(this.namespaces.ORE); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const CITO = this.rdf.Namespace(this.namespaces.CITO); + const PROV = this.rdf.Namespace(this.namespaces.PROV); + const XSD = this.rdf.Namespace(this.namespaces.XSD); let memberStatements = []; - let atLocationStatements = []; // array to store atLocation statements - let memberURIParts; - let memberPIDStr; - let memberPID; - let memberPIDs = []; - let memberModel; - let documentsStatements; - let objectParts; - let objectPIDStr; - let objectPID; - let objectAtLocationValue; - let scimetaID; // documentor - let scidataID; // documentee - const models = []; // the models returned by parse() + let atLocationStatements = []; // array to store atLocation statements + let memberURIParts; + let memberPIDStr; + let memberPID; + let memberPIDs = []; + let memberModel; + let documentsStatements; + let objectParts; + let objectPIDStr; + let objectPID; + let objectAtLocationValue; + let scimetaID; // documentor + let scidataID; // documentee + const models = []; // the models returned by parse() try { // First, make sure we are only using one CN Base URL in the RDF or the RDF parsing will fail. @@ -755,7 +753,7 @@ define([ /* Parse the provenance relationships from the RDF graph, after all DataPackage members have been fetched, as the prov info will be stored in them. */ - parseProv () { + parseProv() { try { /* Now run the SPARQL queries for the provenance relationships */ const provQueries = []; @@ -1074,12 +1072,13 @@ define([ }, // The return values have to be extracted from the result. - getValue (result, name) { + getValue(result, name) { const res = result[name]; // The result is of type 'NamedNode', just return the string value if (res) { return res.value; - } return " "; + } + return " "; }, /* This callback is called for every query solution of the SPARQL queries. One @@ -1095,7 +1094,7 @@ define([ ?primary_data : t {termType: "NamedNode", value: "https://cn-stage.test.dataone.org/cn/v2/resolve/urn%3Auuid%3Aaae9d025-a331-4c3a-b399-a8ca0a2826ef"} ?prov_wasDerivedFrom : t {termType: "Literal", value: "urn:uuid:aae9d025-a331-4c3a-b399-a8ca0a2826ef", datatype: t}] */ - onResult (result) { + onResult(result) { const currentPid = this.getValue(result, "?pid"); let resval; let provFieldResult; @@ -1123,7 +1122,7 @@ define([ // each package member, and are used by functions such as ProvChartView(). for (let iFld = 0; iFld < this.provFields.length; iFld++) { fieldName = this.provFields[iFld]; - resval = `?${ fieldName}`; + resval = `?${fieldName}`; // The pid corresponding to the object of the RDF triple, with the predicate // of 'prov_generated', 'prov_used', etc. // getValue returns a string value. @@ -1133,7 +1132,9 @@ define([ // prov_* value to it. This is the package member that is the 'subject' of the // prov relationship. // The 'resultMember' could be in the current package, or could be in another 'related' package. - resultMember = this.find((model) => model.get("id") === provFieldResult); + resultMember = this.find( + (model) => model.get("id") === provFieldResult, + ); if (typeof resultMember !== "undefined") { // If this prov field is a 'source' field, add it to 'sources' @@ -1208,7 +1209,7 @@ define([ }, /* This callback is called when all queries have finished. */ - onDone () { + onDone() { if (this.queriesToRun > 1) { this.queriesToRun--; } else { @@ -1221,7 +1222,7 @@ define([ /* * Use the DataONEObject parseSysMeta() function */ - parseSysMeta () { + parseSysMeta() { return DataONEObject.parseSysMeta.call(this, arguments[0]); }, @@ -1231,7 +1232,7 @@ define([ * @param {boolean} [options.sysMetaOnly] - If true, only the system metadata of this Package will be saved. * @param {boolean} [options.resourceMapOnly] - If true, only the Resource Map/Package object will be saved. Metadata and Data objects aggregated by the package will be skipped. */ - save (options) { + save(options) { if (!options) var options = {}; this.packageModel.set("uploadStatus", "p"); @@ -1240,7 +1241,7 @@ define([ if (!this.packageModel.get("sysMetaXML")) { var collection = this; this.packageModel.fetch({ - success () { + success() { collection.save(options); }, }); @@ -1259,14 +1260,17 @@ define([ const metadataModels = this.where({ type: "Metadata" }); const dataModels = _.difference(this.models, metadataModels); const sortedModels = _.union(metadataModels, dataModels); - const modelsInProgress = _.filter(sortedModels, (m) => ( + const modelsInProgress = _.filter( + sortedModels, + (m) => m.get("uploadStatus") == "p" || - m.get("sysMetaUploadStatus") == "p" - )); - const modelsToBeSaved = _.filter(sortedModels, (m) => - // Models should be saved if they are in the save queue, had an error saving earlier, - // or they are Science Metadata model that is NOT already in progress - ( + m.get("sysMetaUploadStatus") == "p", + ); + const modelsToBeSaved = _.filter( + sortedModels, + (m) => + // Models should be saved if they are in the save queue, had an error saving earlier, + // or they are Science Metadata model that is NOT already in progress (m.get("type") == "Metadata" && m.get("uploadStatus") == "q") || (m.get("type") == "Data" && m.get("hasContentChanges") && @@ -1277,20 +1281,19 @@ define([ m.get("uploadStatus") != "p" && m.get("uploadStatus") != "c" && m.get("uploadStatus") != "e" && - m.get("uploadStatus") !== null) - ) + m.get("uploadStatus") !== null), ); // Get an array of data objects whose system metadata should be updated. - var sysMetaToUpdate = _.reject(dataModels, (m) => - // Find models that don't have any content changes to save, - // and whose system metadata is not already saving - ( + var sysMetaToUpdate = _.reject( + dataModels, + (m) => + // Find models that don't have any content changes to save, + // and whose system metadata is not already saving !m.hasUpdates() || m.get("hasContentChanges") || m.get("sysMetaUploadStatus") == "p" || m.get("sysMetaUploadStatus") == "c" || - m.get("sysMetaUploadStatus") == "e" - ) + m.get("sysMetaUploadStatus") == "e", ); // First quickly validate all the models before attempting to save any @@ -1298,9 +1301,8 @@ define([ if (m.isValid()) { m.trigger("valid"); return true; - } - return false; - + } + return false; }); // If at least once model to be saved is invalid, @@ -1338,8 +1340,7 @@ define([ this.packageModel.set("uploadStatus", "q"); this.trigger( "errorSaving", - `There was a Javascript error during the serialization process: ${ - serializationException}`, + `There was a Javascript error during the serialization process: ${serializationException}`, ); return; } @@ -1410,8 +1411,7 @@ define([ this.packageModel.set("uploadStatus", "q"); this.trigger( "errorSaving", - `There was a Javascript error during the serialization process: ${ - serializationException}`, + `There was a Javascript error during the serialization process: ${serializationException}`, ); return; } @@ -1465,8 +1465,7 @@ define([ this.packageModel.set("uploadStatus", "q"); this.trigger( "errorSaving", - `There was a Javascript error during the serialization process: ${ - serializationException}`, + `There was a Javascript error during the serialization process: ${serializationException}`, ); return; } @@ -1485,8 +1484,9 @@ define([ // Set the file name based on the id this.packageModel.set( "fileName", - `${this.packageModel.get("id").replace(/[^a-zA-Z0-9]/g, "_") - }.rdf.xml`, + `${this.packageModel + .get("id") + .replace(/[^a-zA-Z0-9]/g, "_")}.rdf.xml`, ); // Create the system metadata @@ -1512,7 +1512,7 @@ define([ contentType: false, processData: false, data: formData, - success (response) { + success(response) { // Update the object XML collection.objectXML = mapXML; collection.packageModel.set( @@ -1549,7 +1549,7 @@ define([ dataModel.set("sysMetaUploadStatus", "c"); }); }, - error (data) { + error(data) { // Reset the id back to its original state collection.packageModel.resetID(); @@ -1597,7 +1597,7 @@ define([ * When a data package member updates, we evaluate it for its formatid, * and update it appropriately if it is not a data object only */ - getMember (context, args) { + getMember(context, args) { let memberModel = {}; switch (context.get("formatId")) { @@ -1903,7 +1903,7 @@ define([ return memberModel; }, - triggerComplete (model) { + triggerComplete(model) { // If the last fetch did not fetch the models of the collection, then mark as complete now. if (this.fetchModels === false) { // Delete the fetchModels property since it is set only once per fetch. @@ -1915,7 +1915,9 @@ define([ } // Check if the collection is done being retrieved - const notSynced = this.reject((m) => m.get("synced") || m.get("id") == model.get("id")); + const notSynced = this.reject( + (m) => m.get("synced") || m.get("id") == model.get("id"), + ); // If there are any models that are not synced yet, the collection is not complete if (notSynced.length > 0) { @@ -1932,19 +1934,21 @@ define([ /* Accumulate edits that are made to the provenance relationships via the ProvChartView. these edits are accumulated here so that they are available to any package member or view. */ - recordProvEdit (operation, subject, predicate, object) { + recordProvEdit(operation, subject, predicate, object) { if (!this.provEdits.length) { this.provEdits = [[operation, subject, predicate, object]]; } else { // First check if the edit already exists in the list. If yes, then // don't add it again! This could occur if an edit icon was clicked rapidly // before it is dismissed. - const editFound = _.find(this.provEdits, (edit) => ( + const editFound = _.find( + this.provEdits, + (edit) => edit[0] == operation && edit[1] == subject && edit[2] == predicate && - edit[3] == object - )); + edit[3] == object, + ); if (typeof editFound !== "undefined") { return; @@ -1982,7 +1986,7 @@ define([ }, // Return true if the prov edits list is not empty - provEditsPending () { + provEditsPending() { if (this.provEdits.length) return true; return false; }, @@ -1990,20 +1994,20 @@ define([ /* If provenance relationships have been modified by the provenance editor (in ProvChartView), then update the ORE Resource Map and save it to the server. */ - saveProv () { - const {rdf} = this; + saveProv() { + const { rdf } = this; const graph = this.dataPackageGraph; - const {provEdits} = this; + const { provEdits } = this; if (!provEdits.length) { return; } const RDF = rdf.Namespace(this.namespaces.RDF); - const PROV = rdf.Namespace(this.namespaces.PROV); - const PROVONE = rdf.Namespace(this.namespaces.PROVONE); - const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); - const CITO = rdf.Namespace(this.namespaces.CITO); - const XSD = rdf.Namespace(this.namespaces.XSD); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const CITO = rdf.Namespace(this.namespaces.CITO); + const XSD = rdf.Namespace(this.namespaces.XSD); const cnResolveUrl = this.getCnURI(); @@ -2013,7 +2017,10 @@ define([ _.each( provEdits, function (edit) { - let operation; let subject; let predicate; let object; + let operation; + let subject; + let predicate; + let object; let provStatements; operation = edit[0]; subject = edit[1]; @@ -2031,16 +2038,23 @@ define([ // inputs and outputs and is connected to a program via a prov:association. We must 'expand' the // simplified provenance updates recorded by the editor into the fully detailed representation // of the actual model. - let executionId; let executionURI; let executionNode; - let programId; let programURI; let programNode; - let dataId; let dataURI; let dataNode; - let derivedDataURI; let derivedDataNode; + let executionId; + let executionURI; + let executionNode; + let programId; + let programURI; + let programNode; + let dataId; + let dataURI; + let dataNode; + let derivedDataURI; + let derivedDataNode; const lastRef = false; // var graph = this.dataPackageGraph; // Create a node for the subject and object const subjectNode = rdf.sym(this.getURIFromRDF(subject)); - const objectNode = rdf.sym(this.getURIFromRDF(object)); + const objectNode = rdf.sym(this.getURIFromRDF(object)); switch (predicate) { case "prov_wasDerivedFrom": @@ -2192,7 +2206,7 @@ define([ /* Add the specified relationship to the RDF graph only if it has not already been added. */ - addToGraph (subject, predicate, object) { + addToGraph(subject, predicate, object) { const graph = this.dataPackageGraph; const statements = graph.statementsMatching(subject, predicate, object); @@ -2208,7 +2222,7 @@ define([ Also don't remove it if the subject is in any other prov statement, meaning it still references another prov object. */ - removeIfLastProvRef (subjectNode, predicateNode, objectNode) { + removeIfLastProvRef(subjectNode, predicateNode, objectNode) { const graph = this.dataPackageGraph; const stillUsed = false; const PROV = rdf.Namespace(this.namespaces.PROV); @@ -2304,7 +2318,7 @@ define([ * Should be called during a call to serialize() and mutates * this.dataPackageGraph directly as a side-effect. */ - removeOrphanedBlankNodes () { + removeOrphanedBlankNodes() { if (!this.dataPackageGraph || !this.dataPackageGraph.statements) { return; } @@ -2354,40 +2368,39 @@ define([ for the program script, or available by tracing backward in the RDF graph from the program node, through the assocation to the related execution. */ - getExecutionId (programId) { - const {rdf} = this; + getExecutionId(programId) { + const { rdf } = this; const graph = this.dataPackageGraph; let stmts = null; const cnResolveUrl = this.getCnURI(); const RDF = rdf.Namespace(this.namespaces.RDF); - const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); - const PROV = rdf.Namespace(this.namespaces.PROV); - const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); const member = this.get(programId); const executionId = member.get("prov_wasExecutedByExecution"); if (executionId.length > 0) { return executionId[0]; - } - const programNode = rdf.sym(this.getURIFromRDF(programId)); - // Get the executionId from the RDF graph - // There can be only one plan for an association - stmts = graph.statementsMatching( - undefined, - PROV("hadPlan"), - programNode, - ); - if (typeof stmts === "undefined") return null; - const associationNode = stmts[0].subject; - // There should be only one execution for this assocation. - stmts = graph.statementsMatching( - undefined, - PROV("qualifiedAssociation"), - associationNode, - ); - if (typeof stmts === "undefined") return null; - return stmts[0].subject; - + } + const programNode = rdf.sym(this.getURIFromRDF(programId)); + // Get the executionId from the RDF graph + // There can be only one plan for an association + stmts = graph.statementsMatching( + undefined, + PROV("hadPlan"), + programNode, + ); + if (typeof stmts === "undefined") return null; + const associationNode = stmts[0].subject; + // There should be only one execution for this assocation. + stmts = graph.statementsMatching( + undefined, + PROV("qualifiedAssociation"), + associationNode, + ); + if (typeof stmts === "undefined") return null; + return stmts[0].subject; }, /* Get the RDF node for an execution that is associated with the execution identifier. @@ -2395,8 +2408,8 @@ define([ (no resolveURI), or as a resolve URL, so check for both until the id is found. */ - getExecutionNode (executionId) { - const {rdf} = this; + getExecutionNode(executionId) { + const { rdf } = this; const graph = this.dataPackageGraph; let stmts = null; let testNode = null; @@ -2421,24 +2434,22 @@ define([ // Couldn't find the execution, return the standard RDF node value executionNode = rdf.sym(this.getURIFromRDF(executionId)); return executionNode; - } - return testNode; - - } - // The executionNode was found in the RDF graph as a urn - var executionNode = stmts[0].subject; - return executionNode; - + } + return testNode; + } + // The executionNode was found in the RDF graph as a urn + var executionNode = stmts[0].subject; + return executionNode; }, - addProgramToGraph (programId) { - const {rdf} = this; + addProgramToGraph(programId) { + const { rdf } = this; const graph = this.dataPackageGraph; const RDF = rdf.Namespace(this.namespaces.RDF); - const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); - const PROV = rdf.Namespace(this.namespaces.PROV); - const PROVONE = rdf.Namespace(this.namespaces.PROVONE); - const XSD = rdf.Namespace(this.namespaces.XSD); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + const XSD = rdf.Namespace(this.namespaces.XSD); const member = this.get(programId); let executionId = member.get("prov_wasExecutedByExecution"); let executionNode = null; @@ -2449,7 +2460,7 @@ define([ if (!executionId.length) { // This is a new execution, so create new execution and association ids - executionId = `urn:uuid:${ uuid.v4()}`; + executionId = `urn:uuid:${uuid.v4()}`; member.set("prov_wasExecutedByExecution", [executionId]); // Blank node id. RDF validator doesn't like ':' so don't use in the id // executionNode = rdf.sym(cnResolveUrl + encodeURIComponent(executionId)); @@ -2505,16 +2516,16 @@ define([ // Remove a program identifier from the RDF graph and remove associated // linkage between the program id and the exection, if the execution is not // being used by any other statements. - removeProgramFromGraph (programId) { + removeProgramFromGraph(programId) { const graph = this.dataPackageGraph; - const {rdf} = this; + const { rdf } = this; let stmts = null; const cnResolveUrl = this.getCnURI(); const RDF = rdf.Namespace(this.namespaces.RDF); - const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); - const PROV = rdf.Namespace(this.namespaces.PROV); - const PROVONE = rdf.Namespace(this.namespaces.PROVONE); - const XSD = rdf.Namespace(this.namespaces.XSD); + const DCTERMS = rdf.Namespace(this.namespaces.DCTERMS); + const PROV = rdf.Namespace(this.namespaces.PROV); + const PROVONE = rdf.Namespace(this.namespaces.PROVONE); + const XSD = rdf.Namespace(this.namespaces.XSD); let associationNode = null; const executionId = this.getExecutionId(programId); @@ -2604,30 +2615,30 @@ define([ /* * Serialize the DataPackage to OAI-ORE RDF XML */ - serialize () { + serialize() { // Create an RDF serializer const serializer = this.rdf.Serializer(); - let oldPidVariations; - let modifiedDate; - let subjectClone; - let predicateClone; - let objectClone; + let oldPidVariations; + let modifiedDate; + let subjectClone; + let predicateClone; + let objectClone; serializer.store = this.dataPackageGraph; // Define the namespaces const ORE = this.rdf.Namespace(this.namespaces.ORE); - const CITO = this.rdf.Namespace(this.namespaces.CITO); - const DC = this.rdf.Namespace(this.namespaces.DC); - const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); - const FOAF = this.rdf.Namespace(this.namespaces.FOAF); - const RDF = this.rdf.Namespace(this.namespaces.RDF); - const XSD = this.rdf.Namespace(this.namespaces.XSD); + const CITO = this.rdf.Namespace(this.namespaces.CITO); + const DC = this.rdf.Namespace(this.namespaces.DC); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const FOAF = this.rdf.Namespace(this.namespaces.FOAF); + const RDF = this.rdf.Namespace(this.namespaces.RDF); + const XSD = this.rdf.Namespace(this.namespaces.XSD); // Get the pid of this package - depends on whether we are updating or creating a resource map const pid = this.packageModel.get("id"); - const oldPid = this.packageModel.get("oldPid"); - let cnResolveUrl = this.getCnURI(); + const oldPid = this.packageModel.get("oldPid"); + let cnResolveUrl = this.getCnURI(); // Get a list of the model pids that should be aggregated by this package let idsFromModel = []; @@ -2752,7 +2763,7 @@ define([ // Create resource map nodes for the subject and object var rMapNode = this.rdf.sym(newRMapURI); - const rMapIdNode = this.rdf.lit(pid); + const rMapIdNode = this.rdf.lit(pid); // Add the triple for the resource map id this.dataPackageGraph.add( rMapNode, @@ -2788,9 +2799,9 @@ define([ oldPidVariations, function (oldPid) { // Create a node for the old aggregation using this pid variation - aggregationNode = this.rdf.sym(`${oldPid }#aggregation`); + aggregationNode = this.rdf.sym(`${oldPid}#aggregation`); const aggregationLitNode = this.rdf.lit( - `${oldPid }#aggregation`, + `${oldPid}#aggregation`, "", XSD("anyURI"), ); @@ -2813,8 +2824,7 @@ define([ objectClone = this.cloneNode(statement.object); // Set the subject value to the new aggregation id - subjectClone.value = - `${this.getURIFromRDF(pid) }#aggregation`; + subjectClone.value = `${this.getURIFromRDF(pid)}#aggregation`; // Add a new statement with the new aggregation subject but the same predicate and object this.dataPackageGraph.add( @@ -2854,8 +2864,7 @@ define([ objectClone = this.cloneNode(statement.object); // Set the object to the new aggregation pid - objectClone.value = - `${this.getURIFromRDF(pid) }#aggregation`; + objectClone.value = `${this.getURIFromRDF(pid)}#aggregation`; // Add the statement with the old subject and predicate but new aggregation object this.dataPackageGraph.add( @@ -2919,10 +2928,10 @@ define([ this.dataPackageGraph.add( this.rdf.sym(this.getURIFromRDF(pid)), ORE("describes"), - this.rdf.sym(`${this.getURIFromRDF(pid) }#aggregation`), + this.rdf.sym(`${this.getURIFromRDF(pid)}#aggregation`), ); this.dataPackageGraph.add( - this.rdf.sym(`${this.getURIFromRDF(pid) }#aggregation`), + this.rdf.sym(`${this.getURIFromRDF(pid)}#aggregation`), ORE("isDescribedBy"), this.rdf.sym(this.getURIFromRDF(pid)), ); @@ -2944,15 +2953,15 @@ define([ var rMapNode = this.rdf.sym(this.getURIFromRDF(this.packageModel.id)); // Create an aggregation node var aggregationNode = this.rdf.sym( - `${this.getURIFromRDF(this.packageModel.id) }#aggregation`, + `${this.getURIFromRDF(this.packageModel.id)}#aggregation`, ); // Describe the resource map with a Creator const creatorNode = this.rdf.blankNode(); const creatorName = this.rdf.lit( - `${MetacatUI.appUserModel.get("firstName") || "" - } ${ - MetacatUI.appUserModel.get("lastName") || ""}`, + `${MetacatUI.appUserModel.get("firstName") || ""} ${ + MetacatUI.appUserModel.get("lastName") || "" + }`, "", XSD("string"), ); @@ -2978,7 +2987,11 @@ define([ ORE("describes"), aggregationNode, ); - const idLiteral = this.rdf.lit(this.packageModel.id, "", XSD("string")); + const idLiteral = this.rdf.lit( + this.packageModel.id, + "", + XSD("string"), + ); this.dataPackageGraph.add(rMapNode, DCTERMS("identifier"), idLiteral); // Describe the aggregation @@ -3013,7 +3026,7 @@ define([ // Clone an rdflib.js Node by creaing a new node based on the // original node RDF term type and data type. - cloneNode (nodeToClone) { + cloneNode(nodeToClone) { switch (nodeToClone.termType) { case "NamedNode": return this.rdf.sym(nodeToClone.value); @@ -3026,9 +3039,9 @@ define([ undefined, nodeToClone.datatype, ); - } - return this.rdf.literal(nodeToClone.value); - + } + return this.rdf.literal(nodeToClone.value); + break; case "BlankNode": // Blank nodes don't need to be cloned @@ -3040,30 +3053,30 @@ define([ break; default: console.log( - `ERROR: unknown node type to clone: ${ nodeToClone.termType}`, + `ERROR: unknown node type to clone: ${nodeToClone.termType}`, ); } }, // Adds a new object to the resource map RDF graph - addToAggregation (id) { + addToAggregation(id) { // Initialize the namespaces const ORE = this.rdf.Namespace(this.namespaces.ORE); - const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); - const XSD = this.rdf.Namespace(this.namespaces.XSD); - const CITO = this.rdf.Namespace(this.namespaces.CITO); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const XSD = this.rdf.Namespace(this.namespaces.XSD); + const CITO = this.rdf.Namespace(this.namespaces.CITO); // Create a node for this object, the identifier, the resource map, and the aggregation const objectNode = this.rdf.sym(this.getURIFromRDF(id)); - const rMapURI = this.getURIFromRDF(this.packageModel.get("id")); - const mapNode = this.rdf.sym(rMapURI); - const aggNode = this.rdf.sym(`${rMapURI }#aggregation`); - const idNode = this.rdf.literal(id, undefined, XSD("string")); - let idStatements = []; - let aggStatements = []; - let aggByStatements = []; - let documentsStatements = []; - let isDocumentedByStatements = []; + const rMapURI = this.getURIFromRDF(this.packageModel.get("id")); + const mapNode = this.rdf.sym(rMapURI); + const aggNode = this.rdf.sym(`${rMapURI}#aggregation`); + const idNode = this.rdf.literal(id, undefined, XSD("string")); + let idStatements = []; + let aggStatements = []; + let aggByStatements = []; + let documentsStatements = []; + let isDocumentedByStatements = []; // Add the statement: this object isAggregatedBy the resource map aggregation aggByStatements = this.dataPackageGraph.statementsMatching( @@ -3097,8 +3110,8 @@ define([ // Find the metadata doc that describes this object const model = this.findWhere({ id }); - const isDocBy = model.get("isDocumentedBy"); - const documents = model.get("documents"); + const isDocBy = model.get("isDocumentedBy"); + const documents = model.get("documents"); // Deal with Solr indexing bug where metadata-only packages must "document" themselves if (isDocBy.length === 0 && documents.length === 0) { @@ -3109,11 +3122,13 @@ define([ if (isDocBy && isDocBy.length) { // Get the ids of all the metadata objects in this package const metadataInPackage = _.compact( - _.map(this.models, (m) => { - if (m.get("formatType") == "METADATA") return m; - }), - ); - const metadataInPackageIDs = _.each(metadataInPackage, (m) => m.get("id")); + _.map(this.models, (m) => { + if (m.get("formatType") == "METADATA") return m; + }), + ); + const metadataInPackageIDs = _.each(metadataInPackage, (m) => + m.get("id"), + ); // Find the metadata IDs that are in this package that also documents this data object let metadataIds = Array.isArray(isDocBy) @@ -3142,17 +3157,17 @@ define([ function (metaId) { // Create the named nodes and statements const dataNode = this.rdf.sym(this.getURIFromRDF(id)); - const metadataNode = this.rdf.sym(this.getURIFromRDF(metaId)); - const isDocByStatement = this.rdf.st( - dataNode, - CITO("isDocumentedBy"), - metadataNode, - ); - const documentsStatement = this.rdf.st( - metadataNode, - CITO("documents"), - dataNode, - ); + const metadataNode = this.rdf.sym(this.getURIFromRDF(metaId)); + const isDocByStatement = this.rdf.st( + dataNode, + CITO("isDocumentedBy"), + metadataNode, + ); + const documentsStatement = this.rdf.st( + metadataNode, + CITO("documents"), + dataNode, + ); // Add the statements documentsStatements = this.dataPackageGraph.statementsMatching( @@ -3241,26 +3256,26 @@ define([ /* * Removes an object from the aggregation in the RDF graph */ - removeFromAggregation (id) { + removeFromAggregation(id) { if (id.indexOf(this.dataPackageGraph.cnResolveUrl) == -1) { id = this.getURIFromRDF(id); } // Create a literal node for the removed object const removedObjNode = this.rdf.sym(id); - // Get the statements from the RDF where the removed object is the subject or object - const statements = $.extend( - true, - [], - _.union( - this.dataPackageGraph.statementsMatching( - undefined, - undefined, - removedObjNode, - ), - this.dataPackageGraph.statementsMatching(removedObjNode), + // Get the statements from the RDF where the removed object is the subject or object + const statements = $.extend( + true, + [], + _.union( + this.dataPackageGraph.statementsMatching( + undefined, + undefined, + removedObjNode, ), - ); + this.dataPackageGraph.statementsMatching(removedObjNode), + ), + ); // Remove all the statements mentioning this object try { @@ -3277,28 +3292,27 @@ define([ * @param {string} id - The identifier to search for * @returns {string} - The full URI for the given id as it exists in the RDF. */ - getURIFromRDF (id) { + getURIFromRDF(id) { // Exit if no id was given if (!id) return ""; // Create a literal node with the identifier as the value const XSD = this.rdf.Namespace(this.namespaces.XSD); - const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); - const idNode = this.rdf.literal(id, undefined, XSD("string")); - // Find the identifier statements for the given id - const idStatements = this.dataPackageGraph.statementsMatching( - undefined, - DCTERMS("identifier"), - idNode, - ); + const DCTERMS = this.rdf.Namespace(this.namespaces.DCTERMS); + const idNode = this.rdf.literal(id, undefined, XSD("string")); + // Find the identifier statements for the given id + const idStatements = this.dataPackageGraph.statementsMatching( + undefined, + DCTERMS("identifier"), + idNode, + ); // If this object has an identifier statement, if (idStatements.length > 0) { // Return the subject of the statement return idStatements[0].subject.value; - } - return this.getCnURI() + encodeURIComponent(id); - + } + return this.getCnURI() + encodeURIComponent(id); }, /** @@ -3306,19 +3320,20 @@ define([ * or if not found in the RDF, from the app configuration. * @returns {string} - The CN resolve URL */ - getCnURI () { + getCnURI() { // If the CN resolve URL was already found, return it if (this.dataPackageGraph.cnResolveUrl) { return this.dataPackageGraph.cnResolveUrl; - } if (this.packageModel.get("oldPid")) { + } + if (this.packageModel.get("oldPid")) { // Find the identifier statement for the resource map in the RDF graph const idNode = this.rdf.lit(this.packageModel.get("oldPid")); - const idStatements = this.dataPackageGraph.statementsMatching( - undefined, - undefined, - idNode, - ); - const idStatement = idStatements.length ? idStatements[0] : null; + const idStatements = this.dataPackageGraph.statementsMatching( + undefined, + undefined, + idNode, + ); + const idStatement = idStatements.length ? idStatements[0] : null; if (idStatement) { // Parse the CN resolve URL from the statement subject URI @@ -3351,7 +3366,7 @@ define([ /** * Checks if this resource map has had any changes that requires an update */ - needsUpdate () { + needsUpdate() { // Check for changes to the list of aggregated members const ids = this.pluck("id"); if ( @@ -3365,14 +3380,14 @@ define([ if (this.provEdits.length) return true; // Check for changes to the isDocumentedBy relationships let isDifferent = false; - let i = 0; + let i = 0; // Keep going until we find a difference while (!isDifferent && i < this.length) { // Get the original isDocBy relationships from the resource map, and the new isDocBy relationships from the models let isDocBy = this.models[i].get("isDocumentedBy"); - const id = this.models[i].get("id"); - let origIsDocBy = this.originalIsDocBy[id]; + const id = this.models[i].get("id"); + let origIsDocBy = this.originalIsDocBy[id]; // Make sure they are both formatted as arrays for these checks isDocBy = _.uniq( @@ -3412,14 +3427,16 @@ define([ /* * Returns an array of the models that are in the queue or in progress of uploading */ - getQueue () { - return this.filter((m) => m.get("uploadStatus") == "q" || m.get("uploadStatus") == "p"); + getQueue() { + return this.filter( + (m) => m.get("uploadStatus") == "q" || m.get("uploadStatus") == "p", + ); }, /* * Adds a DataONEObject model to this DataPackage collection */ - addNewModel (model) { + addNewModel(model) { // Check that this collection doesn't already contain this model if (!this.contains(model)) { this.add(model); @@ -3430,15 +3447,17 @@ define([ } }, - handleAdd (dataONEObject) { + handleAdd(dataONEObject) { const metadataModel = this.find((m) => m.get("type") == "Metadata"); // Append to or create a new documents list if (metadataModel) { if (!Array.isArray(metadataModel.get("documents"))) { metadataModel.set("documents", [dataONEObject.id]); - } else if (!_.contains(metadataModel.get("documents"), dataONEObject.id)) - metadataModel.get("documents").push(dataONEObject.id); + } else if ( + !_.contains(metadataModel.get("documents"), dataONEObject.id) + ) + metadataModel.get("documents").push(dataONEObject.id); // Create an EML Entity for this DataONE Object if there isn't one already if ( @@ -3488,7 +3507,7 @@ define([ * Fetches this DataPackage from the Solr index by using a SolrResults collection * and merging the models in. */ - fetchFromIndex () { + fetchFromIndex() { if (typeof this.solrResults === "undefined" || !this.solrResults) { this.solrResults = new SolrResults(); } @@ -3517,7 +3536,7 @@ define([ * @param {Backbone.Model[]} otherModels - the other models to merge with the models in this collection * @param {string[]} [fieldsToMerge] - If specified, only these fields will be extracted from the otherModels */ - mergeModels (otherModels, fieldsToMerge) { + mergeModels(otherModels, fieldsToMerge) { // If no otherModels are given, exit the function since there is nothing to merge if ( typeof otherModels === "undefined" || @@ -3550,10 +3569,10 @@ define([ else { // Get the default values for this model type const otherModelDefaults = otherModel.defaults; - // Get a JSON object of all the attributes on this model - const otherModelAttr = otherModel.toJSON(); - // Start an array of attributes to omit during the merge - const omitKeys = []; + // Get a JSON object of all the attributes on this model + const otherModelAttr = otherModel.toJSON(); + // Start an array of attributes to omit during the merge + const omitKeys = []; _.each(otherModelAttr, (val, key) => { // If this model's attribute is the default, don't set it on our DataONEObject model @@ -3577,7 +3596,7 @@ define([ /** * Update the relationships in this resource map when its been udpated */ - updateRelationships () { + updateRelationships() { // Get the old id const oldId = this.packageModel.get("oldPid"); @@ -3592,7 +3611,7 @@ define([ }, this); }, - saveReference (model) { + saveReference(model) { // Save a reference to this collection in the model const currentCollections = model.get("collections"); if (currentCollections.length > 0) { @@ -3612,7 +3631,7 @@ define([ * @param {AccessPolicy} accessPolicy - The accessPolicy to * broadcast */ - broadcastAccessPolicy (accessPolicy) { + broadcastAccessPolicy(accessPolicy) { if (!accessPolicy) { return; } @@ -3649,7 +3668,7 @@ define([ * the number of loading files will be calcualted and set on the packageModel. * @since 2.17.1 */ - setLoadingFiles (dataONEObject) { + setLoadingFiles(dataONEObject) { // Set the number of loading files and the isLoadingFiles flag const numLoadingFiles = this.where({ uploadStatus: "l" }).length + @@ -3688,7 +3707,7 @@ define([ * @returns object with PIDs as key and atLocation paths as values * @since 2.28.0 */ - getAtLocation () { + getAtLocation() { return this.atLocationObject; },