diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md index 2e7dfae..6e05e80 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Concrete Label +# Collecting Labels -## A tool for annotating concrete and visual poetry (as well as picture, images, etc) +## A tool forked from [Concrete ๐ŸŽ Label](https://git.xpub.nl/kamo/concrete-label) for annotating visual things in a collective way -Working with NLTK for the analysis of text we noticed that the natural language toolkit lacks of something when it comes to the very materiality of the text: it ignores the layout, the shapes, the spaces as well all the other graphical information that a script may contains. +This starts from the __Selection Process / Filter / Interface__ sub group for the SP16 at XPUB 2021/2022. The group is: Jian Kimberley Supi Kamo. -Hence this is a little tool to annotate visual contents in order to be then processed by tools as NLTK +On the [wiki page](https://pzwiki.wdka.nl/mediadesign/Selection_Process_/_Filter_/_Interface) there are more infos. This is a first test of crowdsourcing annotate an image, to collect different inputs as well as different point of views. \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..219ca8f --- /dev/null +++ b/index.js @@ -0,0 +1,68 @@ +// Dependencies declaration & setup + +// fs is for working with files, aka the JSON file we want to use to store our labels data +const fs = require('fs'); + +// express is for setting up the server and make it works when users do their things in the browser +const express = require('express'); +const app = express(); +const path = require('path'); + +// the port is setted in the etc/nginx/sites-enabled under the location /collecting-labels +const port = 3124; + +// express middleware for working with JSON between the server (this) and the client (all the things in the public folder) +app.use(express.urlencoded({ extended: true })); +app.use(express.json()); + +// serves the contents of the public folder as static resources +// i.e. then you can access it using https://hub.xpub.nl/soupboat/collecting-labels as root +app.use('/collecting-labels', express.static('public')) + + +// we dont need this now because express automatically uses the index.html file in the /public folder +// (still need to understand how to get a notification when someone connects, but maybe it is just a callbac to the app.use function of line 19whe + +// app.get('/collecting-labels', function(req, res) { +// res.sendFile(path.join(__dirname, 'public', 'index.html')); +// console.log('Someone connected!') +// }); + + +// when a post request is made to the https://hub.xpub.nl/soupboat/collecting-labels url this function starts +app.post('/collecting-labels', function(req, res) { + + // with fs we read the file labels.json inside the public folder + // it is there because we then use the same file in the client for drawing all the labels stored in the server + // don't know if it is a good idea or not tho + // for now oke + + // also: we need to check that the contents of the post request is what we want aka a label object, structured as we want ecc + // and not some other random thing, otherwise everything will break + + fs.readFile('public/labels.json', function(err, data) { + if (err) throw err; + // parse a JSON out of the labels.json file + let storage = JSON.parse(data) + + // insert the req.body (aka our label object) to the labels array present inside the labels.json file + storage.labels.push(req.body) + + // overwrite the labels.json file with all the data + // this will not be efficient in the long term, but then we may switch to a proper database and not this scissor and glue thing + fs.writeFile('public/labels.json', JSON.stringify(storage), (err)=>{ + if (err) throw err; + console.log('Data written to file');}) + }) + + // reply to the client + // TODO: sayng oke everything worked nice + // for now it just echoes the incoming data + res.send(req.body) +}); + + + // listen to the port we defined at the beginning aka wait for clients to connect there +app.listen(port, function() { + console.log(`๐Ÿงถ โ†’ Collecting Labels on port ${port}!`) +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a7bc190 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,374 @@ +{ + "name": "test-express", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + }, + "mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "requires": { + "mime-db": "1.51.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..82d9f56 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "test-express", + "version": "1.0.0", + "description": "Bla bla bla", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.17.1" + } +} diff --git a/public/.ipynb_checkpoints/index-checkpoint.html b/public/.ipynb_checkpoints/index-checkpoint.html new file mode 100644 index 0000000..6f45083 --- /dev/null +++ b/public/.ipynb_checkpoints/index-checkpoint.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + Collecting Labels + + +
+
+ +
+
+
+ +
+
+ + + + + diff --git a/public/.ipynb_checkpoints/labels-checkpoint.js b/public/.ipynb_checkpoints/labels-checkpoint.js new file mode 100644 index 0000000..802ade1 --- /dev/null +++ b/public/.ipynb_checkpoints/labels-checkpoint.js @@ -0,0 +1,261 @@ +// Get the container to use as a canvas +const container = document.getElementById("container"); +const editor = document.getElementById("editor"); + +const textForm = document.getElementsByClassName("text-input")[0]; +const input = document.getElementById("input"); +const insert = document.getElementById("insert"); +const cancel = document.getElementById("cancel"); + +// List of labels +let labels = []; +let labelsObj = []; + +let closing = false; + +// Start is where the mouse is pressed, End is where the mouse is released +let startX; +let startY; +let endX; +let endY; + +// Minimum size for the label to be created +let minimumSizeX = 25; +let minimumSizeY = 25; + +// Boolean for showing the editor during drawing +let showEditor = false; + +// Store the coordinates and trigger the function +container.addEventListener("mousedown", (e) => { + // Avoid inserting a new label if the user is clicking on a close button) + if (e.target.tagName !== "BUTTON" && e.target.tagName !== "INPUT") { + startX = e.x; + startY = e.y; + + // activate the editor + showEditor = true; + editor.classList.add("show-editor"); + } +}); + +container.addEventListener("mouseup", (e) => { + if (e.target.tagName !== "BUTTON" && e.target.tagName !== "INPUT") { + endX = e.x; + endY = e.y; + + // disable the editor + showEditor = false; + editor.classList.remove("show-editor"); + editor.style.width = 0; + editor.style.height = 0; + + // draw label + drawLabel(); + } +}); + +// Edit the editor box using transform instead of left / top in order to be more efficient +// (but still with width and height ehm idk if this affects the performance a lot) +// (and it is something we must care of because this event is called like every frame that the mouse is dragged) +container.addEventListener("mousemove", (e) => { + if (showEditor) { + let minX = Math.min(startX, e.x); + let minY = Math.min(startY, e.y); + + let maxX = Math.max(startX, e.x); + let maxY = Math.max(startY, e.y); + + let width = maxX - minX; + let height = maxY - minY; + + // Apply a different class when the sizes pass the minimum size + // (i don't know if is good made like this) + if (width > minimumSizeX && height > minimumSizeY) { + editor.classList.add("can-draw"); + } else { + editor.classList.remove("can-draw"); + } + + editor.style.transform = `translate(${minX}px, ${minY}px)`; + editor.style.width = `${maxX - minX}px`; + editor.style.height = `${maxY - minY}px`; + } +}); + +// Check the mouse direction and create the Label +// The origin points of the label (because is positioned with top left) are always the lowest x and y values +// The width and height are the greater x and y values (because width and height cannot be negative) +function drawLabel() { + let minX = Math.min(startX, endX); + let minY = Math.min(startY, endY); + + let maxX = Math.max(startX, endX); + let maxY = Math.max(startY, endY); + + let width = maxX - minX; + let height = maxY - minY; + + if (width > minimumSizeX && height > minimumSizeY) { + // Create a label and push it into the array of labels + let temporaryLabel = createLabel(minX, minY, width, height, labels.length); + temporaryLabel.classList.add("temporary"); + temporaryLabel.id = "temporary-label"; + container.appendChild(temporaryLabel); + + new Promise(function (resolve, reject) { + // then if the user click insert and there is a value in the input-- > resolve the promise and return the text input to create the label, + // if the user click cancel-- > reject the promise and don't create the label + textForm.classList.add("visible"); + + input.focus(); + + // Insert button + insert.addEventListener("click", (e) => { + e.preventDefault(); + if (input.value) { + resolve(); + } + }); + + // Cancel button + cancel.addEventListener("click", (e) => { + e.preventDefault(); + textForm.classList.remove("visible"); + reject("no input"); + }); + }).then(() => { + // Create the label + let label = createLabel(minX, minY, width, height, labels.length); + + // Add the text input to the label + let text = document.createElement("p"); + text.classList.add("label--text"); + text.innerHTML = input.value; + label.appendChild(text); + + let labelObj = { + position: { + x: minX, + y: minY, + }, + size: { + width: width, + height: height, + }, + index: labels.length, + text: input.value, + timestap: Date.now(), + userID: userID, + }; + + uploadLabel(labelObj); + labelsObj.push(labelObj); + labels.push(label); + container.appendChild(label); + createLabelTranscription(label); + + // Reset the modal + input.value = ""; + input.blur(); + let tempLabel = document.getElementById("temporary-label"); + container.removeChild(tempLabel); + textForm.classList.remove("visible"); + }); + } +} + +// Create the label element +function createLabel(x, y, width, height, index) { + let label = document.createElement("div"); + label.classList.add("label"); + label.style.left = `${x}px`; + label.style.top = `${y}px`; + label.style.width = `${width}px`; + label.style.height = `${height}px`; + + // data attribute index maybe we will need it later maybe not + // with the index number of the label + label.setAttribute("data-index", index); + + // Insert the number in the label + let labelNumber = document.createElement("p"); + labelNumber.classList.add("label--number"); + labelNumber.innerHTML = index + 1; + label.appendChild(labelNumber); + + // Add a button for deleting the label + // TODO: reactive numbering oh no + let close = document.createElement("button"); + close.classList.add("label--close"); + close.innerHTML = "x"; + close.addEventListener("click", (e) => { + label.remove(); + }); + label.appendChild(close); + + return label; +} + +const transcriptionPanel = document.getElementById("transcription-panel"); +const transcriptionList = transcriptionPanel.querySelector("ol"); + +function createLabelTranscription(label) { + let transcription = document.createElement("li"); + transcription.innerHTML = label.querySelector(".label--text").innerHTML; + + transcriptionList.appendChild(transcription); +} + +function uploadLabel(obj){ + + fetch('https://hub.xpub.nl/soupboat/collecting-labels/', { + method: 'POST', // or 'PUT' + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(obj), + }) + .then(response => response.json()) + .then(data => { + console.log('Success:', data); + }) + .catch((error) => { + console.error('Error:', error); + }); +} + + +const load = document.getElementById('load-labels') +load.addEventListener('click', (e)=>{ + loadLabels() +}) + +function loadLabels(){ + + fetch("./labels.json") + .then((response) => { + return response.json(); + }) + .then((data) => { + + data.labels.forEach((label, index) => { + let labelElement = createLabel(label.position.x, label.position.y, label.size.width, label.size.height, index) + + // THIS IS TEMPORARY + labelElement.style.backgroundColor = `hsla(${Math.floor(label.userID/10000000000*255)}, 100%, 75%, 0.2)` + + // Add the text input to the label + let text = document.createElement("p"); + text.classList.add("label--text"); + text.innerHTML = label.text; + labelElement.appendChild(text); + + labelElement.classList.add('loaded') + + container.appendChild(labelElement) + }); + + +}) +} \ No newline at end of file diff --git a/public/.ipynb_checkpoints/labels-checkpoint.json b/public/.ipynb_checkpoints/labels-checkpoint.json new file mode 100644 index 0000000..107b6ae --- /dev/null +++ b/public/.ipynb_checkpoints/labels-checkpoint.json @@ -0,0 +1,3 @@ +{ + "labels": [] +} \ No newline at end of file diff --git a/session.js b/public/.ipynb_checkpoints/session-checkpoint.js similarity index 100% rename from session.js rename to public/.ipynb_checkpoints/session-checkpoint.js diff --git a/public/.ipynb_checkpoints/storedLabels-checkpoint.js b/public/.ipynb_checkpoints/storedLabels-checkpoint.js new file mode 100644 index 0000000..e69de29 diff --git a/public/.ipynb_checkpoints/style-checkpoint.css b/public/.ipynb_checkpoints/style-checkpoint.css new file mode 100644 index 0000000..83c7d71 --- /dev/null +++ b/public/.ipynb_checkpoints/style-checkpoint.css @@ -0,0 +1,270 @@ +html, +body { + margin: 0; + font-family: Arial, Helvetica, sans-serif; + width: 100%; + overflow: hidden; +} + + +.test-form{ + position: fixed; + top: 0; + left: 0; + z-index: 500; +} + +#container { + width: 100%; + height: 100vh; + background-color: #fff; +} + +#editor { + position: absolute; + display: none; + border: 1px solid white; + opacity: 0.5; + width: 0; + height: 0; + z-index: 100; +} + +#editor.can-draw { + opacity: 1; +} + +#editor.show-editor { + display: block; +} + +.label { + position: absolute; + background-color: rgba(250, 99, 72, 0.2); + /* border: 1px solid currentColor; */ + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.2); + + overflow: hidden; +} + +.label.temporary { + background: none; + border: 1px dashed tomato; + box-shadow: none; +} + +.label.temporary .label--number, +.label.temporary .label--close { + display: none; +} + +.label--number { + display: inline-block; + margin: 0; + padding: 0 4px; + + user-select: none; + + background-color: white; +} + +.label--close { + position: absolute; + right: 0; + + border: none; + + padding: 0 4px; + + font-size: 1rem; + + background: none; + color: white; + + cursor: pointer; +} +.label--text { + margin: 1ch 0; + padding: 0 1ch; + + overflow: hidden; + width: 100%; + height: 100%; + text-overflow: ellipsis; + white-space: pre; +} + +.text-input { + display: none; + position: absolute; + z-index: 200; + width: 100%; + height: 100vh; + + justify-content: center; + align-items: center; + + background-color: rgba(255, 99, 71, 0.95); +} + +.text-input.visible { + display: flex; +} + +.modal { + padding: 64px; +} + +.modal input { + font-size: 1.5rem; + background: none; + border: none; + color: white; + border-bottom: 1px solid white; +} + +.modal input:focus { + outline: none; + background-color: rgba(255, 255, 255, 0.25); +} + +.text-input button { + color: white; + font-weight: bold; + background: none; + border: none; + cursor: pointer; + font-size: 1.5rem; +} + +#cancel { + font-weight: normal; +} + +.background-container { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + display: flex; + flex-direction: column; +} + +.background-container img { + max-width: 70w; + max-height: 70vh; + object-fit: contain; + user-select: none; + pointer-events: none; +} + +.background-container img.visible { + display: initial; +} + +.hidden { + display: none; +} + +.info, +.transcription { + position: absolute; + right: 0; + bottom: 0; + top: 0; + z-index: 50; + + padding: 24px; + margin: 0; + + width: 25%; + line-height: 1.6; + + background-color: #111; + color: white; + + transform: translateX(100%); + transition: transform 0.4s ease-out; +} + +.transcription.active, +.info.active { + transform: translateX(0); + transition: transform 0.6s ease-in; +} + +.transcription .title, +.info .title { + margin: 0; +} + +.transcription ol { + padding: 0; + list-style-position: inside; + font-size: 1.125rem; +} + +#show-info, +#show-transcription, +.close, +button { + background: none; + + display: inline-block; + min-width: 24px; + height: 24px; + border-radius: 24px; + padding: 0 4px; + + border: 1px solid currentColor; + + color: tomato; + + cursor: pointer; +} + +#show-transcription:hover, +#show-info:hover { + border: 1px solid tomato; + background-color: tomato; + color: white; +} + +.close { + position: absolute; + right: 24px; + top: 32px; + color: white; +} + +#export-text:hover, +.close:hover { + border: 1px solid white; + background-color: white; + color: #111; +} + +#export-text { + color: white; +} + +nav { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 50; + + padding: 24px; + text-align: right; + + pointer-events: none; +} + +nav > * { + pointer-events: all; +} + +img.hidden { + display: none; +} diff --git a/assets/map_description_H.jpg b/public/assets/map_description_H.jpg similarity index 100% rename from assets/map_description_H.jpg rename to public/assets/map_description_H.jpg diff --git a/assets/map_description_V.jpg b/public/assets/map_description_V.jpg similarity index 100% rename from assets/map_description_V.jpg rename to public/assets/map_description_V.jpg diff --git a/index.html b/public/index.html similarity index 89% rename from index.html rename to public/index.html index 0d41d5b..6f45083 100644 --- a/index.html +++ b/public/index.html @@ -11,12 +11,12 @@ - Concrete Label + Collecting Labels
- +
@@ -31,6 +31,7 @@ +