diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml
index 13f6c32be..1d44f3f0e 100644
--- a/.github/workflows/qa.yml
+++ b/.github/workflows/qa.yml
@@ -3,17 +3,19 @@ name: Code Quality Assurance
on: [ push, pull_request ]
jobs:
- lint:
- name: Lint
+ lint-python:
+ name: Lint Python code
runs-on: ubuntu-latest
if: github.triggering_actor != 'github-actions[bot]'
+ permissions:
+ contents: read
strategy:
matrix:
python-version: [ "3.8", "3.10" ]
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
@@ -22,3 +24,17 @@ jobs:
pip install "tox<4.0.0"
- name: Check code quality with flake8
run: tox -e flake8
+ lint-js:
+ name: Lint Javascript code
+ runs-on: ubuntu-latest
+ if: github.triggering_actor != 'github-actions[bot]'
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Biome
+ uses: biomejs/setup-biome@v2
+ with:
+ version: latest
+ - name: Run Biome
+ run: biome format .
diff --git a/assets/js/custom.js b/assets/js/custom.js
index 454606055..afc0130a9 100644
--- a/assets/js/custom.js
+++ b/assets/js/custom.js
@@ -3,84 +3,103 @@ This script requires underscore.min.js for access to the underscore `_` object.
This doesn't appear to be documented anywhere what it does or why.
*/
-jQuery(document).ready(function() {
-
- function search() {
- $('#close', search).hide();
- var data = false;
- var matches = false;
- var search = $('#search');
- var find = function(phrase) {
- if (!data) {
- return $.ajax({
- url: '/search.json',
- dataType: 'json',
- success: function(resp) {
- data = _(resp).chain()
- .compact()
- .map(function(p) {
- p.words = (p.title.toLowerCase() + ' ' + p.summary.toLowerCase()).match(/(\w+)/g);
- return p;
- })
- .value();
- find(phrase);
- }
- });
- }
-
- matches = _(data).filter(function(p) {
- return _(phrase).filter(function(a) {
- return _(p.words).any(function(b) {
- return a === b || b.indexOf(a) === 0;
- });
- }).length === phrase.length;
- });
-
- $(matches).each(function() {
- $('#search-results', search).append('
' + this.title + ' ' + this.title + '... Read more
');
- });
-
- $('#search-results', search).show();
- $('#close', search).show();
- };
- $('input', search).bind("focus", _(function() {
- $('#search-results', search).empty();
- $('#search-results', search).hide();
- $('#close', search).hide();
-
- var phrase = $('input', search).val();
- if (phrase.length >= 4) {
- find(phrase.toLowerCase().match(/(\w+)/g));
- }
- return false;
- }).debounce(100));
-
- $('#close', search).bind("click", function() {
- $('#search-results', search).hide();
- $('#close', search).hide();
- return false;
+jQuery(document).ready(function () {
+ function search() {
+ $("#close", search).hide();
+ var data = false;
+ var matches = false;
+ var search = $("#search");
+ var find = function (phrase) {
+ if (!data) {
+ return $.ajax({
+ url: "/search.json",
+ dataType: "json",
+ success: function (resp) {
+ data = _(resp)
+ .chain()
+ .compact()
+ .map(function (p) {
+ p.words = (
+ p.title.toLowerCase() +
+ " " +
+ p.summary.toLowerCase()
+ ).match(/(\w+)/g);
+ return p;
+ })
+ .value();
+ find(phrase);
+ },
});
+ }
- $('input', search).keyup(_(function() {
- $('#search-results', search).empty();
- $('#search-results', search).hide();
- $('#close', search).hide();
-
- var phrase = $('input', search).val();
- if (phrase.length >= 4) {
- find(phrase.toLowerCase().match(/(\w+)/g));
- }
- return false;
- }).debounce(100));
+ matches = _(data).filter(function (p) {
+ return (
+ _(phrase).filter(function (a) {
+ return _(p.words).any(function (b) {
+ return a === b || b.indexOf(a) === 0;
+ });
+ }).length === phrase.length
+ );
+ });
+
+ $(matches).each(function () {
+ $("#search-results", search).append(
+ "" +
+ this.title +
+ " " +
+ this.title +
+ '... Read more
',
+ );
+ });
+
+ $("#search-results", search).show();
+ $("#close", search).show();
};
- search();
- //set the first character os string to uppercase
- function capitalize(string) {
- return string.charAt(0).toUpperCase() + string.slice(1);
- }
- //surround data table rows generated in rendertable function with table head
- function tableHtml(content, domain = false, tableDomains) {
- return `${ domain? capitalize(tableDomains): ""}
+ $("input", search).bind(
+ "focus",
+ _(function () {
+ $("#search-results", search).empty();
+ $("#search-results", search).hide();
+ $("#close", search).hide();
+
+ var phrase = $("input", search).val();
+ if (phrase.length >= 4) {
+ find(phrase.toLowerCase().match(/(\w+)/g));
+ }
+ return false;
+ }).debounce(100),
+ );
+
+ $("#close", search).bind("click", function () {
+ $("#search-results", search).hide();
+ $("#close", search).hide();
+ return false;
+ });
+
+ $("input", search).keyup(
+ _(function () {
+ $("#search-results", search).empty();
+ $("#search-results", search).hide();
+ $("#close", search).hide();
+
+ var phrase = $("input", search).val();
+ if (phrase.length >= 4) {
+ find(phrase.toLowerCase().match(/(\w+)/g));
+ }
+ return false;
+ }).debounce(100),
+ );
+ }
+ search();
+ //set the first character os string to uppercase
+ function capitalize(string) {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+ }
+ //surround data table rows generated in rendertable function with table head
+ function tableHtml(content, domain = false, tableDomains) {
+ return `${domain ? capitalize(tableDomains) : ""}
@@ -115,28 +134,34 @@ jQuery(document).ready(function() {
`;
- }
-
- // constrain status values, make them sortable
- const DashboardStatus = {
- PASS: 5, // all checks pass
- INFO: 4, // info parameters returned
- WARN: 3, // warnings raised
- ERROR: 2, // errors raised
- FAILED: 1, // dashboard QC failed to run
- UNKNOWN: 0, // not found in dashboard results
- }
-
- /**
- * Construct dashboard QC badge. Use predefined "NA" badge for obsolete/excluded ontologies.
- * @param {string} id Ontology id (short name)
- * @param {object} success_data Selected fields from dashboard for {id}
- * @param {number} dash_success Success grouping for ontology
- */
- const getDashboardBadge = (id, success_data, dash_success) => {
- const dash_badge_link_url = success_data.status > DashboardStatus.FAILED ? `http://dashboard.obofoundry.org/dashboard/${id}/dashboard.html` : `http://dashboard.obofoundry.org/dashboard`;
- const dash_badge_url = success_data.status !== DashboardStatus.UNKNOWN ? `https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FOBOFoundry%2Fobo-dash.github.io%2Fgh-pages%2Fdashboard%2F${id}%2Fdashboard-qc-badge.json` : "https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FOBOFoundry%2FOBOFoundry.github.io%2Fmaster%2Fassets%2Fjson%2Fna_dashboard_badge.json";
- const dash_status_indicator = `
+ }
+
+ // constrain status values, make them sortable
+ const DashboardStatus = {
+ PASS: 5, // all checks pass
+ INFO: 4, // info parameters returned
+ WARN: 3, // warnings raised
+ ERROR: 2, // errors raised
+ FAILED: 1, // dashboard QC failed to run
+ UNKNOWN: 0, // not found in dashboard results
+ };
+
+ /**
+ * Construct dashboard QC badge. Use predefined "NA" badge for obsolete/excluded ontologies.
+ * @param {string} id Ontology id (short name)
+ * @param {object} success_data Selected fields from dashboard for {id}
+ * @param {number} dash_success Success grouping for ontology
+ */
+ const getDashboardBadge = (id, success_data, dash_success) => {
+ const dash_badge_link_url =
+ success_data.status > DashboardStatus.FAILED
+ ? `http://dashboard.obofoundry.org/dashboard/${id}/dashboard.html`
+ : `http://dashboard.obofoundry.org/dashboard`;
+ const dash_badge_url =
+ success_data.status !== DashboardStatus.UNKNOWN
+ ? `https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FOBOFoundry%2Fobo-dash.github.io%2Fgh-pages%2Fdashboard%2F${id}%2Fdashboard-qc-badge.json`
+ : "https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FOBOFoundry%2FOBOFoundry.github.io%2Fmaster%2Fassets%2Fjson%2Fna_dashboard_badge.json";
+ const dash_status_indicator = `
${dash_success}
`;
- return dash_status_indicator;
- }
-
- /**
- * Construct and render HTML ontology table(s)
- * @param {object} data Ontology json data.
- * @param {boolean} [domain=false] if true, render tables grouped by domain rather than one big table
- */
- function renderTable(data, domain= false ) {
- const dashboard_url = "https://raw.githubusercontent.com/OBOFoundry/obo-dash.github.io/gh-pages/dashboard/dashboard-results.json";
- let dashboard_success_data;
- fetch(dashboard_url)
- .then(response => response.json())
- .then((dashboard_complete_data) => {
- dashboard_success_data = dashboard_complete_data.ontologies.reduce((acc, onto) => {
- const key = onto.namespace;
- if ("summary" in onto && "status" in onto.summary) {
- acc[key] = {
- status: DashboardStatus[onto.summary.status],
- is_passing: DashboardStatus[onto.summary.status] >= DashboardStatus.WARN,
- ...onto.summary.summary_count
- };
- } else {
- // dashboard QC failed
- acc[key] = {
- status: DashboardStatus.FAILED,
- is_passing: false,
- }
- }
- return acc;
- }, {})
- // the dashboard might be missing some ontologies -- e.g. if they're brand new
- dashboard_success_data = data.reduce((acc, onto) => {
- if (!(onto.id in dashboard_success_data)) {
- acc[onto.id] = {
- status: DashboardStatus.UNKNOWN,
- is_passing: false,
- }
- };
- return acc;
- }, dashboard_success_data);
- }).then(() => {
- // by default, sort ontology records first by dashboard success status, then alphabetically
- data.sort((a, b) => {
- if (dashboard_success_data[a.id].is_passing !== dashboard_success_data[b.id].is_passing) {
- return dashboard_success_data[a.id].is_passing > dashboard_success_data[b.id].is_passing ? -1 : 1;
- } else {
- return a.id > b.id ? 1 : -1;
- }
- });
-
- let table = ``;
- let domainTables = ``;
- let tableDomains = []; // list of domains
- let tableDomainhtml = {}; // hold html table data with domain as key
-
-
- for (let i = 0; i < data.length; i++) {
- let id = data[i]['id'];
- let dash_success = dashboard_success_data[id].is_passing ? 1 : 0;
- let is_obsolete = "";
- let is_inactive = "";
- let activity_status = data[i]['activity_status']
- let title = data[i]['title'];
- let license_url = "#";
- let license_logo = "#";
- let license_label = "";
- let description = data[i]['description'];
- let repo = "";
- let homepage = data[i]["homepage"];
- let tracker = "";
- let contact = "";
- let publication = "";
- let domainInner = ["Unknown"];
- if (data[i]['license']) {
- license_url = data[i]["license"]["url"];
- license_logo = data[i]["license"]["logo"];
- license_label = data[i]["license"]["label"];
- }
+ return dash_status_indicator;
+ };
+
+ /**
+ * Construct and render HTML ontology table(s)
+ * @param {object} data Ontology json data.
+ * @param {boolean} [domain=false] if true, render tables grouped by domain rather than one big table
+ */
+ function renderTable(data, domain = false) {
+ const dashboard_url =
+ "https://raw.githubusercontent.com/OBOFoundry/obo-dash.github.io/gh-pages/dashboard/dashboard-results.json";
+ let dashboard_success_data;
+ fetch(dashboard_url)
+ .then((response) => response.json())
+ .then((dashboard_complete_data) => {
+ dashboard_success_data = dashboard_complete_data.ontologies.reduce(
+ (acc, onto) => {
+ const key = onto.namespace;
+ if ("summary" in onto && "status" in onto.summary) {
+ acc[key] = {
+ status: DashboardStatus[onto.summary.status],
+ is_passing:
+ DashboardStatus[onto.summary.status] >= DashboardStatus.WARN,
+ ...onto.summary.summary_count,
+ };
+ } else {
+ // dashboard QC failed
+ acc[key] = {
+ status: DashboardStatus.FAILED,
+ is_passing: false,
+ };
+ }
+ return acc;
+ },
+ {},
+ );
+ // the dashboard might be missing some ontologies -- e.g. if they're brand new
+ dashboard_success_data = data.reduce((acc, onto) => {
+ if (!(onto.id in dashboard_success_data)) {
+ acc[onto.id] = {
+ status: DashboardStatus.UNKNOWN,
+ is_passing: false,
+ };
+ }
+ return acc;
+ }, dashboard_success_data);
+ })
+ .then(() => {
+ // by default, sort ontology records first by dashboard success status, then alphabetically
+ data.sort((a, b) => {
+ if (
+ dashboard_success_data[a.id].is_passing !==
+ dashboard_success_data[b.id].is_passing
+ ) {
+ return dashboard_success_data[a.id].is_passing >
+ dashboard_success_data[b.id].is_passing
+ ? -1
+ : 1;
+ } else {
+ return a.id > b.id ? 1 : -1;
+ }
+ });
- if (data[i]["repository"] && data[i]["repository"].includes("https://github.com/")) {
- repo = data[i]["repository"];
- github_box = `
+ let table = ``;
+ let domainTables = ``;
+ let tableDomains = []; // list of domains
+ let tableDomainhtml = {}; // hold html table data with domain as key
+
+ for (let i = 0; i < data.length; i++) {
+ let id = data[i]["id"];
+ let dash_success = dashboard_success_data[id].is_passing ? 1 : 0;
+ let is_obsolete = "";
+ let is_inactive = "";
+ let activity_status = data[i]["activity_status"];
+ let title = data[i]["title"];
+ let license_url = "#";
+ let license_logo = "#";
+ let license_label = "";
+ let description = data[i]["description"];
+ let repo = "";
+ let homepage = data[i]["homepage"];
+ let tracker = "";
+ let contact = "";
+ let publication = "";
+ let domainInner = ["Unknown"];
+ if (data[i]["license"]) {
+ license_url = data[i]["license"]["url"];
+ license_logo = data[i]["license"]["logo"];
+ license_label = data[i]["license"]["label"];
+ }
+
+ if (
+ data[i]["repository"] &&
+ data[i]["repository"].includes("https://github.com/")
+ ) {
+ repo = data[i]["repository"];
+ github_box = `
`;
- } else {
- github_box = ``;
- }
- if (data[i]["tracker"]) {
- tracker =`
+ } else {
+ github_box = ``;
+ }
+ if (data[i]["tracker"]) {
+ tracker = `
`;
- } else {
- tracker = `
+ } else {
+ tracker = `
`;
- }
- if (data[i].hasOwnProperty("domain") && data[i]['domain'] !== undefined) {
- domainInner[0] = data[i]['domain'];
- }
- if (description !== undefined && description.toString().length > 140) {
- description = description.toString().slice(0, 140).trim() + '...'
- }
- if (data[i]["contact"]) {
- contact =`
+ }
+ if (
+ data[i].hasOwnProperty("domain") &&
+ data[i]["domain"] !== undefined
+ ) {
+ domainInner[0] = data[i]["domain"];
+ }
+ if (
+ description !== undefined &&
+ description.toString().length > 140
+ ) {
+ description = description.toString().slice(0, 140).trim() + "...";
+ }
+ if (data[i]["contact"]) {
+ contact = `
`;
- } else {
- contact = `
+ } else {
+ contact = `
`;
- }
- if (data[i]["publications"] && data[i]["publications"].length > 0) {
- publication = `
+ }
+ if (data[i]["publications"] && data[i]["publications"].length > 0) {
+ publication = `
`;
- } else {
- publication = `
+ } else {
+ publication = `
`;
- }
- if (activity_status === "inactive" || activity_status === "orphaned") {
- is_inactive = "inactive_row";
- is_obsolete = `(${activity_status})`
- }
- if (data[i]["is_obsolete"]) {
- is_obsolete = "(obsolete)"
- }
- if (license_logo) {
- license_box = `
+ }
+ if (
+ activity_status === "inactive" ||
+ activity_status === "orphaned"
+ ) {
+ is_inactive = "inactive_row";
+ is_obsolete = `(${activity_status})`;
+ }
+ if (data[i]["is_obsolete"]) {
+ is_obsolete = "(obsolete)";
+ }
+ if (license_logo) {
+ license_box = `
`;
- } else {
- license_box = `${license_label} `;
- }
- if (description) {
- description_box = `${description}`;
- } else {
- description_box = ``;
- }
- // const dash_badge_link_url = dashboard_success_data[id].status !== DashboardStatus.FAILED ? `http://dashboard.obofoundry.org/dashboard/${id}/dashboard.html` : `http://dashboard.obofoundry.org/dashboard`;
- // dash_success_indicator = `
- // ${dash_success}
- //
- // `;
- let tr_class = is_inactive;
- if (!dash_success) {
- tr_class += " failing";
- }
+ } else {
+ license_box = `${license_label} `;
+ }
+ if (description) {
+ description_box = `${description}`;
+ } else {
+ description_box = ``;
+ }
+ // const dash_badge_link_url = dashboard_success_data[id].status !== DashboardStatus.FAILED ? `http://dashboard.obofoundry.org/dashboard/${id}/dashboard.html` : `http://dashboard.obofoundry.org/dashboard`;
+ // dash_success_indicator = `
+ // ${dash_success}
+ //
+ // `;
+ let tr_class = is_inactive;
+ if (!dash_success) {
+ tr_class += " failing";
+ }
// TODO
- console.log(getDashboardBadge(id, dashboard_success_data[id], dash_success));
- let template = `
+ console.log(
+ getDashboardBadge(id, dashboard_success_data[id], dash_success),
+ );
+ let template = `
@@ -348,215 +398,223 @@ jQuery(document).ready(function() {
`;
-
- if (domain === true) {
- tableDomains.push(domainInner[0].trim())
- if (!tableDomainhtml.hasOwnProperty(domainInner[0].trim())) {
- tableDomainhtml[domainInner[0].trim()] = template;
- } else {
- tableDomainhtml[domainInner[0].trim()] += template;
- }
- }
- table += template;
- }
- if (domain === true) {
- tableDomains = [...new Set(tableDomains)]
- //loops through list of domains and surrounds html row with table tag and headers
- for (let i = 0; i < tableDomains.length; i++) {
- let content = tableDomainhtml[tableDomains[i]];
-
- let table = tableHtml(content, true, tableDomains[i]);
-
- // merge all final table html generated above with the upper domain at top.
- if (tableDomains[i].toLowerCase() === "upper") {
- domainTables = table + domainTables;
- } else {
- domainTables = domainTables + table;
- }
-
- }
- }
- // append table(s) generated depending on if domain filter is active or not.
- if (domain === true) {
- $("#tableDiv").html(domainTables);
- } else {
- let res = tableHtml(table, false, "")
- $("#tableDiv").html(res);
- }
- });
- }
-
- /**
- * Sort json ontology data by the given sort field
- * @param {Object} data
- * @param {string|number} sortField
- */
- function sortByField(data, sortField) {
- return data.sort(function(a, b) {
- if (a[sortField] === undefined || b[sortField] === undefined) {
- return 0;
- }
- if (a[sortField].toLowerCase() < b[sortField].toLowerCase()) {
- return -1;
+ if (domain === true) {
+ tableDomains.push(domainInner[0].trim());
+ if (!tableDomainhtml.hasOwnProperty(domainInner[0].trim())) {
+ tableDomainhtml[domainInner[0].trim()] = template;
+ } else {
+ tableDomainhtml[domainInner[0].trim()] += template;
}
- if (a[sortField].toLowerCase() > b[sortField].toLowerCase()) {
- return 1;
+ }
+ table += template;
+ }
+ if (domain === true) {
+ tableDomains = [...new Set(tableDomains)];
+ //loops through list of domains and surrounds html row with table tag and headers
+ for (let i = 0; i < tableDomains.length; i++) {
+ let content = tableDomainhtml[tableDomains[i]];
+
+ let table = tableHtml(content, true, tableDomains[i]);
+
+ // merge all final table html generated above with the upper domain at top.
+ if (tableDomains[i].toLowerCase() === "upper") {
+ domainTables = table + domainTables;
+ } else {
+ domainTables = domainTables + table;
}
- return 0;
- });
- }
-
- /**
- * make a function wait for a given amount of time before running
- * @param {*} func
- * @param {number} timeout
- */
- function debounce(func, timeout = 300) {
- let timer;
- return (...args) => {
- clearTimeout(timer);
- timer = setTimeout(() => {
- func.apply(this, args);
- }, timeout);
- };
- }
-
- /**
- * applies selected checkbox filter to data passed in
- * @param {object} data
- */
- function applyFilters(data) {
- let selector = $("[data-filter]");
- let domain = selector[0].checked
- let hideactive = selector[1].checked
- let hideObsolete = selector[2].checked
- let filteredData = [];
-
- if (!domain && !hideactive && !hideObsolete) {
- renderTable(data);
- } else if (domain && !hideactive && !hideObsolete) {
- renderTable(sortByField(data, "domain"), true);
- } else if (domain && hideactive && !hideObsolete) {
- filteredData = data.filter(x => x["activity_status"] === "active");
- renderTable(sortByField(filteredData, "domain"), true);
- } else if (domain && !hideactive && hideObsolete) {
- filteredData = data.filter(x => x["is_obsolete"] !== true);
- renderTable(sortByField(filteredData, "domain"), true);
- } else if (domain && hideactive && hideObsolete) {
- filteredData = data.filter(x => x["is_obsolete"] !== true)
- .filter(x => x["activity_status"] === "active");
- renderTable(sortByField(filteredData, "domain"), true);
- } else if (!domain && hideactive && hideObsolete) {
- filteredData = data.filter(x => x["is_obsolete"] !== true)
- .filter(x => x["activity_status"] === "active");
- renderTable(filteredData);
- } else if (!domain && hideactive && !hideObsolete) {
- filteredData = data["ontologies"].filter(x => x["activity_status"] === "active");
- renderTable(filteredData);
- } else if (!domain && !hideactive && hideObsolete) {
- filteredData = data.filter(x => x["is_obsolete"] !== true);
- renderTable(filteredData);
+ }
}
- }
-
- /**
- * Search the given fields {domain, description, id} for input text
- * @param {*} input input is a Jquery selector of the search box
- * @param {object} JsonData Ontology data to search through
- * @return {object} returns filtered json data
- */
- function Search(input, JsonData) {
- let value = input.val().toLowerCase();
- // check text length greater than 2 before doing search
- if (value.length >= 2) {
- return JsonData.filter((row) => {
- let term = input.val().toLowerCase();
- if (row.domain === undefined) {
- row.domain = ""
- }
- if (row.description === undefined) {
- row.description = ""
- }
- if (row.title === undefined) {
- row.title = ""
- }
- return (row.id.toLowerCase().includes(term) ||
- row.domain.toLowerCase().includes(term) ||
- row.title.toLowerCase().includes(term) ||
- row.description.toLowerCase().includes(term))
- });
+ // append table(s) generated depending on if domain filter is active or not.
+ if (domain === true) {
+ $("#tableDiv").html(domainTables);
+ } else {
+ let res = tableHtml(table, false, "");
+ $("#tableDiv").html(res);
}
- return JsonData;
+ });
+ }
+
+ /**
+ * Sort json ontology data by the given sort field
+ * @param {Object} data
+ * @param {string|number} sortField
+ */
+ function sortByField(data, sortField) {
+ return data.sort(function (a, b) {
+ if (a[sortField] === undefined || b[sortField] === undefined) {
+ return 0;
+ }
+ if (a[sortField].toLowerCase() < b[sortField].toLowerCase()) {
+ return -1;
+ }
+ if (a[sortField].toLowerCase() > b[sortField].toLowerCase()) {
+ return 1;
+ }
+ return 0;
+ });
+ }
+
+ /**
+ * make a function wait for a given amount of time before running
+ * @param {*} func
+ * @param {number} timeout
+ */
+ function debounce(func, timeout = 300) {
+ let timer;
+ return (...args) => {
+ clearTimeout(timer);
+ timer = setTimeout(() => {
+ func.apply(this, args);
+ }, timeout);
+ };
+ }
+
+ /**
+ * applies selected checkbox filter to data passed in
+ * @param {object} data
+ */
+ function applyFilters(data) {
+ let selector = $("[data-filter]");
+ let domain = selector[0].checked;
+ let hideactive = selector[1].checked;
+ let hideObsolete = selector[2].checked;
+ let filteredData = [];
+
+ if (!domain && !hideactive && !hideObsolete) {
+ renderTable(data);
+ } else if (domain && !hideactive && !hideObsolete) {
+ renderTable(sortByField(data, "domain"), true);
+ } else if (domain && hideactive && !hideObsolete) {
+ filteredData = data.filter((x) => x["activity_status"] === "active");
+ renderTable(sortByField(filteredData, "domain"), true);
+ } else if (domain && !hideactive && hideObsolete) {
+ filteredData = data.filter((x) => x["is_obsolete"] !== true);
+ renderTable(sortByField(filteredData, "domain"), true);
+ } else if (domain && hideactive && hideObsolete) {
+ filteredData = data
+ .filter((x) => x["is_obsolete"] !== true)
+ .filter((x) => x["activity_status"] === "active");
+ renderTable(sortByField(filteredData, "domain"), true);
+ } else if (!domain && hideactive && hideObsolete) {
+ filteredData = data
+ .filter((x) => x["is_obsolete"] !== true)
+ .filter((x) => x["activity_status"] === "active");
+ renderTable(filteredData);
+ } else if (!domain && hideactive && !hideObsolete) {
+ filteredData = data["ontologies"].filter(
+ (x) => x["activity_status"] === "active",
+ );
+ renderTable(filteredData);
+ } else if (!domain && !hideactive && hideObsolete) {
+ filteredData = data.filter((x) => x["is_obsolete"] !== true);
+ renderTable(filteredData);
}
-
- /**
- * applies filters taking into consideration the state of
- * all the other filters and search text
- * @param {object} data
- */
- function apply_all_filters(data) {
- let selectedDomain = $("#dd-domains").children("option:selected").val();
- let res = data["ontologies"].filter(x => x["domain"] !== undefined);
- let dt = res.filter(x => x["domain"].includes(selectedDomain));
- let dt2 = Search($("#searchVal"), dt);
- applyFilters(dt2)
+ }
+
+ /**
+ * Search the given fields {domain, description, id} for input text
+ * @param {*} input input is a Jquery selector of the search box
+ * @param {object} JsonData Ontology data to search through
+ * @return {object} returns filtered json data
+ */
+ function Search(input, JsonData) {
+ let value = input.val().toLowerCase();
+ // check text length greater than 2 before doing search
+ if (value.length >= 2) {
+ return JsonData.filter((row) => {
+ let term = input.val().toLowerCase();
+ if (row.domain === undefined) {
+ row.domain = "";
+ }
+ if (row.description === undefined) {
+ row.description = "";
+ }
+ if (row.title === undefined) {
+ row.title = "";
+ }
+ return (
+ row.id.toLowerCase().includes(term) ||
+ row.domain.toLowerCase().includes(term) ||
+ row.title.toLowerCase().includes(term) ||
+ row.description.toLowerCase().includes(term)
+ );
+ });
}
-// obtain json data using fetch
- fetch('/registry/ontologies.jsonld')
- .then(response => response.json())
- .then((data) => {
-
- // create change observer to handle sorting when we have single and multiple tables based on domains
- let target = document.querySelector('#tableDiv')
- // Create an observer instance.
- let observer = new MutationObserver(function(mutations) {
- let sortableTables = document.querySelectorAll('table.sortable');
- for (let i = 0; i < sortableTables.length; i++) {
- new SortableTable(sortableTables[i]);
- }
- });
- // Pass in the target node, as well as the observer options.
- observer.observe(target, {
- attributes: true,
- childList: true,
- characterData: true
- });
-
- // extract domain and set values for dropdown menu
- let domains = [];
- for (let k = 0; k < data["ontologies"].length; k++) {
- if (data["ontologies"][k]["domain"] !== undefined) {
- let d = data["ontologies"][k]["domain"] //.replace(" and", ",").split(",")
- domains.push(d)
- }
- }
- domains = [...new Set(domains)];
- domains.sort();
- $("#dd-domains").append(` `);
- domains.forEach(function(r) {
- $("#dd-domains").append(`${r.trim()} `);
- })
- //render table on page load
- renderTable(data["ontologies"]);
-
- // check box filter event for table data
- $("[data-filter]").on("change", () => {
- apply_all_filters(data)
- });
- // get table by domain dropdown
- $("#dd-domains").on("change", () => {
- apply_all_filters(data)
-
- });
- // search word in table
- $("#searchVal").on("keyup", debounce((e) => {
- apply_all_filters(data)
- }));
-
- // dispatch change event on initial load to apply checkbox filters
- let element = document.querySelector('[data-filter]');
- let event = new Event('change');
- element.dispatchEvent(event);
- $('#table-main').css('display', 'block');
- }).catch(error => console.log(error));
+ return JsonData;
+ }
+
+ /**
+ * applies filters taking into consideration the state of
+ * all the other filters and search text
+ * @param {object} data
+ */
+ function apply_all_filters(data) {
+ let selectedDomain = $("#dd-domains").children("option:selected").val();
+ let res = data["ontologies"].filter((x) => x["domain"] !== undefined);
+ let dt = res.filter((x) => x["domain"].includes(selectedDomain));
+ let dt2 = Search($("#searchVal"), dt);
+ applyFilters(dt2);
+ }
+ // obtain json data using fetch
+ fetch("/registry/ontologies.jsonld")
+ .then((response) => response.json())
+ .then((data) => {
+ // create change observer to handle sorting when we have single and multiple tables based on domains
+ let target = document.querySelector("#tableDiv");
+ // Create an observer instance.
+ let observer = new MutationObserver(function (mutations) {
+ let sortableTables = document.querySelectorAll("table.sortable");
+ for (let i = 0; i < sortableTables.length; i++) {
+ new SortableTable(sortableTables[i]);
+ }
+ });
+ // Pass in the target node, as well as the observer options.
+ observer.observe(target, {
+ attributes: true,
+ childList: true,
+ characterData: true,
+ });
+
+ // extract domain and set values for dropdown menu
+ let domains = [];
+ for (let k = 0; k < data["ontologies"].length; k++) {
+ if (data["ontologies"][k]["domain"] !== undefined) {
+ let d = data["ontologies"][k]["domain"]; //.replace(" and", ",").split(",")
+ domains.push(d);
+ }
+ }
+ domains = [...new Set(domains)];
+ domains.sort();
+ $("#dd-domains").append(` `);
+ domains.forEach(function (r) {
+ $("#dd-domains").append(
+ `${r.trim()} `,
+ );
+ });
+ //render table on page load
+ renderTable(data["ontologies"]);
+
+ // check box filter event for table data
+ $("[data-filter]").on("change", () => {
+ apply_all_filters(data);
+ });
+ // get table by domain dropdown
+ $("#dd-domains").on("change", () => {
+ apply_all_filters(data);
+ });
+ // search word in table
+ $("#searchVal").on(
+ "keyup",
+ debounce((e) => {
+ apply_all_filters(data);
+ }),
+ );
+
+ // dispatch change event on initial load to apply checkbox filters
+ let element = document.querySelector("[data-filter]");
+ let event = new Event("change");
+ element.dispatchEvent(event);
+ $("#table-main").css("display", "block");
+ })
+ .catch((error) => console.log(error));
});
diff --git a/assets/js/sorttable.js b/assets/js/sorttable.js
index 7a5ba7bdd..098e295c3 100644
--- a/assets/js/sorttable.js
+++ b/assets/js/sorttable.js
@@ -8,8 +8,7 @@
* Docs: https://w3c.github.io/aria-practices/examples/table/sortable-table.html
*/
-
-'use strict';
+"use strict";
class SortableTable {
/**
* initialize sortable table. Add button in table header to use for initializing sortable column
@@ -17,10 +16,10 @@ class SortableTable {
*/
constructor(tableNode) {
this.tableNode = tableNode;
- this.selector = '.sort-button'
+ this.selector = ".sort-button";
//get all table headers
- this.columnHeaders = tableNode.querySelectorAll('thead th');
+ this.columnHeaders = tableNode.querySelectorAll("thead th");
this.sortColumns = [];
@@ -31,23 +30,23 @@ class SortableTable {
if (buttonNode) {
this.sortColumns.push(i);
//set custom attribute for tracking sortable columns column-index.
- buttonNode.setAttribute('data-column-index', i);
+ buttonNode.setAttribute("data-column-index", i);
// handle button click event on table column
- buttonNode.addEventListener('click', this.handleClick.bind(this));
+ buttonNode.addEventListener("click", this.handleClick.bind(this));
}
}
this.optionCheckbox = document.querySelector(
- 'input[type="checkbox"][value="show-unsorted-icon"]'
+ 'input[type="checkbox"][value="show-unsorted-icon"]',
);
if (this.optionCheckbox) {
this.optionCheckbox.addEventListener(
- 'change',
- this.handleOptionChange.bind(this)
+ "change",
+ this.handleOptionChange.bind(this),
);
if (this.optionCheckbox.checked) {
- this.tableNode.classList.add('show-unsorted-icon');
+ this.tableNode.classList.add("show-unsorted-icon");
}
}
}
@@ -57,7 +56,7 @@ class SortableTable {
* @param {string|number} columnIndex
*/
setColumnHeaderSort(columnIndex, chevron) {
- if (typeof columnIndex === 'string') {
+ if (typeof columnIndex === "string") {
columnIndex = parseInt(columnIndex);
}
@@ -66,33 +65,33 @@ class SortableTable {
var buttonNode = ch.querySelector(this.selector);
if (i === columnIndex) {
//get the sort order from aria-sort attribute
- var value = ch.getAttribute('aria-sort');
- if (value === 'descending') {
+ var value = ch.getAttribute("aria-sort");
+ if (value === "descending") {
//change sort order parameter
- ch.setAttribute('aria-sort', 'ascending');
+ ch.setAttribute("aria-sort", "ascending");
//sort column
this.sortColumn(
columnIndex,
- 'ascending',
- ch.classList.contains('num')
+ "ascending",
+ ch.classList.contains("num"),
);
// Point chevron in appropriate direction
- chevron.setAttribute('class', 'bi-chevron-up')
+ chevron.setAttribute("class", "bi-chevron-up");
} else {
//change sort order parameter
- ch.setAttribute('aria-sort', 'descending');
+ ch.setAttribute("aria-sort", "descending");
//sort column
this.sortColumn(
columnIndex,
- 'descending',
- ch.classList.contains('num')
+ "descending",
+ ch.classList.contains("num"),
);
// Point chevron in appropriate direction
- chevron.setAttribute('class', 'bi-chevron-down')
+ chevron.setAttribute("class", "bi-chevron-down");
}
} else {
- if (ch.hasAttribute('aria-sort') && buttonNode) {
- ch.removeAttribute('aria-sort');
+ if (ch.hasAttribute("aria-sort") && buttonNode) {
+ ch.removeAttribute("aria-sort");
}
}
}
@@ -106,7 +105,7 @@ class SortableTable {
*/
sortColumn(columnIndex, sortValue, isNumber) {
function compareValues(a, b) {
- if (sortValue === 'ascending') {
+ if (sortValue === "ascending") {
if (a.value === b.value) {
return 0;
} else {
@@ -129,11 +128,11 @@ class SortableTable {
}
}
- if (typeof isNumber !== 'boolean') {
+ if (typeof isNumber !== "boolean") {
isNumber = false;
}
- var tbodyNode = this.tableNode.querySelector('tbody');
+ var tbodyNode = this.tableNode.querySelector("tbody");
var rowNodes = [];
var dataCells = [];
@@ -143,7 +142,7 @@ class SortableTable {
//extract table data cells
while (rowNode) {
rowNodes.push(rowNode);
- var rowCells = rowNode.querySelectorAll('th, td');
+ var rowCells = rowNode.querySelectorAll("th, td");
var dataCell = rowCells[columnIndex];
var data = {};
@@ -176,8 +175,8 @@ class SortableTable {
var tgt = event.currentTarget;
//get the column to sort and pass index t
this.setColumnHeaderSort(
- tgt.getAttribute('data-column-index'),
- tgt.firstElementChild
+ tgt.getAttribute("data-column-index"),
+ tgt.firstElementChild,
);
}
@@ -185,9 +184,9 @@ class SortableTable {
var tgt = event.currentTarget;
if (tgt.checked) {
- this.tableNode.classList.add('show-unsorted-icon');
+ this.tableNode.classList.add("show-unsorted-icon");
} else {
- this.tableNode.classList.remove('show-unsorted-icon');
+ this.tableNode.classList.remove("show-unsorted-icon");
}
}
-}
\ No newline at end of file
+}
diff --git a/biome.json b/biome.json
new file mode 100644
index 000000000..a02aa0d93
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
+ "formatter": {
+ "indentStyle": "space"
+ },
+ "files": {
+ "includes": ["assets/**/*/*.js", "!assets/**/*/underscore.min.js"]
+ }
+}