diff --git a/codemeta_generation.js b/codemeta_generation.js index 97aa898..c88ea1e 100644 --- a/codemeta_generation.js +++ b/codemeta_generation.js @@ -1,162 +1,180 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ "use strict"; const SPDX_PREFIX = 'https://spdx.org/licenses/'; function emptyToUndefined(v) { if (v == null || v == "") return undefined; else return v; } function getIfSet(query) { return emptyToUndefined(document.querySelector(query).value); } function setIfDefined(query, value) { if (value !== undefined) { document.querySelector(query).value = value; } } // Names of codemeta properties with a matching HTML field name const directCodemetaFields = [ 'codeRepository', 'contIntegration', 'dateCreated', 'datePublished', 'dateModified', 'issueTracker', 'name', 'version', + 'identifier', + 'description', + 'applicationCategory', + //keywords TODO:keywords array + 'releaseNotes', + 'funding', + 'runtimePlatform', + //softwareRequiremnts + 'developmentStatus', + //relatedLink + 'programmingLanguage', + 'isPartOf', + //'referencePublication' + // "@type": "ScholarlyArticle", + // "idendifier": "https://doi.org/xx.xxxx/xxxx.xxxx.xxxx", + // "name": "title of publication" + ]; // Names of codemeta properties with a matching HTML field name, // in a Person object const directPersonCodemetaFields = [ 'givenName', 'familyName', 'email', + 'affiliation' ]; function generatePerson(idPrefix) { var doc = { "@type": "Person", "@id": getIfSet(`#${idPrefix}_id`), } directPersonCodemetaFields.forEach(function (item, index) { doc[item] = getIfSet(`#${idPrefix}_${item}`); }); return doc; } function generatePersons(prefix) { var persons = []; var nbPersons = getNbPersons(prefix); for (let personId = 1; personId <= nbPersons; personId++) { persons.push(generatePerson(`${prefix}_${personId}`)); } return persons; } function generateCodemeta() { var inputForm = document.querySelector('#inputForm'); var codemetaText, errorHTML; if (inputForm.checkValidity()) { var doc = { "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "license": SPDX_PREFIX + getIfSet('#license'), }; // Generate most fields directCodemetaFields.forEach(function (item, index) { doc[item] = getIfSet('#' + item) }); // Generate dynamic fields doc = Object.assign(doc, { "author": generatePersons('author'), "contributor": generatePersons('contributor'), }); codemetaText = JSON.stringify(doc, null, 4); errorHTML = ""; } else { codemetaText = ""; errorHTML = "invalid input (see error above)"; inputForm.reportValidity(); } document.querySelector('#codemetaText').innerText = codemetaText; setError(errorHTML); if (codemetaText) { // For restoring the form state on page reload sessionStorage.setItem('codemetaText', codemetaText); } } function importPersons(prefix, legend, docs) { if (docs === 'undefined') { return; } docs.forEach(function (doc, index) { var personId = addPerson(prefix, legend); setIfDefined(`#${personId}_id`, doc['@id']); directPersonCodemetaFields.forEach(function (item, index) { setIfDefined(`#${prefix}_${personId}_${item}`, doc[item]); }); }) } function importCodemeta() { var inputForm = document.querySelector('#inputForm'); var codemetaText = document.querySelector('#codemetaText').innerText; var doc; try { doc = JSON.parse(codemetaText); } catch (e) { setError(`Could not read codemeta document because it is not valid JSON (${e})`); return; } resetForm(); if (doc['license'] !== undefined && doc['license'].indexOf(SPDX_PREFIX) == 0) { var license = doc['license'].substring(SPDX_PREFIX.length); document.querySelector('#license').value = license; } directCodemetaFields.forEach(function (item, index) { setIfDefined('#' + item, doc[item]); }); importPersons('author', 'Author', doc['author']) importPersons('contributor', 'Contributor', doc['contributor']) setError(""); } function loadStateFromStorage() { codemetaText = sessionStorage.getItem('codemetaText') if (codemetaText) { document.querySelector('#codemetaText').innerText = codemetaText; importCodemeta(); } } diff --git a/dynamic_form.js b/dynamic_form.js index 39be4c3..a62ad96 100644 --- a/dynamic_form.js +++ b/dynamic_form.js @@ -1,115 +1,120 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ "use strict"; function createPersonFieldset(personPrefix, legend) { // Creates a fieldset containing inputs for informations about a person var fieldset = document.createElement("fieldset") fieldset.classList.add("person"); fieldset.id = personPrefix; fieldset.innerHTML = ` <legend>${legend}</legend> <p> <label for="${personPrefix}_givenName">Given name</label> <input type="text" id="${personPrefix}_givenName" name="${personPrefix}_givenName" placeholder="Jane" required="true" /> </p> <p> <label for="${personPrefix}_familyName">Family name</label> <input type="text" id="${personPrefix}_familyName" name="${personPrefix}_familyName" placeholder="Doe" /> </p> <p> <label for="${personPrefix}_email">E-mail address</label> <input type="email" id="${personPrefix}_email" name="${personPrefix}_email" placeholder="jane.doe@example.org" /> </p> <p> <label for="${personPrefix}_id">URI</label> <input type="url" id="${personPrefix}_id" name="${personPrefix}_id" placeholder="http://orcid.org/0000-0002-1825-0097" /> </p> + <p> + <label for="${personPrefix}_affiliation">Affiliation</label> + <input type="url" id="${personPrefix}_affiliation" name="${personPrefix}_affiliation" + placeholder="Department of Computer Science, University of Pisa" /> + </p> `; return fieldset; } function addPersonWithId(container, prefix, legend, id) { var fieldset = createPersonFieldset(`${prefix}_${id}`, `${legend} #${id}`); container.appendChild(fieldset); } function addPerson(prefix, legend) { var container = document.querySelector(`#${prefix}_container`); var personId = getNbPersons(prefix) + 1; addPersonWithId(container, prefix, legend, personId); setNbPersons(prefix, personId); return personId; } function removePerson(prefix) { var personId = getNbPersons(prefix); document.querySelector(`#${prefix}_${personId}`).remove(); setNbPersons(prefix, personId-1); } // Initialize a group of persons (authors, contributors) on page load. // Useful if the page is reloaded. function initPersons(prefix, legend) { var nbPersons = getNbPersons(prefix); var personContainer = document.querySelector(`#${prefix}_container`) for (let personId = 1; personId <= nbPersons; personId++) { addPersonWithId(personContainer, prefix, legend, personId); } } function removePersons(prefix) { var nbPersons = getNbPersons(prefix); var personContainer = document.querySelector(`#${prefix}_container`) for (let personId = 1; personId <= nbPersons; personId++) { removePerson(prefix) } } function resetForm() { removePersons('author'); removePersons('contributor'); // Reset the form after deleting elements, so nbPersons doesn't get // reset before it's read. document.querySelector('#inputForm').reset(); } function initCallbacks() { document.querySelector('#license') .addEventListener('change', validateLicense); document.querySelector('#generateCodemeta') .addEventListener('click', generateCodemeta); document.querySelector('#resetForm') .addEventListener('click', resetForm); document.querySelector('#importCodemeta') .addEventListener('click', importCodemeta); document.querySelector('#inputForm') .addEventListener('change', generateCodemeta); initPersons('author', 'Author'); initPersons('contributor', 'Contributor'); } diff --git a/index.html b/index.html index f3e910e..14caa29 100644 --- a/index.html +++ b/index.html @@ -1,135 +1,208 @@ <!doctype html> <!-- Copyright (C) 2019 The Software Heritage developers See the AUTHORS file at the top-level directory of this distribution License: GNU Affero General Public License version 3, or any later version See top-level LICENSE file for more information --> <html lang="en"> <head> <meta charset="utf-8"> <title>CodeMeta generator</title> <script src="./utils.js"></script> <script src="./fields_data.js"></script> <script src="./dynamic_form.js"></script> <script src="./codemeta_generation.js"></script> <link rel="stylesheet" type="text/css" href="./style.css"> </head> <body> <h1>CodeMeta generator</h1> <p>Most fields are optional. Mandatory fields will be highlighted when generating Codemeta.</p> <form id="inputForm"> <fieldset id="fieldsetSoftwareItself"> <legend>The software itself</legend> <p title="The name of the software"> <label for="name">Name</label> <input type="text" name="name" id="name" placeholder="My Software" required="true" /> </p> + + <p title="A unique identifier"> + <label for="identifier">Unique identifier</label> + <input type="text" name="identifier" id="identifier" + placeholder="?????" /> + </p> <!-- TODO:define better--> + + <p title="Type of the software application"> + <label for="applicationCategory">Application category</label> + <input type="text" name="applicationCategory" id="applicationCategory" + placeholder="Astronomy" /> + </p> + + <p title="a brief description of the software"> + <label for="description">Description</label> + <br> + <textarea rows="4" cols="50" + name="description" id="description" + placeholder="My Software computes ephemerides and orbit propagation. It has been developed from early ยด80." ></textarea> + </p> + + <p title="The date on which the software was created."> <label for="dateCreated">Creation date</label> <input type="text" name="dateCreated" id="dateCreated" placeholder="YYYY-MM-DD" pattern="\d{4}-\d{2}-\d{2}" /> </p> <p title="Date of first publication."> <label for="datePublished">First release date</label> <input type="text" name="datePublished" id="datePublished" placeholder="YYYY-MM-DD" pattern="\d{4}-\d{2}-\d{2}" /> </p> <p> <label for="license">License</label> <input list="licenses" name="license" id="license"> <datalist id="licenses"> </datalist> </p> </fieldset> + <fieldset id="fieldsetDevelopmentCommunity"> + <legend>Additional Info</legend> + <p title="Funding"> + <label for="funding">Funding</label> + <input type="text" name="funding" id="funding" + placeholder="software funded by...(e.g. specific grant)" /> + </p> + + <p title="Runtime Platform"> + <label for="runtimePlatform">Runtime Platform</label> + <input type="text" name="runtimePlatform" id="runtimePlatform" + placeholder="Operating system" /> + </p> + + <p title="Development Status"> + <label for="developmentStatus">Development Status</label> + <input type="text" name="developmentStatus" id="developmentStatus" + placeholder="Concept/WIP/Suspended/Abandoned/Active/Inactive/Unsupported/Moved - (www.repostatus.org)" /> + </p> + + <p title="Programming Language"> + <label for="programmingLanguage">Programming Language</label> + <input type="text" name="programmingLanguage" id="programmingLanguage" + placeholder="C#" /> + </p> + + <p title="Part of"> + <label for="isPartOf">Funding</label> + <input type="text" name="isPartOf" id="isPartOf" + placeholder="part of ... project" /> + </p> + </fieldset> + <fieldset id="fieldsetDevelopmentCommunity"> <legend>Development community / tools</legend> <p title="Link to the repository where the un-compiled, human readable code and related code is located (SVN, Git, GitHub, CodePlex, institutional GitLab instance, etc.)."> <label for="codeRepository">Code repository</label> <input type="URL" name="codeRepository" id="codeRepository" placeholder="git+https://github.com/You/RepoName.git" /> </p> <p title="Link to continuous integration service (Travis-CI, Gitlab CI, etc.)."> <label for="contIntegration">Continuous integration</label> <input type="URL" name="contIntegration" id="contIntegration" placeholder="https://travis-ci.org/You/RepoName" /> </p> <p title="Link to a place for users/developpers to report and manage bugs (JIRA, GitHub issues, etc.)."> <label for="issueTracker">Issue tracker</label> <input type="URL" name="issueTracker" id="issueTracker" placeholder="https://github.com/You/RepoName/issues" /> </p> </fieldset> <fieldset id="fieldsetCurrentVersion"> <legend>Current version of the software</legend> <p title="Version number of the software"> <label for="version">Version number</label> <input type="text" name="version" id="version" placeholder="1.0.0" /> </p> <p title="The date on which the software was most recently modified."> <label for="dateModified">Release date</label> <input type="text" name="dateModified" id="dateModified" placeholder="YYYY-MM-DD" pattern="\d{4}-\d{2}-\d{2}" /> </p> + + <p title="a brief description of the software"> + <label for="releaseNotes">Release notes</label> + <br> + <textarea rows="4" cols="50" + name="releaseNotes" id="releaseNotes" + placeholder= +"Change log: this and that; +Bugfixes: that and this." ></textarea> + </p> +<!--TODO: keywords as a string array --> + +<!--TODO: software requirements as string array --> + +<!--TODO: related links as URL array --> + +<!--TODO: referencePublication as ScholarlyArticle array --> + </fieldset> <fieldset class="persons" id="author_container"> <legend>Authors</legend> <p>Order of authors does not matter.</p> <input type="hidden" id="author_nb" value="0" /> <div id="addRemoveAuthor"> <input type="button" id="author_add" value="Add one" onclick="addPerson('author', 'Author');" /> <input type="button" id="author_remove" value="Remove last" onclick="removePerson('author');" /> </div> </fieldset> <fieldset class="persons" id="contributor_container"> <legend>Contributors</legend> <p>Order of contributors does not matter.</p> <input type="hidden" id="contributor_nb" value="0" /> <div id="addRemoveContributor"> <input type="button" id="contributor_add" value="Add one" onclick="addPerson('contributor', 'Contributor');" /> <input type="button" id="contributor_remove" value="Remove last" onclick="removePerson('contributor');" /> </div> </fieldset> </form> <form> <input type="button" id="generateCodemeta" value="Generate Codemeta" /> <input type="button" id="resetForm" value="Reset form" /> <input type="button" id="importCodemeta" value="Import Codemeta" /> </form> <p id="errorMessage"> </p> <pre contentEditable="true" id="codemetaText"></pre> <script> initFieldsData(); initCallbacks(); loadStateFromStorage(); </script> </body> </html> diff --git a/style.css b/style.css index bd4f8c8..67e7e65 100644 --- a/style.css +++ b/style.css @@ -1,34 +1,35 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ + .person { display: inline-block; } #fieldsetSoftwareItself, #fieldsetDevelopmentCommunity, #fieldsetCurrentVersion { display: inline-block; } #license { /* License names are long */ min-width: 20em; } input[type=URL] { /* URLs are longer than the other fields */ min-width: 20em; } #codemetaText { width: 100%; min-height: 10em; border: 1px solid black; } #errorMessage { color: red; }