diff --git a/public/assets/css/csvxml.css b/public/assets/css/csvxml.css index d32ffb1..4b44841 100644 --- a/public/assets/css/csvxml.css +++ b/public/assets/css/csvxml.css @@ -36,7 +36,7 @@ body { margin: 2em; background: var(--color-bg-normal); a { text-decoration: none; color: inherit; } -h1 { display: block; max-width: 600px; margin: 0 auto 1.5em auto; } +h1 { display: block; max-width: 600px; margin: 0 auto 1.5em auto; line-height: 1.2em; } h1 > * { display: inline-block; vertical-align: middle; color: var(--color-fg-less); } h1 img { height: 2em; margin-right: .5em; border-radius: .1em; opacity: .7; transition: opacity .4s; } h1 img:hover { opacity: 1; } @@ -106,13 +106,13 @@ ul.fieldList > li:hover { background: var(--color-bg-normal); border-color: var( ul.fieldList > li.requiredField:before { display: inline-block; content: " \002612 "; margin-right: .5em; } ul.fieldList > li.humanTLToggled { border-color: var(--color-accent-hover); background: var(--color-accent-hover); box-shadow: 0 8px 6px -6px black; } -.options > a.buttonLike { display: inline-block; width: auto; +.options > .buttonLike { display: inline-block; width: auto; margin: .15em 0; padding: .4em .5em; text-transform: inherit; cursor: pointer; background: initial; border: 2px solid var(--color-borders); opacity: 1; transition: background .4s, opacity .4s; } -.options > a.buttonLike:hover { background: var(--color-borders); } +.options > .buttonLike:hover { background: var(--color-borders); } .actionList { margin: 1em 0 1em 1em; padding: .5em 0; } .actionList > li a { display: inline-block; padding: .3em; border-radius: .3em; transition: background .4s; } @@ -135,6 +135,11 @@ ul.fieldList > li.humanTLToggled { border-color: var(--color-accent-hover); back to { transform: rotate(360deg); } } +@media screen and (max-width: 75em) { + body { font-size: 1.08em; } + h1 img { display: none; } +} + /* ============= | Dark mode |============ */ diff --git a/public/assets/css/csvxml.min.css b/public/assets/css/csvxml.min.css index 2500e95..c0eb457 100644 --- a/public/assets/css/csvxml.min.css +++ b/public/assets/css/csvxml.min.css @@ -2,7 +2,7 @@ @font-face{font-family:sourceSansPro;src:local('Source-Sans-Pro'),local('Source Sans Pro'),url(../fonts/SourceSansPro-Regular.woff2)format('woff2'),url(../fonts/SourceSansPro-Regular.ttf)format('truetype');font-display:swap}*{box-sizing:border-box;z-index:1} body{margin:2em;background:var(--color-bg-normal);font-family:sourceSansPro,Arial,Helvetica,Times;font-size:1.2em;line-height:1.5em} a{text-decoration:none;color:inherit} -h1{display:block;max-width:600px;margin:0 auto 1.5em auto} +h1{display:block;max-width:600px;margin:0 auto 1.5em auto;line-height:1.2em} h1>*{display:inline-block;vertical-align:middle;color:var(--color-fg-less)} h1 img{height:2em;margin-right:.5em;border-radius:.1em;opacity:.7;transition:opacity.4s} h1 img:hover{opacity:1} @@ -27,9 +27,11 @@ ul.fieldList{display:block;margin:.5em 0;padding:0 0;list-style:none} ul.fieldList>li{display:inline-block;padding:.3em;margin:.1em;border:1px solid var(--color-borders);background:var(--color-bg-raised2);cursor:pointer;transition:background.4s,border.4s,box-shadow.4s} ul.fieldList>li:hover{background:var(--color-bg-normal);border-color:var(--color-fg-less)} ul.fieldList>li.requiredField:before{display:inline-block;content:" \002612 ";margin-right:.5em} -ul.fieldList>li.humanTLToggled{border-color:var(--color-accent-hover);background:var(--color-accent-hover);box-shadow:0 8px 6px-6px black}.options>a.buttonLike{display:inline-block;width:auto;margin:.15em 0;padding:.4em.5em;text-transform:inherit;cursor:pointer;background:initial;border:2px solid var(--color-borders);opacity:1;transition:background.4s,opacity.4s}.options>a.buttonLike:hover{background:var(--color-borders)}.actionList{margin:1em 0 1em 1em;padding:.5em 0}.actionList>li a{display:inline-block;padding:.3em;border-radius:.3em;transition:background.4s}.actionList>li a:hover{background:var(--color-accent-hover)}.loading:before,.loading:after{content:" ";display:block;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);height:64px;width:64px;margin:0;padding:0;border-radius:50%;border:8px solid var(--color-accent-hover);border-color:var(--color-accent-hover)transparent transparent transparent;z-index:100;animation:rotating 1.2s cubic-bezier(0.5,0,0.5,1)infinite;animation-delay:-0.15s}.loading:after{animation-delay:-0.45s} +ul.fieldList>li.humanTLToggled{border-color:var(--color-accent-hover);background:var(--color-accent-hover);box-shadow:0 8px 6px-6px black}.options>.buttonLike{display:inline-block;width:auto;margin:.15em 0;padding:.4em.5em;text-transform:inherit;cursor:pointer;background:initial;border:2px solid var(--color-borders);opacity:1;transition:background.4s,opacity.4s}.options>.buttonLike:hover{background:var(--color-borders)}.actionList{margin:1em 0 1em 1em;padding:.5em 0}.actionList>li a{display:inline-block;padding:.3em;border-radius:.3em;transition:background.4s}.actionList>li a:hover{background:var(--color-accent-hover)}.loading:before,.loading:after{content:" ";display:block;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);height:64px;width:64px;margin:0;padding:0;border-radius:50%;border:8px solid var(--color-accent-hover);border-color:var(--color-accent-hover)transparent transparent transparent;z-index:100;animation:rotating 1.2s cubic-bezier(0.5,0,0.5,1)infinite;animation-delay:-0.15s}.loading:after{animation-delay:-0.45s} @keyframes rotating{from{transform:rotate(0deg)} to{transform:rotate(360deg)}} +@media screen and(max-width:75em){body{font-size:1.08em} +h1 img{display:none}} @media(prefers-color-scheme:dark){body{background:#263238;color:#ECEFF1} input,select,textarea{background:inherit;color:inherit;border:3px solid #37474F;transition:border.4s,box-shadow.4s} a.buttonLike:focus,.options>a.buttonLike:focus,button:focus,select:focus,textarea:focus,input:focus,textarea:active,input:active,a.buttonLike:hover,.options>a.buttonLike:hover,ul.fieldList>li:hover,button:hover,select:hover,textarea:hover,input:hover{background:inherit;border-color:#FFF;box-shadow:initial;border-radius:.2em} diff --git a/public/assets/img/mdlogo-csvxml-1028px.png b/public/assets/img/mdlogo-csvxml-1028px.png new file mode 100644 index 0000000..51ef993 Binary files /dev/null and b/public/assets/img/mdlogo-csvxml-1028px.png differ diff --git a/public/assets/img/mdlogo-csvxml-192px.png b/public/assets/img/mdlogo-csvxml-192px.png new file mode 100644 index 0000000..5ed3e0e Binary files /dev/null and b/public/assets/img/mdlogo-csvxml-192px.png differ diff --git a/public/assets/js/csvxmlV2.js b/public/assets/js/csvxmlV2.js index d82f53e..a1dab42 100644 --- a/public/assets/js/csvxmlV2.js +++ b/public/assets/js/csvxmlV2.js @@ -1,5 +1,10 @@ "use strict"; +if ('serviceWorker' in navigator) { + console.log("Registering service worker"); + navigator.serviceWorker.register('/sw.js'); +} + class CsvxmlValidator { fieldList; // {}{} @@ -625,6 +630,7 @@ class CsvxmlPage { const input = document.createElement("input"); input.type = "file"; input.id = "fileToUpload"; + input.setAttribute("tabindex", "1"); input.accept = ".csv"; input.required = "required"; input.addEventListener('change', async function() { @@ -703,8 +709,9 @@ class CsvxmlPage { function genButton(id, text, link = "") { - const output = document.createElement("a"); + const output = document.createElement("span"); output.id = id; + output.setAttribute("tabindex", "1"); output.textContent = text; output.classList.add("buttonLike"); if (link !== "") output.href = link; @@ -719,7 +726,8 @@ class CsvxmlPage { const dlAllButton = genButton("dlAll", this.tls.download_csv_all); dlAllButton.cursor = "pointer"; - dlAllButton.addEventListener('click', function() { + dlAllButton.addEventListener('click', function(e) { + e.preventDefault(); app.generateCsv(); }); options.appendChild(dlAllButton); @@ -739,6 +747,7 @@ class CsvxmlPage { options.appendChild(this.unsetSelectionButton); this.csvBySelectionButton.addEventListener('click', function(e) { + e.preventDefault(); let selected = document.getElementsByClassName("humanTLToggled"); let selectedFields = []; @@ -750,6 +759,7 @@ class CsvxmlPage { }); optionSelectRequired.addEventListener('click', function(e) { + e.preventDefault(); app.doForFieldList(function(field) { if (field.classList.contains("requiredField") === false) return; @@ -763,6 +773,7 @@ class CsvxmlPage { }); optionSelectAll.addEventListener('click', function(e) { + e.preventDefault(); app.doForFieldList(function(field) { if (field.classList.contains("humanTLToggled") === true) return; @@ -775,6 +786,7 @@ class CsvxmlPage { }); this.unsetSelectionButton.addEventListener('click', function(e) { + e.preventDefault(); app.doForFieldList(function(field) { if (field.classList.contains("humanTLToggled") === false) return; @@ -893,6 +905,7 @@ class CsvxmlPage { } const lang = getLang(); + document.documentElement.setAttribute("lang", lang); document.body.classList.add("loading"); diff --git a/public/assets/js/csvxmlV2.min.js b/public/assets/js/csvxmlV2.min.js index 480fb80..6ccae3d 100644 --- a/public/assets/js/csvxmlV2.min.js +++ b/public/assets/js/csvxmlV2.min.js @@ -1,4 +1,5 @@ -"use strict";class CsvxmlValidator{fieldList;toValidate;errors;constructor(fieldList,csvRaw){this.errors={parsing:[],mandatoryTags:[],duplicateInvNos:[],dependentColumns:[],controlledLists:[],mainImageResource:[],};this.fieldList=Object.freeze(fieldList);const lines=csvRaw.trim().replace("\r\n","\n").split("\n");let separator;let delimiter;if(csvRaw.substr(0,1)==='"'){separator='";"';delimiter='"'}else{separator=';';delimiter=''} +"use strict";if('serviceWorker' in navigator){console.log("Registering service worker");navigator.serviceWorker.register('/sw.js')} +class CsvxmlValidator{fieldList;toValidate;errors;constructor(fieldList,csvRaw){this.errors={parsing:[],mandatoryTags:[],duplicateInvNos:[],dependentColumns:[],controlledLists:[],mainImageResource:[],};this.fieldList=Object.freeze(fieldList);const lines=csvRaw.trim().replace("\r\n","\n").split("\n");let separator;let delimiter;if(csvRaw.substr(0,1)==='"'){separator='";"';delimiter='"'}else{separator=';';delimiter=''} let headersFields=lines.shift();let headers;if(delimiter==="")headers=headersFields.split(separator);else headers=headersFields.substr(1,headersFields.length-2).split(separator);let expectedFieldCount=headers.length;let toValidate=[];let lineCounter=1;for(let line of lines){if(delimiter!==''){line=line.substr(1,line.length-2)} if(line.length<=headers.length)continue;let lineContents={};let fields=line.split(separator);if(fields.length!==headers.length){this.errors.parsing.push("Number of columns in line "+lineCounter+" does not match number of headers")} for(let i=0,max=fields.length;i - - + + - - CSVXML :: museum-digital + + + + + + + + + + + + - + - + \ No newline at end of file diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..a061633 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,19 @@ +{ + "name": "csvxml :: museum-digital", + "short_name": "csvxml", + "start_url": "/", + "display": "standalone", + "background_color": "#F2F2F2", + "theme_color": "#aa4400", + "description": "Validate import CSV files for museum-digital.", + "icons": [{ + "src": "assets/img/mdlogo-csvxml-1028x.png", + "sizes": "1028x1028", + "type": "image/png" + }, { + "src": "assets/img/mdlogo-csvxml.svg", + "sizes": "48x48 72x72 96x96 128x128 256x256 512x512", + "type": "image/svg+xml", + "purpose": "any maskable" + }] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..4afe43e --- /dev/null +++ b/public/sw.js @@ -0,0 +1,88 @@ +const debug = false; +const CACHE_NAME = 'csvxml-cache-v1'; +const urlsToCache = [ + + '/index.htm', + '/assets/js/jszip/dist/jszip.min.js', + '/assets/js/csvxmlV2.min.js', + '/assets/css/csvxml.min.css', + + '/assets/fonts/SourceSansPro-Regular.woff2', + + '/json/fields.de.json', + '/json/fields.en.json', + '/json/fields.hu.json', + + '/json/tls.de.json', + '/json/tls.en.json', + '/json/tls.hu.json', + +]; + +self.addEventListener('install', function(event) { + // Perform install steps + event.waitUntil( + Promise.all([ + caches.open(CACHE_NAME) + .then(function(cache) { + if (debug === true) console.log('Opened cache'); + return cache.addAll(urlsToCache.map(url => new Request(url, {credentials: 'same-origin'}))); + }), + + ]) + ); +}); + +self.addEventListener('fetch', function(event) { + + if (event.request.url.startsWith(location.origin) === false) return; + + event.respondWith( + caches.match(event.request) + .then(function(response) { + + if (response) { + if (debug === true) console.log("Serving " + event.request.url + " from cache."); + return response; + } + + // IMPORTANT: Clone the request. A request is a stream and + // can only be consumed once. Since we are consuming this + // once by cache and once by the browser for fetch, we need + // to clone the response. + var fetchRequest = event.request.clone(); + + return fetch(fetchRequest).then( + function(response) { + // Check if we received a valid response + if(!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + + var responseToCache = response.clone(); + + caches.open(CACHE_NAME) + .then(function(cache) { + if (debug === true) console.log("Added " + event.request.url + "to cache"); + cache.put(event.request, responseToCache); + }); + + return response; + } + ).catch( + () => { + console.log("Problem"); + if (event.request.url.indexOf("index.php?") == -1) return; + else { + console.log("Fixing"); + return caches.open(CACHE_NAME).then( + function(cache) { + // cache.keys().then(function(keys) { console.log(keys); }) + // return cache.match("index.php") + }); + } + } + ); + }) + ); +}); diff --git a/scripts/compile.php b/scripts/compile.php index ff33126..b509349 100644 --- a/scripts/compile.php +++ b/scripts/compile.php @@ -70,25 +70,30 @@ function generateAppShell():string { - - + + - - CSVXML :: museum-digital + + + + + + + + + + + + -

- - museum-digital:csvxml -

- - + ';