Skip to content

Conversation

@robyngit
Copy link
Member

@robyngit robyngit commented Mar 5, 2025

  • Fixes some linting errors
  • Adds a few taxa related methods to EML model (including unit tests)

Fixes issue #25 (the oldest issue in MetacatUI currently :) )

robyngit added 4 commits March 5, 2025 13:53
Minimal linting and clean-up, mostly code left-as is but relocated

Issue #25
Code could still use some re-organization and modernization

Issue #25
@robyngit robyngit requested a review from rushirajnenuji March 5, 2025 21:23
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

eslint

🚫 [eslint] <no-unused-vars> reported by reviewdog 🐶
'nodeNameMap' is assigned a value but never used. Allowed unused vars must match /^_/u.

const nodeNameMap = this.nodeNameMap();


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.

function (fieldName) {


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.

function (thisTextModel, i) {


🚫 [eslint] <vars-on-top> reported by reviewdog 🐶
All 'var' declarations must be at the top of the function scope.

var coverageNode = $(document.createElement("coverage"));


🚫 [eslint] <no-var> reported by reviewdog 🐶
Unexpected var, use let or const instead.

var coverageNode = $(document.createElement("coverage"));


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

var coverageNode = $(document.createElement("coverage"));


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

if (coveragePosition) coveragePosition.after(coverageNode);


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

else datasetNode.append(coverageNode);


🚫 [eslint] <vars-on-top> reported by reviewdog 🐶
All 'var' declarations must be at the top of the function scope.

var coverageNode = datasetNode.children("coverage").first();


🚫 [eslint] <no-var> reported by reviewdog 🐶
Unexpected var, use let or const instead.

var coverageNode = datasetNode.children("coverage").first();


🚫 [eslint] <no-redeclare> reported by reviewdog 🐶
'coverageNode' is already defined.

var coverageNode = datasetNode.children("coverage").first();


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

var coverageNode = datasetNode.children("coverage").first();


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

coverageNode.children("geographiccoverage").remove();


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

coverageNode.children("geographiccoverage").remove();


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

const existingTaxonCov = coverageNode.children("taxonomiccoverage");


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

const existingTaxonCov = coverageNode.children("taxonomiccoverage");


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

coverageNode.append(taxonCoverage.updateDOM());


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

coverageNode.append(taxonCoverage.updateDOM());


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

sortedTaxonModels.notEmpty.length == 0


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

coverageNode.append(temporalCoverage.updateDOM());


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

coverageNode.append(temporalCoverage.updateDOM());


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

if (!coverageNode.children("temporalcoverage").children().length) {


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

if (!coverageNode.children("temporalcoverage").children().length) {


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

coverageNode.children("temporalcoverage").remove();


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

coverageNode.children("temporalcoverage").remove();


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

if (coverageNode.children().length == 0) {


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

if (coverageNode.children().length == 0) {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (coverageNode.children().length == 0) {


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 913 column 15 is used outside of binding context.

coverageNode.remove();


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'coverageNode' declared on line 919 column 15 is used outside of binding context.

coverageNode.remove();


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.

function (entity, position) {


🚫 [eslint] <no-plusplus> reported by reviewdog 🐶
Unary operator '++' used.


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.

function (rootEMLNode) {


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.

function (party, i) {


🚫 [eslint] <no-unused-vars> reported by reviewdog 🐶
'i' is defined but never used. Allowed unused args must match /^_/u.

function (party, i) {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (type == "contact" && !this.get("contact").length) {


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (this.get("keywordSets").length == 0) return;


🚫 [eslint] <no-plusplus> reported by reviewdog 🐶
Unary operator '++' used.

for (let i = models.length; i < nodes.length; i++) {


🚫 [eslint] <vars-on-top> reported by reviewdog 🐶
All 'var' declarations must be at the top of the function scope.

var party = new EMLParty({ parentModel: this, type: "creator" });


🚫 [eslint] <no-var> reported by reviewdog 🐶
Unexpected var, use let or const instead.

var party = new EMLParty({ parentModel: this, type: "creator" });


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'party' declared on line 1369 column 15 is used outside of binding context.

var party = new EMLParty({ parentModel: this, type: "creator" });


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'party' declared on line 1369 column 15 is used outside of binding context.

this.set("creator", [party]);


🚫 [eslint] <vars-on-top> reported by reviewdog 🐶
All 'var' declarations must be at the top of the function scope.

var party = new EMLParty({ parentModel: this, type: "contact" });


🚫 [eslint] <no-var> reported by reviewdog 🐶
Unexpected var, use let or const instead.

var party = new EMLParty({ parentModel: this, type: "contact" });


🚫 [eslint] <no-redeclare> reported by reviewdog 🐶
'party' is already defined.

var party = new EMLParty({ parentModel: this, type: "contact" });


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'party' declared on line 1360 column 15 is used outside of binding context.

var party = new EMLParty({ parentModel: this, type: "contact" });


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'party' declared on line 1360 column 15 is used outside of binding context.

this.set("contact", [party]);


🚫 [eslint] <vars-on-top> reported by reviewdog 🐶
All 'var' declarations must be at the top of the function scope.


🚫 [eslint] <no-var> reported by reviewdog 🐶
Unexpected var, use let or const instead.


🚫 [eslint] <consistent-return> reported by reviewdog 🐶
Method 'save' expected a return value.


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'md5' is not defined.

const checksum = md5(xml);


🚫 [eslint] <vars-on-top> reported by reviewdog 🐶
All 'var' declarations must be at the top of the function scope.


🚫 [eslint] <no-var> reported by reviewdog 🐶
Unexpected var, use let or const instead.


🚫 [eslint] <no-redeclare> reported by reviewdog 🐶
'model' is already defined.


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'model' declared on line 1378 column 15 is used outside of binding context.


🚫 [eslint] <block-scoped-var> reported by reviewdog 🐶
'model' declared on line 1378 column 15 is used outside of binding context.

model.set("uploadProgress", percentComplete);


🚫 [eslint] <no-shadow> reported by reviewdog 🐶
'model' is already declared in the upper scope on line 1378 column 15.

success(model, response, xhr) {


🚫 [eslint] <no-unused-vars> reported by reviewdog 🐶
'response' is defined but never used. Allowed unused args must match /^_/u.

success(model, response, xhr) {


🚫 [eslint] <no-unused-vars> reported by reviewdog 🐶
'xhr' is defined but never used. Allowed unused args must match /^_/u.

success(model, response, xhr) {


🚫 [eslint] <no-shadow> reported by reviewdog 🐶
'model' is already declared in the upper scope on line 1378 column 15.

error(model, response, xhr) {


🚫 [eslint] <no-unused-vars> reported by reviewdog 🐶
'xhr' is defined but never used. Allowed unused args must match /^_/u.

error(model, response, xhr) {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

(response.status == 408 || response.status == 0)


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

(response.status == 408 || response.status == 0)


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (!errorMsg || response.status == 408 || response.status == 0)


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (!errorMsg || response.status == 408 || response.status == 0)


🚫 [eslint] <consistent-return> reported by reviewdog 🐶
Expected to return a value at the end of method 'validate'.


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (errors.temporalCoverage.length == 0) {


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.


🚫 [eslint] <no-unused-vars> reported by reviewdog 🐶
'i' is defined but never used. Allowed unused args must match /^_/u.


🚫 [eslint] <no-unused-vars> reported by reviewdog 🐶
'i' is defined but never used. Allowed unused args must match /^_/u.

(geoCoverageModel, i) => {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

for ([field, isRequired] of Object.entries(


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'isRequired' is not defined.

for ([field, isRequired] of Object.entries(


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'isRequired' is not defined.

if (!isRequired) continue;


🚫 [eslint] <no-continue> reported by reviewdog 🐶
Unexpected use of continue statement.

if (!isRequired) continue;


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

if (field == "alternateIdentifier") {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (field == "alternateIdentifier") {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

(altId) => altId.trim() == "",


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

else if (field == "methods") {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

else if (field == "methods") {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

} else if (field == "abstract") {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

} else if (field == "abstract") {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

} else if (field == "dataSensitivity") {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

} else if (field == "dataSensitivity") {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

if (EMLParty.prototype.defaults().roleOptions?.includes(field)) {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

(t) => t.dataCategory == field,


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

(t) => t.dataCategory == field,


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

} else if (!this.get(field)?.length) {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

(t) => t.dataCategory == field,


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

(t) => t.dataCategory == field,


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

} else if (!this.get(field) || !this.get(field)?.length) {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

} else if (!this.get(field) || !this.get(field)?.length) {


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

errors[field] = `Provide a ${field}.`;


🚫 [eslint] <no-undef> reported by reviewdog 🐶
'field' is not defined.

errors[field] = `Provide a ${field}.`;


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (position == -1) {


🚫 [eslint] <no-plusplus> reported by reviewdog 🐶
Unary operator '--' used.

for (let i = position - 1; i >= 0; i--) {


🚫 [eslint] <no-underscore-dangle> reported by reviewdog 🐶
Unexpected dangling '_' in 'super'.

if (this.constructor.__super__.hasUpdates.call(this)) return true;


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (typeof position === "undefined" || position == -1)


🚫 [eslint] <consistent-return> reported by reviewdog 🐶
Expected to return a value at the end of function.


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (e.get("xmlID") == dataONEObj.getXMLSafeID()) return true;


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

e.get("physicalMD5Checksum") == dataONEObj.get("checksum") &&


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

dataONEObj.get("checksumAlgorithm").toUpperCase() == "MD5"


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

e.get("downloadID") == dataONEObj.get("id")


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

fileNameFromEML.replace(/ /g, "_").toLowerCase() ==


🚫 [eslint] <consistent-return> reported by reviewdog 🐶
Expected to return a value at the end of arrow function.

const otherMatchingEntity = _.find(otherEntities, (otherE) => {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

otherFileNameFromEML == dataONEObj.get("fileName") ||


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

otherFileNameFromEML.replace(/ /g, "_") ==


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (entity.get("dataONEObject") == dataONEObj) {


🚫 [eslint] <consistent-return> reported by reviewdog 🐶
Method 'getEntity' expected a return value.


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

e.get("formatName") ==


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (matchingTypes.length == 1) {


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (this.get("entities").length == 1) {


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.

this.listenTo(dataONEObject, "errorSaving", function () {


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed function.

this.listenToOnce(dataONEObject, "successSaving", function () {


⚠️ [eslint] <jsdoc/valid-types> reported by reviewdog 🐶
Syntax error in namepath: partyModel:

* @param {EMLParty} partyModel: The EMLParty model we're moving


⚠️ [eslint] <jsdoc/check-param-names> reported by reviewdog 🐶
Expected @param names to be "partyModel". Got "partyModel:, partyModel".

* @param {EMLParty} partyModel: The EMLParty model we're moving


⚠️ [eslint] <jsdoc/require-param-description> reported by reviewdog 🐶
Missing JSDoc @param "partyModel" description.


⚠️ [eslint] <jsdoc/require-param-type> reported by reviewdog 🐶
Missing JSDoc @param "partyModel" type.


⚠️ [eslint] <jsdoc/valid-types> reported by reviewdog 🐶
Syntax error in namepath: partyModel:

* @param {EMLParty} partyModel: The EMLParty model we're moving


⚠️ [eslint] <jsdoc/check-param-names> reported by reviewdog 🐶
Expected @param names to be "partyModel". Got "partyModel:, partyModel".

* @param {EMLParty} partyModel: The EMLParty model we're moving


⚠️ [eslint] <jsdoc/require-param-description> reported by reviewdog 🐶
Missing JSDoc @param "partyModel" description.


⚠️ [eslint] <jsdoc/require-param-type> reported by reviewdog 🐶
Missing JSDoc @param "partyModel" type.


🚫 [eslint] <consistent-return> reported by reviewdog 🐶
Expected to return a value at the end of method 'getPartiesByType'.

getPartiesByType(partyType) {


⚠️ [eslint] <no-console> reported by reviewdog 🐶
Unexpected console statement.


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (this.get("uploadStatus") == "e" && this.get("errorMessage")) {


🚫 [eslint] <no-param-reassign> reported by reviewdog 🐶
Assignment to function parameter 'textString'.

textString = textString.trim();


🚫 [eslint] <no-param-reassign> reported by reviewdog 🐶
Assignment to function parameter 'textString'.

textString = textString.replace(xmlNode, tagName);


🚫 [eslint] <no-control-regex> reported by reviewdog 🐶
Unexpected control character(s) in regular expression: \x09, \x0a, \x0d.

/[^\u0009\u000a\u000d\u0020-\uD7FF\uE000-\uFFFD]/g;


🚫 [eslint] <no-param-reassign> reported by reviewdog 🐶
Assignment to function parameter 'textString'.

textString = textString.replace(invalidCharsRegEx, "");


🚫 [eslint] <prefer-const> reported by reviewdog 🐶
'referencesList' is never reassigned. Use 'const' instead.

referencesList = xmlDOM.getElementsByTagName("references");


🚫 [eslint] <no-shadow> reported by reviewdog 🐶
'referencesList' is already declared in the upper scope on line 2424 column 13.

(referencesEl, index, referencesList) => {


🚫 [eslint] <prefer-destructuring> reported by reviewdog 🐶
Use array destructuring.

referencesParentEl = $(referencesEl).parent()[0];


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '!==' and instead saw '!='.

if (typeof referencedID !== "undefined" && referencedID != "") {


🚫 [eslint] <prefer-destructuring> reported by reviewdog 🐶
Use array destructuring.

title = this.get("title")[0];


⚠️ [eslint] <jsdoc/valid-types> reported by reviewdog 🐶
Syntax error in namepath: eml:

* @param {Element} eml: The root eml:eml element to modify


⚠️ [eslint] <jsdoc/check-param-names> reported by reviewdog 🐶
Expected @param names to be "eml". Got "eml:, eml".

* @param {Element} eml: The root eml:eml element to modify


⚠️ [eslint] <jsdoc/require-param-description> reported by reviewdog 🐶
Missing JSDoc @param "eml" description.


⚠️ [eslint] <jsdoc/require-param-type> reported by reviewdog 🐶
Missing JSDoc @param "eml" type.


⚠️ [eslint] <jsdoc/check-property-names> reported by reviewdog 🐶
@Property path declaration ("annotationData.elementName") appears before any real property.

* @property {string} [annotationData.elementName] The name of the EML Element that this


🚫 [eslint] <no-param-reassign> reported by reviewdog 🐶
Assignment to property of function parameter 'annotationData'.

delete annotationData.elementName;


🚫 [eslint] <no-param-reassign> reported by reviewdog 🐶
Assignment to property of function parameter 'annotationData'.

delete annotationData.allowDuplicates;


🚫 [eslint] <eqeqeq> reported by reviewdog 🐶
Expected '===' and instead saw '=='.

if (elementName == "dataset") {


⚠️ [eslint] <jsdoc/require-returns-description> reported by reviewdog 🐶
Missing JSDoc @returns description.

* @returns {EMLAnnotation[]|undefined}


⚠️ [eslint] <no-console> reported by reviewdog 🐶
Unexpected console statement.

console.error("Failed to get Data Sensitivity from EML model: ", e);


⚠️ [eslint] <jsdoc/check-types> reported by reviewdog 🐶
Invalid JSDoc @typedef "TaxonomicClassification" type "Object"; prefer: "object".

* @typedef {Object} TaxonomicClassification


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed method 'renderTaxa'.

renderTaxa: function () {


🚫 [eslint] <object-shorthand> reported by reviewdog 🐶
Expected method shorthand.

renderTaxa: function () {
// const taxaSectionEl = this.$(".section.taxa");
const taxaSectionEl = document.querySelector(".section.taxa");
if (!taxaSectionEl) return;
// TODO
this.taxaView = new EMLTaxonView({
el: taxaSectionEl,
parentModel: this.model,
taxonArray: this.model.get("taxonCoverage"),
edit: this.edit,
// isNew: false, // needed?
// parentView: this, // needed?
}).render();
},


⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed method 'updateRadioButtons'.

updateRadioButtons: function (e) {


🚫 [eslint] <spaced-comment> reported by reviewdog 🐶
Expected exception block, space or tab after '//' in comment.

//Get the element of this radio button set that is checked

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <prefer-arrow-callback> reported by reviewdog 🐶
Unexpected function expression.

define(["jquery", "underscore", "backbone", "models/DataONEObject"], function (
$,
_,
Backbone,
DataONEObject,
) {
/**
* @typedef {Object} TaxonomicClassification
* @property {string} taxonRankName - The name of the taxonomic rank, for
* example, Domain, Kingdom, etc.
* @property {string} taxonRankValue - The value for the given taxonomic rank,
* for example, Animalia, Chordata, etc.
* @property {string[]} commonName - Common name(s) for the taxon, for example
* ["Animal"]
* @property {Object[]} taxonId - A taxon identifier from a controlled
* vocabulary, for example, ITIS, NCBI, etc.
* @property {string} taxonId.provider - The provider of the taxon identifier,
* given as a URI, for example http://www.itis.gov
* @property {string} taxonId.value - The identifier from the provider, for
* example, 180092
* @property {Object[]} taxonomicClassification - A nested taxonomic
* classification, since taxonomy is represented as a hierarchy in EML.
*/
/**
* @class EMLTaxonCoverage
* @classdesc The EMLTaxonCoverage model represents the taxonomic coverage of
* a dataset. It includes a general description of the taxonomic coverage, as
* well as a list of taxonomic classifications.
* @classcategory Models/Metadata/EML
* @extends Backbone.Model
* @constructor
*/
var EMLTaxonCoverage = Backbone.Model.extend(
/** @lends EMLTaxonCoverage.prototype */ {
/**
* Returns the default properties for this model. Defined here.
* @type {Object}
* @property {string} objectXML - The XML string for this model
* @property {Element} objectDOM - The XML DOM for this model
* @property {EML211} parentModel - The parent EML211 model
* @property {taxonomicClassification[]} taxonomicClassification - An array
* of taxonomic classifications, defining the taxonomic coverage of the
* dataset
* @property {string} generalTaxonomicCoverage - A general description of the
* taxonomic coverage of the dataset
*/
defaults: {
objectXML: null,
objectDOM: null,
parentModel: null,
taxonomicClassification: [],
generalTaxonomicCoverage: null,
},
initialize: function (attributes) {
if (attributes.objectDOM) this.set(this.parse(attributes.objectDOM));
this.on("change:taxonomicClassification", this.trickleUpChange);
this.on("change:taxonomicClassification", this.updateDOM);
},
/*
* Maps the lower-case EML node names (valid in HTML DOM) to the camel-cased
* EML node names (valid in EML). Used during parse() and serialize()
*/
nodeNameMap: function () {
return {
generaltaxonomiccoverage: "generalTaxonomicCoverage",
taxonomicclassification: "taxonomicClassification",
taxonrankname: "taxonRankName",
taxonrankvalue: "taxonRankValue",
taxonomiccoverage: "taxonomicCoverage",
taxonomicsystem: "taxonomicSystem",
classificationsystem: "classificationSystem",
classificationsystemcitation: "classificationSystemCitation",
classificationsystemmodifications:
"classificationSystemModifications",
identificationreference: "identificationReference",
identifiername: "identifierName",
taxonomicprocedures: "taxonomicProcedures",
taxonomiccompleteness: "taxonomicCompleteness",
taxonid: "taxonId",
commonname: "commonName",
};
},
parse: function (objectDOM) {
if (!objectDOM) var xml = this.get("objectDOM");
var model = this,
taxonomicClassifications = $(objectDOM).children(
"taxonomicclassification",
),
modelJSON = {
taxonomicClassification: _.map(
taxonomicClassifications,
function (tc) {
return model.parseTaxonomicClassification(tc);
},
),
generalTaxonomicCoverage: $(objectDOM)
.children("generaltaxonomiccoverage")
.first()
.text(),
};
return modelJSON;
},
parseTaxonomicClassification: function (classification) {
var id = $(classification).attr("id");
var rankName = $(classification).children("taxonrankname");
var rankValue = $(classification).children("taxonrankvalue");
var commonName = $(classification).children("commonname");
var taxonId = $(classification).children("taxonId");
var taxonomicClassification = $(classification).children(
"taxonomicclassification",
);
var model = this,
modelJSON = {
id: id,
taxonRankName: $(rankName).text().trim(),
taxonRankValue: $(rankValue).text().trim(),
commonName: _.map(commonName, function (cn) {
return $(cn).text().trim();
}),
taxonId: _.map(taxonId, function (tid) {
return {
provider: $(tid).attr("provider").trim(),
value: $(tid).text().trim(),
};
}),
taxonomicClassification: _.map(
taxonomicClassification,
function (tc) {
return model.parseTaxonomicClassification(tc);
},
),
};
if (
Array.isArray(modelJSON.taxonomicClassification) &&
!modelJSON.taxonomicClassification.length
)
modelJSON.taxonomicClassification = {};
return modelJSON;
},
serialize: function () {
var objectDOM = this.updateDOM(),
xmlString = objectDOM.outerHTML;
//Camel-case the XML
xmlString = this.formatXML(xmlString);
return xmlString;
},
/*
* Makes a copy of the original XML DOM and updates it with the new values
* from the model.
*/
updateDOM: function () {
var objectDOM = this.get("objectDOM")
? this.get("objectDOM").cloneNode(true)
: document.createElement("taxonomiccoverage");
$(objectDOM).empty();
// generalTaxonomicCoverage
var generalCoverage = this.get("generalTaxonomicCoverage");
if (_.isString(generalCoverage) && generalCoverage.length > 0) {
$(objectDOM).append(
$(document.createElement("generaltaxonomiccoverage")).text(
this.get("generalTaxonomicCoverage"),
),
);
}
// taxonomicClassification(s)
var classifications = this.get("taxonomicClassification");
if (
typeof classifications === "undefined" ||
classifications.length === 0
) {
return objectDOM;
}
for (var i = 0; i < classifications.length; i++) {
$(objectDOM).append(
this.createTaxonomicClassificationDOM(classifications[i]),
);
}
// Remove empty (zero-length or whitespace-only) nodes
$(objectDOM)
.find("*")
.filter(function () {
return $.trim(this.innerHTML) === "";
})
.remove();
return objectDOM;
},
/*
* Create the DOM for a single EML taxonomicClassification.
* This function is currently recursive!
*/
createTaxonomicClassificationDOM: function (classification) {
var id = classification.id,
taxonRankName = classification.taxonRankName || "",
taxonRankValue = classification.taxonRankValue || "",
commonName = classification.commonName || "",
taxonId = classification.taxonId,
finishedEl;
if (!taxonRankName || !taxonRankValue) return "";
finishedEl = $(document.createElement("taxonomicclassification"));
if (typeof id === "string" && id.length > 0) {
$(finishedEl).attr("id", id);
}
if (taxonRankName && taxonRankName.length > 0) {
$(finishedEl).append(
$(document.createElement("taxonrankname")).text(taxonRankName),
);
}
if (taxonRankValue && taxonRankValue.length > 0) {
$(finishedEl).append(
$(document.createElement("taxonrankvalue")).text(taxonRankValue),
);
}
if (commonName && commonName.length > 0) {
$(finishedEl).append(
$(document.createElement("commonname")).text(commonName),
);
}
if (taxonId) {
if (!Array.isArray(taxonId)) taxonId = [taxonId];
_.each(taxonId, function (el) {
var taxonIdEl = $(document.createElement("taxonId")).text(el.value);
if (el.provider) {
$(taxonIdEl).attr("provider", el.provider);
}
$(finishedEl).append(taxonIdEl);
});
}
if (classification.taxonomicClassification) {
_.each(
classification.taxonomicClassification,
function (tc) {
$(finishedEl).append(this.createTaxonomicClassificationDOM(tc));
},
this,
);
}
return finishedEl;
},
/* Validate this model */
validate: function () {
var errors = {};
if (
!this.get("generalTaxonomicCoverage") &&
MetacatUI.appModel.get("emlEditorRequiredFields")
.generalTaxonomicCoverage
)
errors.generalTaxonomicCoverage =
"Provide a description of the taxonomic coverage.";
//If there are no taxonomic classifications and it is either required in
// the AppModel OR a general coverage was given, then require it
if (
!this.get("taxonomicClassification").length &&
(MetacatUI.appModel.get("emlEditorRequiredFields").taxonCoverage ||
this.get("generalTaxonomicCoverage"))
) {
errors.taxonomicClassification =
"Provide at least one complete taxonomic classification.";
} else {
//Every taxonomic classification should be valid
if (
!_.every(
this.get("taxonomicClassification"),
this.isClassificationValid,
this,
)
)
errors.taxonomicClassification =
"Every classification row should have a rank and value.";
}
// Check for and remove duplicate classifications
this.removeDuplicateClassifications();
if (Object.keys(errors).length) return errors;
},
isEmpty: function () {
return (
!this.get("generalTaxonomicCoverage") &&
!this.get("taxonomicClassification").length
);
},
isClassificationValid: function (taxonomicClassification) {
if (!Object.keys(taxonomicClassification).length) return true;
if (Array.isArray(taxonomicClassification)) {
if (
!taxonomicClassification[0].taxonRankName ||
!taxonomicClassification[0].taxonRankValue
) {
return false;
}
} else if (
!taxonomicClassification.taxonRankName ||
!taxonomicClassification.taxonRankValue
) {
return false;
}
if (taxonomicClassification.taxonomicClassification)
return this.isClassificationValid(
taxonomicClassification.taxonomicClassification,
);
else return true;
},
/**
* Check if two classifications are equal. Two classifications are equal if
* they have the same rankName, rankValue, commonName, and taxonId, as well
* as the same nested classifications. This function is recursive.
* @param {taxonomicClassification} c1
* @param {taxonomicClassification} c2
* @returns {boolean} - True if the two classifications are equal
* @since 2.24.0
*/
classificationsAreEqual: function (c1, c2) {
if (!c1 && !c2) return true;
if (!c1 && c2) return false;
if (c1 && !c2) return false;
// stringify the two classifications for
const stringKeys = ["taxonRankName", "taxonRankValue", "commonName"];
// Recursively stringify the nested classifications for comparison
stringifyClassification = function (c) {
const stringified = {};
for (let key of stringKeys) {
if (c[key]) stringified[key] = c[key];
}
if (c.taxonId) stringified.taxonId = c.taxonId;
if (c.taxonomicClassification) {
stringified.taxonomicClassification = stringifyClassification(
c.taxonomicClassification,
);
}
const st = JSON.stringify(stringified);
// convert all to uppercase for comparison
return st.toUpperCase();
};
return stringifyClassification(c1) === stringifyClassification(c2);
},
/**
* Returns true if the given classification is a duplicate of another
* classification in this model. Duplicates are considered those that have
* all values identical, including rankName, rankValue, commonName, and
* taxonId. If there are any nested classifications, then they too must
* be identical for the classification to be considered a duplicate, this
* this function is recursive. Only checks one classification at a time.
* @param {taxonomicClassification} classification
* @param {number} indexToSkip - The index of the classification to skip
* when checking for duplicates. This is useful when checking if a
* classification is a duplicate of another classification in the same
* model, but not itself.
* @returns {boolean} - True if the given classification is a duplicate
* @since 2.24.0
*/
isDuplicate: function (classification, indexToSkip) {
const classifications = this.get("taxonomicClassification");
for (let i = 0; i < classifications.length; i++) {
if (typeof indexToSkip === "number" && i === indexToSkip) continue;
if (
this.classificationsAreEqual(classifications[i], classification)
) {
return true;
}
}
return false;
},
/**
* Remove any duplicated classifications from this model. See
* {@link isDuplicate} for more information on what is considered a
* duplicate. If any classifications are removed, then a
* "duplicateClassificationsRemoved" event is triggered, passing the
* removed classifications as an argument.
* @fires duplicateClassificationsRemoved
* @since 2.24.0
*/
removeDuplicateClassifications: function () {
const classifications = this.get("taxonomicClassification");
const removed = [];
for (let i = 0; i < classifications.length; i++) {
const classification = classifications[i];
if (this.isDuplicate(classification, i)) {
classifications.splice(i, 1);
this.set("taxonomicClassification", classifications);
removed.push(classification);
i--;
}
}
if (removed.length) {
this.trigger("duplicateClassificationsRemoved", removed);
}
},
/*
* Climbs up the model hierarchy until it finds the EML model
*
* @return {EML211 or false} - Returns the EML 211 Model or false if not
* found
*/
getParentEML: function () {
var emlModel = this.get("parentModel"),
tries = 0;
while (emlModel.type !== "EML" && tries < 6) {
emlModel = emlModel.get("parentModel");
tries++;
}
if (emlModel && emlModel.type == "EML") return emlModel;
else return false;
},
trickleUpChange: function () {
MetacatUI.rootDataPackage.packageModel.set("changed", true);
},
formatXML: function (xmlString) {
return DataONEObject.prototype.formatXML.call(this, xmlString);
},
},
);
return EMLTaxonCoverage;
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <object-shorthand> reported by reviewdog 🐶
Expected method shorthand.

showPubDateValidation: function (e) {
var container = $(e.target).parents(".pubDate").first(),
input = $(e.target),
messageEl = $(container).find(".notification"),
value = input.val(),
errors = [];
// Remove existing error borders and notifications
input.removeClass("error");
messageEl.text("");
messageEl.removeClass("error");
if (value != "" && value.length > 0) {
if (!this.isDateFormatValid(value)) {
errors.push(
"The value entered for publication date, '" +
value +
"' is not a valid value for this field. Enter either a year (e.g. 2017) or a date in the format YYYY-MM-DD.",
);
input.addClass("error");
}
}
if (errors.length > 0) {
messageEl.text(errors[0]).addClass("error");
}
},

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <object-shorthand> reported by reviewdog 🐶
Expected method shorthand.

updateRadioButtons: function (e) {
//Get the element of this radio button set that is checked
var choice = this.$(
"[name='" + $(e.target).attr("name") + "']:checked",
).val();
if (typeof choice == "undefined" || !choice)
this.model.set($(e.target).attr("data-category"), "");
else this.model.set($(e.target).attr("data-category"), choice);
this.model.trickleUpChange();
},

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <no-var> reported by reviewdog 🐶
Unexpected var, use let or const instead.

var choice = this.$(
"[name='" + $(e.target).attr("name") + "']:checked",
).val();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <object-shorthand> reported by reviewdog 🐶
Expected method shorthand.

handleRemove: function (e) {
var submodel = $(e.target).data("submodel"), // Optional sub-model to remove attribute from
attribute = $(e.target).data("attribute"), // Attribute on the EML211 model we're removing from
selector = $(e.target).data("selector"), // Selector to find the parent DOM elemente we'll remove
container = $(e.target).data("container"), // Selector to find the parent container so we can remove by index
parentEl, // Element we'll remove
model; // Specific sub-model we're removing
if (!attribute) return;
if (!container) return;
// Find the element we'll remove from the DOM
if (selector) {
parentEl = $(e.target).parents(selector).first();
} else {
parentEl = $(e.target).parents().first();
}
if (parentEl.length == 0) return;
// Handle remove on a EML model / sub-model
if (submodel) {
model = this.model.get(submodel);
if (!model) return;
// Get the current value of the attribute so we can remove from it
var currentValue, submodelIndex;
if (Array.isArray(this.model.get(submodel))) {
// Stop now if there's nothing to remove in the first place
if (this.model.get(submodel).length == 0) return;
// For multi-valued submodels, find *which* submodel we are removing or
// removingn from
submodelIndex = $(container).index(
$(e.target).parents(container).first(),
);
if (submodelIndex === -1) return;
currentValue = this.model
.get(submodel)
[submodelIndex].get(attribute);
} else {
currentValue = this.model.get(submodel).get(attribute);
}
//FInd the position of this field in the list of fields
var position = $(e.target)
.parents(container)
.first()
.children(selector)
.index($(e.target).parents(selector));
// Remove from the EML Model
if (position >= 0) {
if (Array.isArray(this.model.get(submodel))) {
currentValue.splice(position, 1); // Splice returns the removed members
this.model
.get(submodel)
[submodelIndex].set(attribute, currentValue);
} else {
currentValue.splice(position, 1); // Splice returns the removed members
this.model.get(submodel).set(attribute, currentValue);
}
}
} else if (selector) {
// Find the index this attribute is in the DOM
var position = $(e.target)
.parents(container)
.first()
.children(selector)
.index($(e.target).parents(selector));
//Remove this index of the array
var currentValue = this.model.get(attribute);
if (Array.isArray(currentValue)) currentValue.splice(position, 1);
//Set the array on the model so the 'set' function is executed
this.model.set(attribute, currentValue);
}
// Handle remove on a basic text field
else {
// The DOM order matches the EML model attribute order so we can remove
// by that
var position = $(e.target)
.parents(container)
.first()
.children(selector)
.index(selector);
var currentValue = this.model.get(attribute);
// Remove from the EML Model
if (position >= 0) {
currentValue.splice(position, 1);
this.model.set(attribute, currentValue);
}
}
// Trigger a change on the entire package
MetacatUI.rootDataPackage.packageModel.set("changed", true);
// Remove the DOM
$(parentEl).remove();
//updating the tablesIndex once the element has been removed
var tableNums = this.$(".editor-header-index");
for (var i = 0; i < tableNums.length; i++) {
$(tableNums[i]).text(i + 1);
}
// If this was a taxon, update the quickAdd interface
if (submodel === "taxonCoverage") {
this.taxaView.updateQuickAddTaxa();
}
},

@robyngit robyngit linked an issue Mar 5, 2025 that may be closed by this pull request
}
return undefined;
} catch (e) {
console.error("Failed to get Data Sensitivity from EML model: ", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [eslint] <no-console> reported by reviewdog 🐶
Unexpected console statement.

Comment on lines 7 to +8
/**
* @name taxonomicClassification
* @type {Object}
* @typedef {Object} TaxonomicClassification
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [eslint] <jsdoc/check-types> reported by reviewdog 🐶
Invalid JSDoc @typedef "TaxonomicClassification" type "Object"; prefer: "object".

Suggested change
/**
* @name taxonomicClassification
* @type {Object}
* @typedef {Object} TaxonomicClassification
/**
* @typedef {object} TaxonomicClassification

/*
* Renders the Taxa section of the page
*/
renderTaxa: function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed method 'renderTaxa'.

/*
* Renders the Taxa section of the page
*/
renderTaxa: function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <object-shorthand> reported by reviewdog 🐶
Expected method shorthand.

Suggested change
renderTaxa: function () {
renderTaxa () {

}
},

updateRadioButtons: function (e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [eslint] <func-names> reported by reviewdog 🐶
Unexpected unnamed method 'updateRadioButtons'.

},

updateRadioButtons: function (e) {
//Get the element of this radio button set that is checked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [eslint] <spaced-comment> reported by reviewdog 🐶
Expected exception block, space or tab after '//' in comment.

Suggested change
//Get the element of this radio button set that is checked
// Get the element of this radio button set that is checked

@robyngit robyngit merged commit 4076f24 into develop Apr 7, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor EMLTaxon code into its own Backbone View

2 participants