diff --git a/index.html b/index.html
index 78f3e08..47e1d58 100644
--- a/index.html
+++ b/index.html
@@ -1,361 +1,363 @@
 <!doctype html>
 <!--
 Copyright (C) 2019-2020  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="./js/utils.js"></script>
     <script src="./js/fields_data.js"></script>
     <script src="./js/dynamic_form.js"></script>
     <script src="./js/codemeta_generation.js"></script>
     <script src="./js/validation/primitives.js"></script>
     <script src="./js/validation/things.js"></script>
     <script src="./js/validation/index.js"></script>
     <link rel="stylesheet" type="text/css" href="./main.css">
     <link rel="stylesheet" type="text/css" href="./codemeta.css">
 </head>
 <body>
   <header>
     <h1>CodeMeta generator</h1>
   </header>
 
   <main>
 
     <p>Most fields are optional. Mandatory fields will be highlighted when generating Codemeta.</p>
 
     <noscript>
         <p id="noscriptError">
             This application requires Javascript to show dynamic fields in the form,
             and generate a JSON file; but your browser does not support Javascript.
             If you cannot use a browser with Javascript support, you can try
             <a href="https://codemeta.github.io/tools/">one of the other available tools</a>
             or write the codemeta.json file directly.
         </p>
     </noscript>
 
     <form id="inputForm">
         <fieldset id="fieldsetSoftwareItself" class="leafFieldset">
             <legend>The software itself</legend>
 
             <p title="The name of the software">
                 <label for="name">Name</label>
                 <input type="text" name="name" id="name" aria-describedby="name_descr"
                     placeholder="My Software" required="required" />
 
                 <span class="field-description" id="name_descr">the software title</span>
             </p>
 
             <p title="a brief description of the software">
                 <label for="description">Description</label>
                 <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(s)</label>
                 <input list="licenses" name="license" id="license"
                     aria-describedby="licenses_descr"> <!-- TODO: insert placeholder -->
 
                 <datalist id="licenses">
                 </datalist>
                 <!-- This datalist is be filled automatically -->
 
                 <br />
                 <span class="field-description" id="licenses_descr">from <a href="https://spdx.org/license-list">SPDX licence list</a></span>
 
                 <div id="selected-licenses">
                     <!-- This div is to be filled as the user selects licenses -->
                 </div>
             </p>
         </fieldset>
 
         <fieldset id="fieldsetDiscoverabilityAndCitation" class="leafFieldset">
             <legend>Discoverability and citation</legend>
 
             <p title="Unique identifier">
                 <label for="identifier">Unique identifier</label>
                 <input type="text" name="identifier" id="identifier"
                     placeholder="10.151.xxxxx" aria-describedby="identifier_descr" />
 
                 <br />
                 <span class="field-description" id="identifier_descr">
                     such as ISBNs, GTIN codes, UUIDs etc..  <a href="http://schema.org/identifier">http://schema.org/identifier</a>
                 </span>
             </p>
             <!-- TODO:define better
 
             I looked at the schema.org definition of identifier (https://schema.org/identifier),
             it can be text, url or PropertyValue.
             Used as follows in data representation with microdata:
 
             <div property="identifier" typeof="PropertyValue">
             <span property="propertyID">DOI</span>:
             <span property="value">10.151.xxxxx</span>
             </div>
             we can use that with identifier-type and identifier-value to have a clearer idea
             of what needs to be in the input.
 
             -->
 
             <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="Comma-separated list of keywords">
                 <label for="keywords">Keywords</label>
                 <input type="text" name="keywords" id="keywords"
                     placeholder="ephemerides, orbit, astronomy"  />
             </p>
 
             <p title="Funding / grant">
                 <label for="funding">Funding</label>
                 <input type="text" name="funding" id="funding" aria-describedby="funding_descr"
                     placeholder="PRA_2018_73"/>
 
                 <br />
                 <span class="field-description" id="funding_descr">grant funding software development</span>
             </p>
 
             <p title="Funding / organization">
                 <label for="funder">Funder</label>
                 <input type="text" name="funder" id="funder" aria-describedby="funder_descr"
                     placeholder="Università di Pisa"/>
 
                 <br />
                 <span class="field-description" id="funder_descr">organization funding software development</span>
             </p>
 
             Authors and contributors can be added below
         </fieldset>
 
         <fieldset id="fieldsetDevelopmentCommunity" class="leafFieldset">
             <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>
 
             <p title="Related document, software, tools">
                 <label for="relatedLink">Related links</label>
                 <br />
                 <textarea rows="4" cols="50"
                     name="relatedLink" id="relatedLink"></textarea>
         </fieldset>
 
         <fieldset id="fieldsetRuntime" class="leafFieldset">
             <legend>Run-time environment</legend>
 
             <p title="Programming Languages, separated by commas">
                 <label for="programmingLanguage">Programming Language</label>
                 <input type="text" name="programmingLanguage" id="programmingLanguage"
                     placeholder="C#, Java, Python 3"  />
             </p>
 
             <p title="Runtime Platforms, separated by commas">
                 <label for="runtimePlatform">Runtime Platform</label>
                 <input type="text" name="runtimePlatform" id="runtimePlatform"
                     placeholder=".NET, JVM" />
             </p>
 
             <p title="Operating Systems, separated by commas">
                 <label for="operatingSystem">Operating System</label>
                 <input type="text" name="operatingSystem" id="operatingSystem"
                     placeholder="Android 1.6, Linux, Windows, macOS" />
             </p>
 
             <p title="Required software to run/use this one.">
                 <label for="softwareRequirements">Other software requirements</label>
                 <br />
                 <textarea rows="4" cols="50"
                     name="softwareRequirements" id="softwareRequirements"
                     placeholder=
 "Python 3.4
 https://github.com/psf/requests"></textarea>
         </fieldset>
 
         <fieldset id="fieldsetCurrentVersion" class="leafFieldset">
             <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="Download link">
                 <label for="downloadUrl">Download URL</label>
                 <input type="URL" name="downloadUrl" id="downloadUrl"
                     placeholder="https://example.org/MySoftware.tar.gz" />
             </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: referencePublication as ScholarlyArticle array -->
 
         </fieldset>
 
         <fieldset id="fieldsetAdditionalInfo" class="leafFieldset">
             <legend>Additional Info</legend>
 
             <p title="Scholarly article describing this software">
                 <label for="referencePublication">Reference Publication</label>
                 <input type="URL" name="referencePublication" id="referencePublication"
                     placeholder="https://doi.org/10.1000/xyz123" />
             </p>
 
             <p title="Development Status">
             <label for="developmentStatus">Development Status</label>
                 <datalist id="developmentStatuses">
                         <option value="concept">
                         <option value="wip">
                         <option value="suspended">
                         <option value="abandoned">
                         <option value="active">
                         <option value="inactive">
                         <option value="unsupported">
                         <option value="moved">
                 </datalist>
                 <input list="developmentStatuses" id="developmentStatus" aria-describedby="developmentStatuses_descr"
                 pattern="concept|wip|suspended|abandoned|active|inactive|unsupported|moved">
 
                 <br />
                 <span class="field-description" id="developmentStatuses_descr">
                     see <a href="http://www.repostatus.org">www.repostatus.org</a> for details
                 </span>
             </p>
 
             <p title="Part of">
                 <label for="isPartOf">Is part of</label>
                 <input type="URL" name="isPartOf" id="isPartOf"
                     placeholder="http://The.Bigger.Framework.org"  />
             </p>
         </fieldset>
 
         <div class="dynamicFields">
             <fieldset class="persons" id="author_container">
                 <legend>Authors</legend>
 
                 <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>
         </div>
 
     </form>
     <form>
         <input type="button" id="generateCodemeta" value="Generate codemeta.json"
             title="Creates a codemeta.json file below, from the information provided above." />
         <input type="button" id="resetForm" value="Reset form"
             title="Erases all fields." />
         <input type="button" id="validateCodemeta" value="Validate codemeta.json"
             title="Checks the codemeta.json file below is valid, and displays errors." />
         <input type="button" id="importCodemeta" value="Import codemeta.json"
             title="Fills the fields above based on the codemeta.json file below." />
     </form>
 
     <p id="errorMessage">
     </p>
     <p>codemeta.json:</p>
     <pre contentEditable="true" id="codemetaText"></pre>
   </main>
 
   <footer>
     <p style="text-align:center;">
         Do you want to improve this tool ?
         Check out the
         <a href="https://github.com/codemeta/codemeta-generator">
             CodeMeta-generator repository</a>
         <br />
         Join the
         <a href="https://github.com/codemeta/codemeta">CodeMeta community</a>
         discussion
         <br />
         The CodeMeta vocabulary -
         <a href="https://doi.org/10.5063/schema/codemeta-2.0">v2.0</a>
     </p>
 
     <h2 style="text-align:right;">Contributed by</h2>
     <p style="text-align:right;">
         <a href="https://www.softwareheritage.org/save-and-reference-research-software/">
             <img alt="Software Heritage" src="https://annex.softwareheritage.org/public/logo/software-heritage-logo-title-motto.svg"
                 width="300">
         </a>
     </p>
   </footer>
 
   <script src="./js/libs/jsonld/jsonld.min.js"></script>
   <script>
-    initJsonldLoader();
     initFieldsData();
-    initCallbacks();
-    loadStateFromStorage();
+    initContext().then(contexts => {
+      initJsonldLoader(contexts);
+      initCallbacks();
+      loadStateFromStorage();
+    });
   </script>
 </body>
 </html>
diff --git a/js/codemeta_generation.js b/js/codemeta_generation.js
index f3e1ff2..074053f 100644
--- a/js/codemeta_generation.js
+++ b/js/codemeta_generation.js
@@ -1,285 +1,296 @@
 /**
  * Copyright (C) 2019-2020  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 CODEMETA_CONTEXT_URL = 'https://doi.org/10.5063/schema/codemeta-2.0';
-const CONTEXTS = {
-    [CODEMETA_CONTEXT_URL]: `${window.location.href}/../data/contexts/codemeta-2.0.jsonld` // Relative to index.html
-}
-
 const SPDX_PREFIX = 'https://spdx.org/licenses/';
 
-const jsonldCustomLoader = url => {
-    const xhrDocumentLoader = jsonld.documentLoaders.xhr();
-    if (url in CONTEXTS) {
-        return xhrDocumentLoader(CONTEXTS[url]);
+const initContext = async () => {
+    const contextResponse = await fetch("../data/contexts/codemeta-2.0.jsonld");
+    const context = await contextResponse.json();
+    return {
+        [CODEMETA_CONTEXT_URL]: context
+    }
+}
+
+const getJsonldCustomLoader = contexts => {
+    return url => {
+        const xhrDocumentLoader = jsonld.documentLoaders.xhr();
+        if (url in contexts) {
+            console.log(contexts);
+            return {
+                contextUrl: null,
+                document: contexts[url],
+                documentUrl: url
+            };
+        }
+        return xhrDocumentLoader(url);
     }
-    return xhrDocumentLoader(url);
 };
 
-const initJsonldLoader = () => {
-    jsonld.documentLoader = jsonldCustomLoader;
-}
+const initJsonldLoader = contexts => {
+    jsonld.documentLoader = getJsonldCustomLoader(contexts);
+};
 
 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;
     }
 }
 
 function getLicenses() {
     let selectedLicenses = Array.from(document.getElementById("selected-licenses").children);
     return selectedLicenses.map(licenseDiv => SPDX_PREFIX + licenseDiv.children[0].innerText);
 }
 
 // Names of codemeta properties with a matching HTML field name
 const directCodemetaFields = [
     'codeRepository',
     'contIntegration',
     'dateCreated',
     'datePublished',
     'dateModified',
     'downloadUrl',
     'issueTracker',
     'name',
     'version',
     'identifier',
     'description',
     'applicationCategory',
     'releaseNotes',
     'funding',
     'developmentStatus',
     'isPartOf',
     'referencePublication'
 ];
 
 const splittedCodemetaFields = [
     ['keywords', ','],
     ['programmingLanguage', ','],
     ['runtimePlatform', ','],
     ['operatingSystem', ','],
     ['softwareRequirements', '\n'],
     ['relatedLink', '\n'],
 ]
 
 // Names of codemeta properties with a matching HTML field name,
 // in a Person object
 const directPersonCodemetaFields = [
     'givenName',
     'familyName',
     'email',
     'affiliation',
 ];
 
 function generateShortOrg(fieldName) {
     var affiliation = getIfSet(fieldName);
     if (affiliation !== undefined) {
         if (isUrl(affiliation)) {
             return {
                 "@type": "Organization",
                 "@id": affiliation,
             };
         }
         else {
             return {
                 "@type": "Organization",
                 "name": affiliation,
             };
         }
     }
     else {
         return undefined;
     }
 }
 
 function generatePerson(idPrefix) {
     var doc = {
         "@type": "Person",
     }
     var id = getIfSet(`#${idPrefix}_id`);
     if (id !== undefined) {
         doc["@id"] = id;
     }
     directPersonCodemetaFields.forEach(function (item, index) {
         doc[item] = getIfSet(`#${idPrefix}_${item}`);
     });
     doc["affiliation"] = generateShortOrg(`#${idPrefix}_affiliation`)
 
     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 buildDoc() {
     var doc = {
         "@context": CODEMETA_CONTEXT_URL,
         "@type": "SoftwareSourceCode",
     };
 
     let licenses = getLicenses();
     if (licenses.length > 0) {
         doc["license"] = licenses;
     }
 
     // Generate most fields
     directCodemetaFields.forEach(function (item, index) {
         doc[item] = getIfSet('#' + item)
     });
 
     doc["funder"] = generateShortOrg('#funder', doc["affiliation"]);
 
     // Generate simple fields parsed simply by splitting
     splittedCodemetaFields.forEach(function (item, index) {
         const id = item[0];
         const separator = item[1];
         const value = getIfSet('#' + id);
         if (value !== undefined) {
             doc[id] = value.split(separator).map(trimSpaces);
         }
     });
 
     // Generate dynamic fields
     var authors = generatePersons('author');
     if (authors.length > 0) {
         doc["author"] = authors;
     }
     var contributors = generatePersons('contributor');
     if (contributors.length > 0) {
         doc["contributor"] = contributors;
     }
     return doc;
 }
 
 async function generateCodemeta() {
     var inputForm = document.querySelector('#inputForm');
     var codemetaText, errorHTML;
 
     if (inputForm.checkValidity()) {
         var doc = buildDoc();
         const expanded = await jsonld.expand(doc);
         const compacted = await jsonld.compact(expanded, CODEMETA_CONTEXT_URL);
         codemetaText = JSON.stringify(compacted, null, 4);
         errorHTML = "";
     }
     else {
         codemetaText = "";
         errorHTML = "invalid input (see error above)";
         inputForm.reportValidity();
     }
 
     document.querySelector('#codemetaText').innerText = codemetaText;
     setError(errorHTML);
 
 
     // Run validator on the exported value, for extra validation.
     // If this finds a validation, it means there is a bug in our code (either
     // generation or validation), and the generation MUST NOT generate an
     // invalid codemeta file, regardless of user input.
     if (codemetaText && !validateDocument(JSON.parse(codemetaText))) {
         alert('Bug detected! The data you wrote is correct; but for some reason, it seems we generated an invalid codemeta.json. Please report this bug at https://github.com/codemeta/codemeta-generator/issues/new and copy-paste the generated codemeta.json file. Thanks!');
     }
 
     if (codemetaText) {
         // For restoring the form state on page reload
         sessionStorage.setItem('codemetaText', codemetaText);
     }
 }
 
 // Imports a single field (name or @id) from an Organization.
 function importShortOrg(fieldName, doc) {
     if (doc !== undefined) {
         // Use @id if set, else use name
         setIfDefined(fieldName, doc["name"]);
         setIfDefined(fieldName, doc["@id"]);
     }
 }
 
 function importPersons(prefix, legend, docs) {
     if (docs === undefined) {
         return;
     }
 
     docs.forEach(function (doc, index) {
         var personId = addPerson(prefix, legend);
 
         setIfDefined(`#${prefix}_${personId}_id`, doc['@id']);
         directPersonCodemetaFields.forEach(function (item, index) {
             setIfDefined(`#${prefix}_${personId}_${item}`, doc[item]);
         });
 
         importShortOrg(`#${prefix}_${personId}_affiliation`, doc['affiliation'])
     })
 }
 
 function importCodemeta() {
     var inputForm = document.querySelector('#inputForm');
     var doc = parseAndValidateCodemeta(false);
     resetForm();
 
     if (doc['license'] !== undefined) {
         if (typeof doc['license'] === 'string') {
             doc['license'] = [doc['license']];
         }
 
         doc['license'].forEach(l => {
             if (l.indexOf(SPDX_PREFIX) !== 0) { return; }
             let licenseId = l.substring(SPDX_PREFIX.length);
             insertLicenseElement(licenseId);
         });
     }
 
     directCodemetaFields.forEach(function (item, index) {
         setIfDefined('#' + item, doc[item]);
     });
     importShortOrg('#funder', doc["funder"]);
 
     // Import simple fields by joining on their separator
     splittedCodemetaFields.forEach(function (item, index) {
         const id = item[0];
         const separator = item[1];
         let value = doc[id];
         if (value !== undefined) {
             if (Array.isArray(value)) {
                 value = value.join(separator);
             }
             setIfDefined('#' + id, value);
         }
     });
 
     importPersons('author', 'Author', doc['author'])
     importPersons('contributor', 'Contributor', doc['contributor'])
 }
 
 function loadStateFromStorage() {
     var codemetaText = sessionStorage.getItem('codemetaText')
     if (codemetaText) {
         document.querySelector('#codemetaText').innerText = codemetaText;
         importCodemeta();
     }
 }