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}
    @@ -144,171 +169,196 @@ jQuery(document).ready(function() {
    `; - 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 = ` GitHub Repo stars `; - } 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 = ` ${license_label} `; - } 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} - //
    - // - // OBO Dashboard badge for ${id} - // - //
    - // `; - 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} + //
    + // + // OBO Dashboard badge for ${id} + // + //
    + // `; + 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(``); - }) - //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( + ``, + ); + }); + //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"] + } +}