diff --git a/.gitignore b/.gitignore index b512c09..b3aed83 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -node_modules \ No newline at end of file +node_modules +rename.sh +data-persistence.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ab42783 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/index.js" + } + ] +} \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.MD b/README.MD index 54bcd5a..05f05d0 100644 --- a/README.MD +++ b/README.MD @@ -1,10 +1,5 @@ # openCountdown # ToDo -- [X] Pausing functions -- [X] Presets -- [X] time on countdown page -- [X] one-way messaging -- [ ] Better UI -- [X] Progress bar - [P] Endpoint docs +- [ ] Better WS frames \ No newline at end of file diff --git a/helpers.js b/helpers.js new file mode 100644 index 0000000..7ffcad1 --- /dev/null +++ b/helpers.js @@ -0,0 +1,32 @@ +/** + * Converts a string into a boolean + * @param {String} stringBoolean + * @returns Boolean if valid or -1 otherwise + */ +function convertStringBooleanToBoolean(stringBoolean) { + if (stringBoolean === 'true') { + return true; + } else if(stringBoolean === 'false') { + return false; + } else { + return(-1) + } +} + +/** + * Wraps convertStringBooleanToBoolean to be used with express. Will respond with an error if the boolean is invalid. Else dataObj will be set to the converted boolean value. + * @param {String} stringBoolean An input string given by the user + * @param {*} res Expresses response object + * @param {*} dataObj The data to manipulate + */ + +function wrapBooleanConverter(stringBoolean, res) { + const temp = convertStringBooleanToBoolean(stringBoolean); + if(temp == -1) { + res.json({ status: "error", message: "Invalid boolean value" }); + } else { + return(temp); + } +} + +module.exports = { convertStringBooleanToBoolean, wrapBooleanConverter }; \ No newline at end of file diff --git a/index.js b/index.js index 3597f74..f0a484d 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,10 @@ const express = require("express"); const fs = require("fs"); const bodyParser = require("body-parser"); +const ws = require('ws'); +const helper = require("./helpers.js"); +console.log("Preparing server..."); const app = express(); app.use(express.static("static")); @@ -15,31 +18,72 @@ app.use( }) ); +let loadedData = {} + +if (fs.existsSync("data-persistence.json")) { + const loadedDataRaw = fs.readFileSync("data-persistence.json", "utf8"); + loadedData = JSON.parse(loadedDataRaw); +} else { + console.warn("Unable to load persistent data"); +} + currentState = { mode: "clock", countdownGoal: new Date().getTime(), showMilliSeconds: true, defaultFullScreen: true, timeAmountInital: 0, - timerRunState: true, + timerRunState: false, pauseMoment: 0, showTimeOnCountdown: true, message: "", showMessage: false, messageAppearTime: 0, showProgressbar: true, - colorSegments: {20000: "#FFAE00", 5000: "#ff0000", "START": "yellow"}, + colorSegments: { 40000: "yellow", 20000: "#FFAE00", 5000: "#ff0000", "START": "green" }, textColors: {}, srvTime: 0, enableColoredText: true, - debug: false + debug: false, + sessionToken: Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15), }; +const dataToBeWritten = {}; + +currentState = Object.assign({}, currentState, loadedData); currentState.textColors = currentState.colorSegments + +const wsServer = new ws.Server({ noServer: true }); +wsServer.on('connection', socket => { + socket.on('message', function incoming(data) { + if (data.toString() == "new client") { + updatedData() + } + }); +}); + +wsServer.broadcast = function broadcast(data) { + wsServer.clients.forEach(function each(client) { + // The data is coming in correctly + // console.log(data); + client.send(data); + }); +}; + +let updatey = undefined; + +function updatedData() { + currentState.srvTime = new Date().getTime() + wsServer.broadcast(JSON.stringify(currentState)); + clearTimeout(updatey); + setTimeout(updatedData, 5000); +} + +console.log("Preparing routes..."); app.get("/", function (req, res) { - const data = fs.readFileSync("templates/adminPanel.html", "utf8"); + const data = fs.readFileSync("templates/newAdminPanel.html", "utf8"); res.send(data); }); @@ -53,78 +97,155 @@ app.get("/api/v1/data", function (req, res) { res.json(currentState); }); +app.get("/api/v1/system", function (req, res) { + const tempPkgFile = fs.readFileSync("package.json", "utf8"); + const tempPkgObj = JSON.parse(tempPkgFile); + const systemData = { + uptime: process.uptime(), + memoryUsage: process.memoryUsage(), + cpuUsage: process.cpuUsage(), + platform: process.platform, + arch: process.arch, + nodeVersion: process.version, + nodePath: process.execPath, + nodeArgv: process.argv, + nodeExecArgv: process.execArgv, + nodeCwd: process.cwd(), + nodeEnv: process.env, + nodeConfig: process.config, + nodeTitle: process.title, + systemVersion: tempPkgObj.version + } + res.json(systemData); +}); + + app.get("/api/v1/set/mode", function (req, res) { currentState.mode = req.query.mode; + updatedData() res.json({ status: "ok" }); }); app.get("/api/v1/set/layout/showMillis", function (req, res) { - currentState.showMilliSeconds = (req.query.show === 'true'); - res.json({ status: "ok" }); + const resy = helper.wrapBooleanConverter(req.query.show, res) + if (resy != undefined) { + currentState.showMilliSeconds = resy; + if (req.query.persist === 'true') { + dataToBeWritten.showMilliSeconds = currentState.showMilliSeconds + } + res.json({ status: "ok" }); + } + updatedData() + }); app.get("/api/v1/set/timerGoal", function (req, res) { currentState.countdownGoal = new Date(parseInt(req.query.time)).getTime(); // ToDO error handling res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/set/addMillisToTimer", function (req, res) { currentState.timeAmountInital = req.query.time; currentState.countdownGoal = new Date().getTime() + parseInt(req.query.time) + currentState.pauseMoment = new Date().getTime(); res.json({ status: "ok" }); -}); + updatedData() +}); app.get("/api/v1/ctrl/timer/pause", function (req, res) { currentState.timerRunState = false; currentState.pauseMoment = new Date().getTime(); res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/ctrl/timer/play", function (req, res) { - currentState.timerRunState = true - currentState.countdownGoal += new Date().getTime() - currentState.pauseMoment; + + if (currentState.timerRunState == false) { + currentState.timerRunState = true + currentState.countdownGoal += new Date().getTime() - currentState.pauseMoment; + } res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/ctrl/timer/restart", function (req, res) { currentState.countdownGoal = new Date().getTime() + parseInt(currentState.timeAmountInital) res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/set/layout/showTime", function (req, res) { - currentState.showTimeOnCountdown = (req.query.show === 'true'); - res.json({ status: "ok" }); + const resy = helper.wrapBooleanConverter(req.query.show, res) + if (resy != undefined) { + currentState.showTimeOnCountdown = resy; + if (req.query.persist === 'true') { + dataToBeWritten.showTimeOnCountdown = currentState.showTimeOnCountdown + } + res.json({ status: "ok" }); + } + updatedData() }); app.get("/api/v1/set/progressbar/show", function (req, res) { currentState.showProgressbar = (req.query.show === 'true'); + if (req.query.persist === 'true') { + dataToBeWritten.showProgressbar = currentState.showProgressbar + } res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/set/progressbar/colors", function (req, res) { try { - currentState.colorSegments = JSON.parse(req.query.colors); - res.json({ status: "ok" }); - } catch (error) { - res.json({ status: "error", message: error }); - } -}); - -app.get("/api/v1/set/text/colors", function (req, res) { - try { - if(req.query.copy === "true"){ - currentState.textColors = currentState.colorSegments; - } else { - currentState.textColors = JSON.parse(req.query.colors); + let data = req.query.colors + if (req.query.isBase64 === "true") { + data = atob(data) + } + currentState.colorSegments = JSON.parse(data); + if (req.query.persist === 'true') { + dataToBeWritten.colorSegments = currentState.colorSegments } res.json({ status: "ok" }); } catch (error) { res.json({ status: "error", message: error }); + console.error(error) } + updatedData() +}); + +app.get("/api/v1/set/text/colors", function (req, res) { + try { + if (req.query.copy === "true") { + currentState.textColors = currentState.colorSegments + res.json({ status: "ok" }); + } else { + let data = req.query.colors + if (req.query.isBase64 === "true") { + data = atob(data) + } + console.debug(data) + currentState.textColors = JSON.parse(data); + if (req.query.persist === 'true') { + dataToBeWritten.textColors = currentState.textColors + } + } + res.json({ status: "ok" }); + } catch (error) { + res.json({ status: "error", message: error }); + console.error(error) + } + updatedData() }); app.get("/api/v1/set/text/enableColoring", function (req, res) { currentState.enableColoredText = (req.query.enable === 'true'); + if (req.query.persist === 'true') { + dataToBeWritten.enableColoredText = currentState.enableColoredText + } + res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/ctrl/message/show", function (req, res) { @@ -132,16 +253,82 @@ app.get("/api/v1/ctrl/message/show", function (req, res) { currentState.showMessage = true currentState.messageAppearTime = new Date().getTime() res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/debug", function (req, res) { currentState.debug = (req.query.enable === 'true'); res.json({ status: "ok" }); + updatedData() }); app.get("/api/v1/ctrl/message/hide", function (req, res) { currentState.showMessage = false res.json({ status: "ok" }); + updatedData() }); -app.listen(3005); +app.get("/api/v1/storage/commit", function (req, res) { + const tempString = JSON.stringify(dataToBeWritten); + try { + fs.writeFileSync("data-persistence.json", tempString); + res.json({ status: "ok" }); + } catch (error) { + res.json({ status: "error", reason: error }); + } + updatedData() +}); + + +app.get("/api/v1/storage/delete", function (req, res) { + if (req.query.delete === "true") { + if (fs.existsSync("data-persistence.json")) { + fs.unlinkSync("data-persistence.json"); + res.json({ status: "ok" }); + } else { + res.json({ status: "error", reason: "No persistence data was found" }); + } + + } else { + + } res.json({ status: "error", reason: "Missing delete argument" }); + updatedData() +}); + +app.use(function(req, res, next) { + res.status(404); + + // respond with html page + if (req.accepts('html')) { + const data = fs.readFileSync("templates/errorPages/404.html", "utf8"); + res.status(404) + res.send(data); + return; + } + + // respond with json + if (req.accepts('json')) { + res.json({ error: 'Not found' }); + return; + } + + // default to plain-text. send() + res.type('txt').send('Not found'); +}); + + + +console.log("Starting server..."); +const port = 3005 +const server = app.listen(port); +server.on('upgrade', (request, socket, head) => { + wsServer.handleUpgrade(request, socket, head, socket => { + wsServer.emit('connection', socket, request); + }); +}); + + + +console.info("Server running on port " + port); +console.info("Visit localhost:" + port + "/timer for the timer page"); +console.info("Visit localhost:" + port + " for the admin page"); diff --git a/package-lock.json b/package-lock.json index e86fe82..0f8f883 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,36 @@ { "name": "opencountdown", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "opencountdown", - "version": "1.0.0", - "license": "ISC", + "version": "1.0.1", + "license": "LGPL-3.0", "dependencies": { "body-parser": "^1.19.2", - "countdown": "^2.6.0", - "express": "^4.17.3" + "bootstrap": "^5.1.3", + "bootstrap-duration-picker": "^2.1.3", + "bootstrap-icons": "^1.8.1", + "darkreader": "^4.9.44", + "express": "^4.17.3", + "flatpickr": "^4.6.11", + "jquery": "^3.6.0", + "js-cookie": "^3.0.1", + "less": "^3.13", + "mdbootstrap": "^4.20.0", + "ws": "^8.5.0" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", + "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" } }, "node_modules/accepts": { @@ -51,6 +70,31 @@ "node": ">= 0.8" } }, + "node_modules/bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "@popperjs/core": "^2.10.2" + } + }, + "node_modules/bootstrap-duration-picker": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bootstrap-duration-picker/-/bootstrap-duration-picker-2.1.3.tgz", + "integrity": "sha1-vxctw2psm4lQBpYqLYGIwzYPsmU=" + }, + "node_modules/bootstrap-icons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.8.1.tgz", + "integrity": "sha512-IXUqislddPJfwq6H+2nTkHyr9epO9h6u1AG0OZCx616w+TgzeoCjfmI3qJMQqt1J586gN2IxzB4M99Ip4sTZ1w==", + "engines": { + "node": ">=10" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -91,10 +135,25 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "node_modules/countdown": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/countdown/-/countdown-2.6.0.tgz", - "integrity": "sha1-Z3+446nUzE52QVkBuiU7UYrzQXc=" + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/darkreader": { + "version": "4.9.44", + "resolved": "https://registry.npmjs.org/darkreader/-/darkreader-4.9.44.tgz", + "integrity": "sha512-Mrt5s5eMaFC24Tfi2nNsXpYIfzWK/RUzJxgk0Ysqokvk9lBCn/pkUTyQGYeQbU/U3aKZEYXXqsxZjNfiLoA6QQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/darkreader/donate" + } }, "node_modules/debug": { "version": "2.6.9", @@ -130,6 +189,18 @@ "node": ">= 0.8" } }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -200,6 +271,11 @@ "node": ">= 0.8" } }, + "node_modules/flatpickr": { + "version": "4.6.11", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.11.tgz", + "integrity": "sha512-/rnbE/hu5I5zndLEyYfYvqE4vPDvI5At0lFcQA5eOPfjquZLcQ0HMKTL7rv5/+DvbPM3/vJcXpXjB/DjBh+1jw==" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -216,6 +292,12 @@ "node": ">= 0.6" } }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "optional": true + }, "node_modules/http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", @@ -242,6 +324,18 @@ "node": ">=0.10.0" } }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -255,6 +349,66 @@ "node": ">= 0.10" } }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" + }, + "node_modules/jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "node_modules/js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/less": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", + "dependencies": { + "copy-anything": "^2.0.1", + "tslib": "^1.10.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mdbootstrap": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/mdbootstrap/-/mdbootstrap-4.20.0.tgz", + "integrity": "sha512-eIxaYsvHct2BKqkJWphh6j7nFQXHh9NjbbN7a+dctfrVsKaydNW5XpKIxmq2tDSb9+XYGm9hK7hMTlHgeV6wdw==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -311,6 +465,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "node_modules/native-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz", + "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==", + "optional": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -343,6 +503,15 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -355,6 +524,12 @@ "node": ">= 0.10" } }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "optional": true + }, "node_modules/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", @@ -412,6 +587,15 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/send": { "version": "0.17.2", "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", @@ -459,6 +643,15 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -475,6 +668,11 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -510,9 +708,35 @@ "engines": { "node": ">= 0.8" } + }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } }, "dependencies": { + "@popperjs/core": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", + "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==", + "peer": true + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -544,6 +768,22 @@ "type-is": "~1.6.18" } }, + "bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "requires": {} + }, + "bootstrap-duration-picker": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bootstrap-duration-picker/-/bootstrap-duration-picker-2.1.3.tgz", + "integrity": "sha1-vxctw2psm4lQBpYqLYGIwzYPsmU=" + }, + "bootstrap-icons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.8.1.tgz", + "integrity": "sha512-IXUqislddPJfwq6H+2nTkHyr9epO9h6u1AG0OZCx616w+TgzeoCjfmI3qJMQqt1J586gN2IxzB4M99Ip4sTZ1w==" + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -572,10 +812,18 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "countdown": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/countdown/-/countdown-2.6.0.tgz", - "integrity": "sha1-Z3+446nUzE52QVkBuiU7UYrzQXc=" + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "requires": { + "is-what": "^3.14.1" + } + }, + "darkreader": { + "version": "4.9.44", + "resolved": "https://registry.npmjs.org/darkreader/-/darkreader-4.9.44.tgz", + "integrity": "sha512-Mrt5s5eMaFC24Tfi2nNsXpYIfzWK/RUzJxgk0Ysqokvk9lBCn/pkUTyQGYeQbU/U3aKZEYXXqsxZjNfiLoA6QQ==" }, "debug": { "version": "2.6.9", @@ -605,6 +853,15 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -666,6 +923,11 @@ "unpipe": "~1.0.0" } }, + "flatpickr": { + "version": "4.6.11", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.11.tgz", + "integrity": "sha512-/rnbE/hu5I5zndLEyYfYvqE4vPDvI5At0lFcQA5eOPfjquZLcQ0HMKTL7rv5/+DvbPM3/vJcXpXjB/DjBh+1jw==" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -676,6 +938,12 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "optional": true + }, "http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", @@ -696,6 +964,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -706,6 +980,52 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" + }, + "jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==" + }, + "less": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0", + "tslib": "^1.10.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mdbootstrap": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/mdbootstrap/-/mdbootstrap-4.20.0.tgz", + "integrity": "sha512-eIxaYsvHct2BKqkJWphh6j7nFQXHh9NjbbN7a+dctfrVsKaydNW5XpKIxmq2tDSb9+XYGm9hK7hMTlHgeV6wdw==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -744,6 +1064,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "native-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz", + "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==", + "optional": true + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -767,6 +1093,12 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "optional": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -776,6 +1108,12 @@ "ipaddr.js": "1.9.1" } }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "optional": true + }, "qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", @@ -807,6 +1145,12 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true + }, "send": { "version": "0.17.2", "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", @@ -850,6 +1194,12 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -860,6 +1210,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -883,6 +1238,12 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "requires": {} } } } diff --git a/package.json b/package.json index 33a91c8..0026266 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opencountdown", - "version": "1.0.0", + "version": "1.0.1", "description": "An opensource countdown", "main": "index.js", "scripts": { @@ -10,7 +10,16 @@ "license": "LGPL-3.0", "dependencies": { "body-parser": "^1.19.2", - "countdown": "^2.6.0", - "express": "^4.17.3" + "bootstrap": "^5.1.3", + "bootstrap-duration-picker": "^2.1.3", + "bootstrap-icons": "^1.8.1", + "darkreader": "^4.9.44", + "express": "^4.17.3", + "flatpickr": "^4.6.11", + "jquery": "^3.6.0", + "js-cookie": "^3.0.1", + "less": "^3.13", + "mdbootstrap": "^4.20.0", + "ws": "^8.5.0" } } diff --git a/static/bootstrap-duration-picker b/static/bootstrap-duration-picker new file mode 120000 index 0000000..3b83664 --- /dev/null +++ b/static/bootstrap-duration-picker @@ -0,0 +1 @@ +../node_modules/bootstrap-duration-picker/dist/ \ No newline at end of file diff --git a/static/bootstrap/dist b/static/bootstrap/dist new file mode 120000 index 0000000..8fc3423 --- /dev/null +++ b/static/bootstrap/dist @@ -0,0 +1 @@ +../../node_modules/bootstrap/dist \ No newline at end of file diff --git a/static/coloris/coloris.min.css b/static/coloris/coloris.min.css new file mode 100644 index 0000000..1e50bd4 --- /dev/null +++ b/static/coloris/coloris.min.css @@ -0,0 +1 @@ +.clr-picker{display:none;flex-wrap:wrap;position:absolute;width:200px;z-index:1000;border-radius:10px;background-color:#fff;justify-content:space-between;box-shadow:0 0 5px rgba(0,0,0,.05),0 5px 20px rgba(0,0,0,.1);-moz-user-select:none;-webkit-user-select:none;user-select:none}.clr-picker.clr-open{display:flex}.clr-gradient{position:relative;width:100%;height:100px;margin-bottom:15px;border-radius:3px 3px 0 0;background-image:linear-gradient(rgba(0,0,0,0),#000),linear-gradient(90deg,#fff,currentColor);cursor:pointer}.clr-marker{position:absolute;width:12px;height:12px;margin:-6px 0 0 -6px;border:1px solid #fff;border-radius:50%;background-color:currentColor;cursor:pointer}.clr-picker input[type=range]::-webkit-slider-runnable-track{width:100%;height:8px}.clr-picker input[type=range]::-webkit-slider-thumb{width:8px;height:8px;-webkit-appearance:none}.clr-picker input[type=range]::-moz-range-track{width:100%;height:8px;border:0}.clr-picker input[type=range]::-moz-range-thumb{width:8px;height:8px;border:0}.clr-hue{background-image:linear-gradient(to right,red 0,#ff0 16.66%,#0f0 33.33%,#0ff 50%,#00f 66.66%,#f0f 83.33%,red 100%)}.clr-alpha,.clr-hue{position:relative;width:calc(100% - 40px);height:8px;margin:5px 20px;border-radius:4px}.clr-alpha span{display:block;height:100%;width:100%;border-radius:inherit;background-image:linear-gradient(90deg,rgba(0,0,0,0),currentColor)}.clr-alpha input,.clr-hue input{position:absolute;width:calc(100% + 16px);height:16px;left:-8px;top:-4px;margin:0;background-color:transparent;opacity:0;cursor:pointer;appearance:none;-webkit-appearance:none}.clr-alpha div,.clr-hue div{position:absolute;width:16px;height:16px;left:0;top:50%;margin-left:-8px;transform:translateY(-50%);border:2px solid #fff;border-radius:50%;background-color:currentColor;box-shadow:0 0 1px #888;pointer-events:none}.clr-alpha div:before{content:'';position:absolute;height:100%;width:100%;left:0;top:0;border-radius:50%;background-color:currentColor}.clr-format{display:none;order:1;width:calc(100% - 40px);margin:0 20px 20px}.clr-segmented{display:flex;position:relative;width:100%;margin:0;padding:0;border:1px solid #ddd;border-radius:15px;box-sizing:border-box;color:#999;font-size:12px}.clr-segmented input,.clr-segmented legend{position:absolute;width:100%;height:100%;margin:0;padding:0;border:0;left:0;top:0;opacity:0;pointer-events:none}.clr-segmented label{flex-grow:1;padding:4px 0;text-align:center;cursor:pointer}.clr-segmented label:first-of-type{border-radius:10px 0 0 10px}.clr-segmented label:last-of-type{border-radius:0 10px 10px 0}.clr-segmented input:checked+label{color:#fff;background-color:#666}.clr-swatches{order:2;width:calc(100% - 32px);margin:0 16px}.clr-swatches div{display:flex;flex-wrap:wrap;padding-bottom:12px;justify-content:center}.clr-swatches button{position:relative;width:20px;height:20px;margin:0 4px 6px 4px;border:0;border-radius:50%;color:inherit;text-indent:-1000px;white-space:nowrap;overflow:hidden;cursor:pointer}.clr-swatches button:after{content:'';display:block;position:absolute;width:100%;height:100%;left:0;top:0;border-radius:inherit;background-color:currentColor;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1)}input.clr-color{order:1;width:calc(100% - 80px);height:32px;margin:15px 20px 20px 0;padding:0 10px;border:1px solid #ddd;border-radius:16px;color:#444;background-color:#fff;font-family:sans-serif;font-size:14px;text-align:center;box-shadow:none}input.clr-color:focus{outline:0;border:1px solid #1e90ff}.clr-clear{display:none;order:2;height:24px;margin:0 20px 20px auto;padding:0 20px;border:0;border-radius:12px;color:#fff;background-color:#666;font-family:inherit;font-size:12px;font-weight:400;cursor:pointer}.clr-preview{position:relative;width:32px;height:32px;margin:15px 0 20px 20px;border:0;border-radius:50%;overflow:hidden;cursor:pointer}.clr-preview:after,.clr-preview:before{content:'';position:absolute;height:100%;width:100%;left:0;top:0;border:1px solid #fff;border-radius:50%}.clr-preview:after{border:0;background-color:currentColor;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1)}.clr-alpha div,.clr-color,.clr-hue div,.clr-marker{box-sizing:border-box}.clr-field{display:inline-block;position:relative;color:transparent}.clr-field button{position:absolute;width:30px;height:100%;right:0;top:50%;transform:translateY(-50%);border:0;color:inherit;text-indent:-1000px;white-space:nowrap;overflow:hidden;pointer-events:none}.clr-field button:after{content:'';display:block;position:absolute;width:100%;height:100%;left:0;top:0;border-radius:inherit;background-color:currentColor;box-shadow:inset 0 0 1px rgba(0,0,0,.5)}.clr-alpha,.clr-alpha div,.clr-field button,.clr-preview:before,.clr-swatches button{background-image:repeating-linear-gradient(45deg,#aaa 25%,transparent 25%,transparent 75%,#aaa 75%,#aaa),repeating-linear-gradient(45deg,#aaa 25%,#fff 25%,#fff 75%,#aaa 75%,#aaa);background-position:0 0,4px 4px;background-size:8px 8px}.clr-marker:focus{outline:0}.clr-keyboard-nav .clr-alpha input:focus+div,.clr-keyboard-nav .clr-hue input:focus+div,.clr-keyboard-nav .clr-marker:focus,.clr-keyboard-nav .clr-segmented input:focus+label{outline:0;box-shadow:0 0 0 2px #1e90ff,0 0 2px 2px #fff}.clr-picker[data-alpha=false] .clr-alpha{display:none}.clr-picker[data-minimal=true]{padding-top:16px}.clr-picker[data-minimal=true] .clr-alpha,.clr-picker[data-minimal=true] .clr-color,.clr-picker[data-minimal=true] .clr-gradient,.clr-picker[data-minimal=true] .clr-hue,.clr-picker[data-minimal=true] .clr-preview{display:none}.clr-dark{background-color:#444}.clr-dark .clr-segmented{border-color:#777}.clr-dark .clr-swatches button:after{box-shadow:inset 0 0 0 1px rgba(255,255,255,.3)}.clr-dark input.clr-color{color:#fff;border-color:#777;background-color:#555}.clr-dark input.clr-color:focus{border-color:#1e90ff}.clr-dark .clr-preview:after{box-shadow:inset 0 0 0 1px rgba(255,255,255,.5)}.clr-dark .clr-alpha,.clr-dark .clr-alpha div,.clr-dark .clr-preview:before,.clr-dark .clr-swatches button{background-image:repeating-linear-gradient(45deg,#666 25%,transparent 25%,transparent 75%,#888 75%,#888),repeating-linear-gradient(45deg,#888 25%,#444 25%,#444 75%,#888 75%,#888)}.clr-picker.clr-polaroid{border-radius:6px;box-shadow:0 0 5px rgba(0,0,0,.1),0 5px 30px rgba(0,0,0,.2)}.clr-picker.clr-polaroid:before{content:'';display:block;position:absolute;width:16px;height:10px;left:20px;top:-10px;border:solid transparent;border-width:0 8px 10px 8px;border-bottom-color:currentColor;box-sizing:border-box;color:#fff;filter:drop-shadow(0 -4px 3px rgba(0,0,0,.1));pointer-events:none}.clr-picker.clr-polaroid.clr-dark:before{color:#444}.clr-picker.clr-polaroid.clr-left:before{left:auto;right:20px}.clr-picker.clr-polaroid.clr-top:before{top:auto;bottom:-10px;transform:rotateZ(180deg)}.clr-polaroid .clr-gradient{width:calc(100% - 20px);height:120px;margin:10px;border-radius:3px}.clr-polaroid .clr-alpha,.clr-polaroid .clr-hue{width:calc(100% - 30px);height:10px;margin:6px 15px;border-radius:5px}.clr-polaroid .clr-alpha div,.clr-polaroid .clr-hue div{box-shadow:0 0 5px rgba(0,0,0,.2)}.clr-polaroid .clr-format{width:calc(100% - 20px);margin:0 10px 15px}.clr-polaroid .clr-swatches{width:calc(100% - 12px);margin:0 6px}.clr-polaroid .clr-swatches div{padding-bottom:10px}.clr-polaroid .clr-swatches button{width:22px;height:22px}.clr-polaroid input.clr-color{width:calc(100% - 60px);margin:10px 10px 15px 0}.clr-polaroid .clr-clear{margin:0 10px 15px auto}.clr-polaroid .clr-preview{margin:10px 0 15px 10px}.clr-picker.clr-large{width:275px}.clr-large .clr-gradient{height:150px}.clr-large .clr-swatches button{width:22px;height:22px} \ No newline at end of file diff --git a/static/coloris/coloris.min.js b/static/coloris/coloris.min.js new file mode 100644 index 0000000..f6a773f --- /dev/null +++ b/static/coloris/coloris.min.js @@ -0,0 +1,6 @@ +/*! + * Copyright (c) 2021 Momo Bassit. + * Licensed under the MIT License (MIT) + * https://github.com/mdbassit/Coloris + */ +!function(d,p,s){var h,f,v,c,u,y,i,b,l,g,m,w,k,x,a=p.createElement("canvas").getContext("2d"),E={r:0,g:0,b:0,h:0,s:0,v:0,a:1},L={el:"[data-coloris]",parent:null,theme:"default",themeMode:"light",wrap:!0,margin:2,format:"hex",formatToggle:!1,swatches:[],swatchesOnly:!1,alpha:!0,focusInput:!0,autoClose:!1,clearButton:{show:!1,label:"Clear"},a11y:{open:"Open color picker",close:"Close color picker",marker:"Saturation: {s}. Brightness: {v}.",hueSlider:"Hue slider",alphaSlider:"Opacity slider",input:"Color value field",format:"Color format",swatch:"Color swatch",instruction:"Saturation and brightness selector. Use up, down, left and right arrow keys to select."}};function o(e){if("object"==typeof e)for(var t in e)switch(t){case"el":S(e.el),!1!==e.wrap&&T(e.el);break;case"parent":L.parent=p.querySelector(e.parent),L.parent&&L.parent.appendChild(h);break;case"themeMode":L.themeMode=e.themeMode,"auto"===e.themeMode&&d.matchMedia&&d.matchMedia("(prefers-color-scheme: dark)").matches&&(L.themeMode="dark");case"theme":e.theme&&(L.theme=e.theme),h.className="clr-picker clr-"+L.theme+" clr-"+L.themeMode;break;case"margin":e.margin*=1,L.margin=(isNaN(e.margin)?L:e).margin;break;case"wrap":e.el&&e.wrap&&T(e.el);break;case"formatToggle":N("clr-format").style.display=e.formatToggle?"block":"none",e.formatToggle&&(L.format="auto");break;case"swatches":Array.isArray(e.swatches)&&function(){var a=[];e.swatches.forEach(function(e,t){a.push('")}),a.length&&(N("clr-swatches").innerHTML="
"+a.join("")+"
")}();break;case"swatchesOnly":L.swatchesOnly=!!e.swatchesOnly,h.setAttribute("data-minimal",L.swatchesOnly),L.swatchesOnly&&(L.autoClose=!0);break;case"alpha":L.alpha=!!e.alpha,h.setAttribute("data-alpha",L.alpha);break;case"clearButton":var a="none";e.clearButton.show&&(a="block"),e.clearButton.label&&(i.innerHTML=e.clearButton.label),i.style.display=a;break;case"a11y":var l,r=e.a11y,o=!1;if("object"==typeof r)for(var n in r)r[n]&&L.a11y[n]&&(L.a11y[n]=r[n],o=!0);o&&(l=N("clr-open-label"),a=N("clr-swatch-label"),l.innerHTML=L.a11y.open,a.innerHTML=L.a11y.swatch,u.setAttribute("aria-label",L.a11y.close),b.setAttribute("aria-label",L.a11y.hueSlider),g.setAttribute("aria-label",L.a11y.alphaSlider),y.setAttribute("aria-label",L.a11y.input),f.setAttribute("aria-label",L.a11y.instruction));default:L[t]=e[t]}}function S(e){O(p,"click",e,function(e){var t=L.parent,a=e.target.getBoundingClientRect(),l=d.scrollY,r={left:!1,top:!1},o={x:0,y:0},n=a.x,i=l+a.y+a.height+L.margin;w=e.target,x=w.value,k=function(e){e=e.substring(0,3).toLowerCase();return"rgb"!==e&&"hsl"!==e?"hex":e}(x),h.classList.add("clr-open");var c,s=h.offsetWidth,u=h.offsetHeight;t?(c=d.getComputedStyle(t),e=parseFloat(c.marginTop),c=parseFloat(c.borderTopWidth),(o=t.getBoundingClientRect()).y+=c+l,n-=o.x,i-=o.y,n+s>t.clientWidth&&(n+=a.width-s,r.left=!0),i+u>t.clientHeight-e&&(i-=a.height+u+2*L.margin,r.top=!0),i+=t.scrollTop):(n+s>p.documentElement.clientWidth&&(n+=a.width-s,r.left=!0),i+u-l>p.documentElement.clientHeight&&(i=l+a.y-u-L.margin,r.top=!0)),h.classList.toggle("clr-left",r.left),h.classList.toggle("clr-top",r.top),h.style.left=n+"px",h.style.top=i+"px",v={width:f.offsetWidth,height:f.offsetHeight,x:h.offsetLeft+f.offsetLeft+o.x,y:h.offsetTop+f.offsetTop+o.y},A(x),L.focusInput&&y.focus({preventScroll:!0})}),O(p,"input",e,function(e){var t=e.target.parentNode;t.classList.contains("clr-field")&&(t.style.color=e.target.value)})}function T(e){p.querySelectorAll(e).forEach(function(e){var t,a=e.parentNode;a.classList.contains("clr-field")||((t=p.createElement("div")).innerHTML='',a.insertBefore(t,e),t.setAttribute("class","clr-field"),t.style.color=e.value,t.appendChild(e))})}function n(e){w&&(e&&x!==w.value&&(w.value=x,w.dispatchEvent(new Event("input",{bubbles:!0}))),x!==w.value&&w.dispatchEvent(new Event("change",{bubbles:!0})),h.classList.remove("clr-open"),L.focusInput&&w.focus({preventScroll:!0}),w=null)}function A(e){var t=function(e){var t;a.fillStyle="#000",a.fillStyle=e,(e=/^((rgba)|rgb)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i.exec(a.fillStyle))?(t={r:+e[3],g:+e[4],b:+e[5],a:+e[6]}).a=+t.a.toFixed(2):(e=a.fillStyle.replace("#","").match(/.{2}/g).map(function(e){return parseInt(e,16)}),t={r:e[0],g:e[1],b:e[2],a:1});return t}(e),e=function(e){var t=e.r/255,a=e.g/255,l=e.b/255,r=s.max(t,a,l),o=s.min(t,a,l),n=r-o,i=r,c=0,o=0;n&&(r===t&&(c=(a-l)/n),r===a&&(c=2+(l-t)/n),r===l&&(c=4+(t-a)/n),r&&(o=n/r));return{h:(c=s.floor(60*c))<0?c+360:c,s:s.round(100*o),v:s.round(100*i),a:e.a}}(t);M(e.s,e.v),H(t,e),b.value=e.h,h.style.color="hsl("+e.h+", 100%, 50%)",l.style.left=e.h/360*100+"%",c.style.left=v.width*e.s/100+"px",c.style.top=v.height-v.height*e.v/100+"px",g.value=100*e.a,m.style.left=100*e.a+"%"}function r(e){w&&(w.value=void 0!==e?e:y.value,w.dispatchEvent(new Event("input",{bubbles:!0})))}function C(e,t){e={h:+b.value,s:e/v.width*100,v:100-t/v.height*100,a:g.value/100},t=function(e){var t=e.s/100,a=e.v/100,l=t*a,r=e.h/60,o=l*(1-s.abs(r%2-1)),n=a-l;l+=n,o+=n;t=s.floor(r)%6,a=[l,o,n,n,o,l][t],r=[o,l,l,o,n,n][t],t=[n,n,o,l,l,o][t];return{r:s.round(255*a),g:s.round(255*r),b:s.round(255*t),a:e.a}}(e);M(e.s,e.v),H(t,e),r()}function M(e,t){var a=L.a11y.marker;e=+e.toFixed(1),t=+t.toFixed(1),a=(a=a.replace("{s}",e)).replace("{v}",t),c.setAttribute("aria-label",a)}function t(e){var t={pageX:((a=e).changedTouches?a.changedTouches[0]:a).pageX,pageY:(a.changedTouches?a.changedTouches[0]:a).pageY},a=t.pageX-v.x,t=t.pageY-v.y;L.parent&&(t+=L.parent.scrollTop),a=a<0?0:a>v.width?v.width:a,t=t<0?0:t>v.height?v.height:t,c.style.left=a+"px",c.style.top=t+"px",C(a,t),e.preventDefault(),e.stopPropagation()}function H(e,t){void 0===t&&(t={});var a,l,r=L.format;for(a in e=void 0===e?{}:e)E[a]=e[a];for(l in t)E[l]=t[l];var o,n=function(e){var t=e.r.toString(16),a=e.g.toString(16),l=e.b.toString(16),r="";e.r<16&&(t="0"+t);e.g<16&&(a="0"+a);e.b<16&&(l="0"+l);L.alpha&&e.a<1&&(e=255*e.a|0,r=e.toString(16),e<16&&(r="0"+r));return"#"+t+a+l+r}(E),i=n.substring(0,7);switch(c.style.color=i,m.parentNode.style.color=i,m.style.color=n,u.style.color=n,f.style.display="none",f.offsetHeight,f.style.display="",m.nextElementSibling.style.display="none",m.nextElementSibling.offsetHeight,m.nextElementSibling.style.display="","mixed"===r?r=1===E.a?"hex":"rgb":"auto"===r&&(r=k),r){case"hex":y.value=n;break;case"rgb":y.value=(o=E,L.alpha&&1!==o.a?"rgba("+o.r+", "+o.g+", "+o.b+", "+o.a+")":"rgb("+o.r+", "+o.g+", "+o.b+")");break;case"hsl":y.value=(o=function(e){var t,a=e.v/100,l=a*(1-e.s/100/2);0
'+L.a11y.format+'
",p.body.appendChild(h),f=N("clr-color-area"),c=N("clr-color-marker"),i=N("clr-clear"),u=N("clr-color-preview"),y=N("clr-color-value"),b=N("clr-hue-slider"),l=N("clr-hue-marker"),g=N("clr-alpha-slider"),m=N("clr-alpha-marker"),S(L.el),T(L.el),O(h,"mousedown",function(e){h.classList.remove("clr-keyboard-nav"),e.stopPropagation()}),O(f,"mousedown",function(e){O(p,"mousemove",t)}),O(f,"touchstart",function(e){p.addEventListener("touchmove",t,{passive:!1})}),O(c,"mousedown",function(e){O(p,"mousemove",t)}),O(c,"touchstart",function(e){p.addEventListener("touchmove",t,{passive:!1})}),O(y,"change",function(e){A(y.value),r()}),O(i,"click",function(e){r(""),n()}),O(u,"click",function(e){r(),n()}),O(p,"click",".clr-format input",function(e){k=e.target.value,H(),r()}),O(h,"click",".clr-swatches button",function(e){A(e.target.textContent),r(),L.autoClose&&n()}),O(p,"mouseup",function(e){p.removeEventListener("mousemove",t)}),O(p,"touchend",function(e){p.removeEventListener("touchmove",t)}),O(p,"mousedown",function(e){h.classList.remove("clr-keyboard-nav"),n()}),O(p,"keydown",function(e){"Escape"===e.key?n(!0):"Tab"===e.key&&h.classList.add("clr-keyboard-nav")}),O(p,"click",".clr-field button",function(e){e.target.nextElementSibling.dispatchEvent(new Event("click",{bubbles:!0}))}),O(c,"keydown",function(e){var t={ArrowUp:[0,-1],ArrowDown:[0,1],ArrowLeft:[-1,0],ArrowRight:[1,0]};-1!==Object.keys(t).indexOf(e.key)&&(!function(e,t){e=+c.style.left.replace("px","")+e,t=+c.style.top.replace("px","")+t,c.style.left=e+"px",c.style.top=t+"px",C(e,t)}.apply(void 0,t[e.key]),e.preventDefault())}),O(f,"click",t),O(b,"input",e),O(g,"input",B)})}(window,document,Math); \ No newline at end of file diff --git a/static/colorpicker/css b/static/colorpicker/css new file mode 120000 index 0000000..3cf966e --- /dev/null +++ b/static/colorpicker/css @@ -0,0 +1 @@ +../../node_modules/bootstrap-colorpicker/dist/css \ No newline at end of file diff --git a/static/colorpicker/js b/static/colorpicker/js new file mode 120000 index 0000000..cc23749 --- /dev/null +++ b/static/colorpicker/js @@ -0,0 +1 @@ +../../node_modules/bootstrap-colorpicker/dist/js \ No newline at end of file diff --git a/static/css/bootstrap-icons.css b/static/css/bootstrap-icons.css new file mode 120000 index 0000000..996baca --- /dev/null +++ b/static/css/bootstrap-icons.css @@ -0,0 +1 @@ +../../node_modules/bootstrap-icons/font/bootstrap-icons.css \ No newline at end of file diff --git a/static/css/errorPage/style.css b/static/css/errorPage/style.css new file mode 100644 index 0000000..27a7671 --- /dev/null +++ b/static/css/errorPage/style.css @@ -0,0 +1,86 @@ +html, body { + height: 100%; + width: 100%; + margin: 0; + padding: 0; +} + +.container { + height: 100%; + width: 100%; + background-color: #0B161E; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-family: 'Segoe UI'; + text-align: center; +} + +.color-foreground { + color: #474849 !important; +} + +.text-shadow { + text-shadow: 1px 1px 2px; +} + +.rainbow { + -webkit-animation:rainbow 16s infinite; + -ms-animation:rainbow 16s infinite; + -o-animation:rainbow 16s infinite; + animation:rainbow 16s infinite; +} + +@-webkit-keyframes rainbow { +0% {color: #ff0000;} +10% {color: #ff8000;} +20% {color: #ffff00;} +30% {color: #80ff00;} +40% {color: #00ff00;} +50% {color: #00ff80;} +60% {color: #00ffff;} +70% {color: #0080ff;} +80% {color: #0000ff;} +90% {color: #8000ff;} +100% {color: #ff0080;} +} +@-ms-keyframes rainbow { +0% {color: #ff0000;} +10% {color: #ff8000;} +20% {color: #ffff00;} +30% {color: #80ff00;} +40% {color: #00ff00;} +50% {color: #00ff80;} +60% {color: #00ffff;} +70% {color: #0080ff;} +80% {color: #0000ff;} +90% {color: #8000ff;} +100% {color: #ff0080;} +} +@-o-keyframes rainbow { +0% {color: #ff0000;} +10% {color: #ff8000;} +20% {color: #ffff00;} +30% {color: #80ff00;} +40% {color: #00ff00;} +50% {color: #00ff80;} +60% {color: #00ffff;} +70% {color: #0080ff;} +80% {color: #0000ff;} +90% {color: #8000ff;} +100% {color: #ff0080;} +} +@keyframes rainbow { +0% {color: #ff0000;} +10% {color: #ff8000;} +20% {color: #ffff00;} +30% {color: #80ff00;} +40% {color: #00ff00;} +50% {color: #00ff80;} +60% {color: #00ffff;} +70% {color: #0080ff;} +80% {color: #0000ff;} +90% {color: #8000ff;} +100% {color: #ff0080;} +} \ No newline at end of file diff --git a/static/css/errorPage/styles.less b/static/css/errorPage/styles.less new file mode 100644 index 0000000..b2732f5 --- /dev/null +++ b/static/css/errorPage/styles.less @@ -0,0 +1,62 @@ +.glitch-wrapper { + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.glitch { + @offset1: 2px; + @offset2: -2px; + @highlight1: #49FC00; + @highlight2: spin(@highlight1, 180); + + color: white; + font-size: 150px; + text-transform: upercase; + position: relative; + display: inline-block; + + &::before, + &::after { + content: attr(data-text); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #0B161E; + } + + &::before { + left: @offset1; + text-shadow: -2px 0 @highlight1; + clip: rect(24px, 550px, 90px, 0); + animation: glitch-anim-2 3s infinite linear alternate-reverse; + } + + &::after { + left: @offset2; + text-shadow: -2px 0 @highlight2; + clip: rect(85px, 550px, 140px, 0); + animation: glitch-anim 2.5s infinite linear alternate-reverse; + } +} + +.glitch-frames (@n: 20, @index: 0) when (@index <= @n) { + @keyframeSel: percentage(@index/@n); + @rand1: unit(round(`Math.random()*150`),px); + @rand2: unit(round(`Math.random()*150`), px); + @{keyframeSel} { + clip: rect(@rand1, 9999px, @rand2, 0); + } + .glitch-frames(@n, (@index + 1)); +} + +@keyframes glitch-anim { + .glitch-frames(24); +} + +@keyframes glitch-anim-2 { + .glitch-frames(30,2); +} \ No newline at end of file diff --git a/static/css/fonts/bootstrap-icons.woff b/static/css/fonts/bootstrap-icons.woff new file mode 120000 index 0000000..6a1958f --- /dev/null +++ b/static/css/fonts/bootstrap-icons.woff @@ -0,0 +1 @@ +../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff \ No newline at end of file diff --git a/static/css/fonts/bootstrap-icons.woff2 b/static/css/fonts/bootstrap-icons.woff2 new file mode 120000 index 0000000..90ff4c9 --- /dev/null +++ b/static/css/fonts/bootstrap-icons.woff2 @@ -0,0 +1 @@ +../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff2 \ No newline at end of file diff --git a/static/css/mainStyle.css b/static/css/mainStyle.css new file mode 100644 index 0000000..2ae1f08 --- /dev/null +++ b/static/css/mainStyle.css @@ -0,0 +1,129 @@ +body { + min-height: 100vh; + min-height: -webkit-fill-available; + } + + html { + height: -webkit-fill-available; + } + + main { + display: flex; + flex-wrap: nowrap; + height: 100vh !important; + height: -webkit-fill-available; + max-height: 100vh; + overflow-x: auto; + overflow-y: hidden; + } + + .full .clr-field button { + width: 100%; + height: 100%; + border-radius: 5px; + } + + .b-example-divider { + flex-shrink: 0; + width: 1.5rem; + height: 100vh; + background-color: rgba(0, 0, 0, .1); + border: solid rgba(0, 0, 0, .15); + border-width: 1px 0; + box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15); + } + + .bi { + vertical-align: -.125em; + pointer-events: none; + fill: currentColor; + } + + .dropdown-toggle { outline: 0; } + + .nav-flush .nav-link { + border-radius: 0; + } + + .btn-toggle { + display: inline-flex; + align-items: center; + padding: .25rem .5rem; + font-weight: 600; + color: rgba(0, 0, 0, .65); + background-color: transparent; + border: 0; + } + .btn-toggle:hover, + .btn-toggle:focus { + color: rgba(0, 0, 0, .85); + background-color: #d2f4ea; + } + + .btn-toggle::before { + width: 1.25em; + line-height: 0; + content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e"); + transition: transform .35s ease; + transform-origin: .5em 50%; + } + + .btn-toggle[aria-expanded="true"] { + color: rgba(0, 0, 0, .85); + } + .btn-toggle[aria-expanded="true"]::before { + transform: rotate(90deg); + } + + .btn-toggle-nav a { + display: inline-flex; + padding: .1875rem .5rem; + margin-top: .125rem; + margin-left: 1.25rem; + text-decoration: none; + } + .btn-toggle-nav a:hover, + .btn-toggle-nav a:focus { + background-color: #d2f4ea; + } + + .scrollarea { + overflow-y: auto; + } + + .fw-semibold { font-weight: 600; } + .lh-tight { line-height: 1.25; } + + + .hidden{ + display: none; + } + + .pageC { + padding: 20px; + } + + +placeholder { + border: red 2px dashed; + background-color: yellow; +} + +pre { + overflow: auto; + word-wrap: normal; + white-space: pre; +} + +.hidden { + display: none !important; +} + +.trans { + transition: .5s +} + +.helperTip { + opacity: 0.8; + font-size: small; +} \ No newline at end of file diff --git a/static/css/smallerTextMod.css b/static/css/smallerTextMod.css index 1b5f4ab..211158b 100644 --- a/static/css/smallerTextMod.css +++ b/static/css/smallerTextMod.css @@ -1,3 +1,3 @@ body { - font-size:0.5em; + font-size:0.3em; } \ No newline at end of file diff --git a/static/css/styles.css b/static/css/styles.css index 5800c6c..953a446 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -103,4 +103,11 @@ html, body { .blink { animation:fade 1000ms infinite; -webkit-animation:fade 1000ms infinite; +} + +.connectionWarning{ + background-color: red; + width: 100vw; + font-size: x-large; + font-family: sans-serif; } \ No newline at end of file diff --git a/static/flatpickr/dist b/static/flatpickr/dist new file mode 120000 index 0000000..9c148d9 --- /dev/null +++ b/static/flatpickr/dist @@ -0,0 +1 @@ +../../node_modules/flatpickr/dist/ \ No newline at end of file diff --git a/static/js/cookie.js b/static/js/cookie.js new file mode 120000 index 0000000..9097170 --- /dev/null +++ b/static/js/cookie.js @@ -0,0 +1 @@ +../../node_modules/js-cookie/dist/js.cookie.min.js \ No newline at end of file diff --git a/static/js/countdown.js b/static/js/countdown.js deleted file mode 120000 index 71edd1e..0000000 --- a/static/js/countdown.js +++ /dev/null @@ -1 +0,0 @@ -../../node_modules/countdown/countdown.js \ No newline at end of file diff --git a/static/js/darkreader.js b/static/js/darkreader.js new file mode 120000 index 0000000..f810896 --- /dev/null +++ b/static/js/darkreader.js @@ -0,0 +1 @@ +../../node_modules/darkreader/darkreader.js \ No newline at end of file diff --git a/static/js/interface.js b/static/js/interface.js index 827a688..4822c9e 100644 --- a/static/js/interface.js +++ b/static/js/interface.js @@ -1,4 +1,529 @@ -function convertTimeOffset(){ +function convertTimeOffset() { selTime = new Date().getTime() + document.getElementById('time2').valueAsNumber document.getElementById("time").value = selTime } + + +function convertColorSegments(elementId) { + const raw = document.getElementById(elementId); + const segmentData = {} + for (elm in raw.children) { + if (raw.children[elm].nodeName == "TR") { + const child = raw.children[elm].children[1].children[0] + let timeVal = parseInt(raw.children[elm].children[0].children[0].value * 1000); + if (Number.isInteger(timeVal)) { + segmentData[timeVal] = child.style.color + } else { + segmentData["START"] = child.style.color + } + } + } + console.info(segmentData) + return (segmentData) +} + +$(function () { + // $(".numVal").bind("DOMSubtreeModified", alert); + + const modes = ["timer", "clock", "black", "test"] + let selectPresetTime = 0; + + if (Cookies.get("interfaceColor") != undefined) { + const color = Cookies.get("interfaceColor"); + $("#Mbtnradio" + (color))[0].checked = true + if (color == 1) { + DarkReader.disable() + } else if (color == 2) { + DarkReader.enable() + } else { + DarkReader.auto() + } + } + + saveOption("/api/v1/system", function systemInfoLoader(event) { + const dataSystem = JSON.parse(event.target.response) + document.getElementById("nodejsVers").innerHTML = dataSystem.nodeVersion + document.getElementById("nodeSwVers").innerHTML = dataSystem.systemVersion + + const tree2 = jsonview.create(dataSystem); + jsonview.render(tree2, document.getElementById("systemInfo")); + jsonview.expand(tree2); + }) + + $("#addRow").on("click", function (event) { + const tableEntryDom = document.getElementById("tableCopySource").cloneNode(true) + let temp = tableEntryDom.innerHTML + temp = temp.replace("#COLOR#", "gray"); + temp = temp.replace("#bg-COLOR#", "gray"); + temp = temp.replace("#VALUE#", 60); // BUG: Doesn't work but not too important + temp = temp.replace("timeValue-ID", Math.floor(Math.random() * 9999999)) + tableEntryDom.innerHTML = temp; + tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker") + tableEntryDom.id = Math.floor(Math.random() * 9999999) + document.getElementById("colors1").appendChild(tableEntryDom) + $(".deleteRow1").on("click", function removeRowEntry(event) { + $(event.target).closest("tr").remove(); + }); + $('.timeDurPicker').durationPicker({ + showSeconds: true, + showDays: false, + onChanged: function (newVal, test, val2) { + // $('#duration-label2').text(newVal); + val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value = newVal + //console.log(val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value) + //console.log(val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1]) + } + }); + }); + + $("#addRow2").on("click", function (event) { + const tableEntryDom = document.getElementById("tableCopySource").cloneNode(true) + let temp = tableEntryDom.innerHTML + temp = temp.replace("#COLOR#", "gray"); + temp = temp.replace("#bg-COLOR#", "gray"); + temp = temp.replace("#VALUE#", 60); // BUG: Doesn't work but not too important + temp = temp.replace("timeValue-ID", Math.floor(Math.random() * 9999999)) + tableEntryDom.innerHTML = temp; + tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker") + tableEntryDom.id = Math.floor(Math.random() * 9999999) + document.getElementById("colors2").appendChild(tableEntryDom) + $(".deleteRow1").on("click", function removeRowEntry(event) { + $(event.target).closest("tr").remove(); + }); + $('.timeDurPicker').durationPicker({ + showSeconds: true, + showDays: false, + onChanged: function (newVal, test, val2) { + // $('#duration-label2').text(newVal); + val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value = newVal + } + }); + }); + + + + // Restore settings + saveOption("/api/v1/data", function (event, xmlHttp) { + const jsonResult = JSON.parse(xmlHttp.response) + const tree = jsonview.create(jsonResult); + jsonview.render(tree, document.getElementById("responeSnippet")); + jsonview.expand(tree); + + // Restore mode radio + const currentModeInt = modes.indexOf(jsonResult.mode); + $("#btnradio" + (currentModeInt + 1))[0].checked = true + + // Restore layout settings + $("#showTime")[0].checked = jsonResult.showTimeOnCountdown; + $("#showMillis")[0].checked = jsonResult.showMilliSeconds; + $("#progBarShow")[0].checked = jsonResult.showProgressbar; + $("#textColors")[0].checked = jsonResult.enableColoredText; + console.log(document.getElementById("tableCopySource")) + + let tableEntryDom = document.getElementById("tableCopySource").cloneNode(true) + let currIndex = 0 + for (item in jsonResult.colorSegments) { + tableEntryDom = document.getElementById("tableCopySource").cloneNode(true) + let temp = tableEntryDom.innerHTML + temp = temp.replace("#COLOR#", jsonResult.colorSegments[item]); + temp = temp.replace("#bg-COLOR#", jsonResult.colorSegments[item]); + temp = temp.replace("timeValue-ID", "timeValue-1C" + currIndex) + + if (item != "START") { + temp = temp.replace("#VALUE#", item / 1000); + tableEntryDom.innerHTML = temp; + tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker") + } else { + tableEntryDom.innerHTML = temp; + tableEntryDom.children[2].children[0].children[0].disabled = true; + tableEntryDom.children[2].children[0].setAttribute("data-toggle", "tooltip") + tableEntryDom.children[2].children[0].setAttribute("data-placement", "right")// 'data-placement="right" title="Tooltip on right"' + tableEntryDom.children[2].children[0].setAttribute("title", "You cannot delete the start time") + + tableEntryDom.children[0].innerHTML = ' Start' + } + tableEntryDom.id = "1C" + currIndex + tableEntryDom.style.display = "table-row" + document.getElementById("colors1").appendChild(tableEntryDom) + currIndex++; + } + + + // Text colors + currIndex = 0 + for (item in jsonResult.textColors) { + tableEntryDom = document.getElementById("tableCopySource").cloneNode(true) + let temp = tableEntryDom.innerHTML + temp = temp.replace("#COLOR#", jsonResult.textColors[item]); + temp = temp.replace("#bg-COLOR#", jsonResult.textColors[item]); + temp = temp.replace("timeValue-ID", "timeValue-1C" + currIndex) + + if (item != "START") { + temp = temp.replace("#VALUE#", item / 1000); + tableEntryDom.innerHTML = temp; + tableEntryDom.firstChild.nextSibling.children[0].classList.add("timeDurPicker") + } else { + tableEntryDom.innerHTML = temp; + tableEntryDom.children[2].children[0].children[0].disabled = true; + tableEntryDom.children[2].children[0].setAttribute("data-toggle", "tooltip") + tableEntryDom.children[2].children[0].setAttribute("data-placement", "right")// 'data-placement="right" title="Tooltip on right"' + tableEntryDom.children[2].children[0].setAttribute("title", "You cannot delete the start time") + + tableEntryDom.children[0].innerHTML = ' Start' + } + + + // timeDurPicker + tableEntryDom.id = "1C" + currIndex + tableEntryDom.style.display = "table-row" + document.getElementById("colors2").appendChild(tableEntryDom) + currIndex++; + } + + + $('.timeDurPicker').durationPicker({ + showSeconds: true, + showDays: false, + onChanged: function (newVal, test, val2) { + val2.days[0].parentElement.parentElement.parentElement.parentElement.children[1].value = newVal + } + }); + + + + //console.debug(jsonResult, currentModeInt) + $('.colorPicky').on('colorpickerChange', function (event) { + event.target.parentElement.style.backgroundColor = event.target.value + }); + + $(".deleteRow1").on("click", function removeRowEntry(event) { + $(event.target).closest("tr").remove(); + }); + $('[data-toggle="tooltip"]').tooltip({ container: "body" }) + }) + + $("#copyColors").click(function CopyTextColors(event) { + event.target.innerHTML = '
' + saveOption("/api/v1/set/text/colors?copy=true", function finishCopyColors(event) { + setTimeout(function () { + document.getElementById("copyColors").innerHTML = "Copy from progressbar colors" + }, 500) + + }) + }); + + + $("input[name='btnradio2']").click(function (event) { + const darkid = parseInt(event.currentTarget.id.replace("Mbtnradio", "")) + if (darkid == 1) { + DarkReader.disable() + } else if (darkid == 2) { + DarkReader.enable() + } else { + DarkReader.auto() + } + Cookies.set("interfaceColor", darkid) + }); + + // Presets + $(".pres").click(function (event) { + currentTime = parseInt(event.currentTarget.value) + $("#customValue").data("durationPicker").setValue(currentTime / 1000) + }) + + + $(".goTimer").on("click", function (event) { + event.currentTarget.innerHTML = '
' + setTimeout(function () { + event.currentTarget.innerHTML = 'Go' + }, 200); + + saveOption("/api/v1/set/addMillisToTimer?time=" + currentTime, function (ev) { + }) + }) + + // Layout settings + $("#applyLayout").click(function (event) { + $("#applyLayout")[0].innerHTML = '
' + + // Collect all data, build all paths + const allPathes = []; + + const showTimeB = $("#showTime")[0].checked + const showMillisB = $("#showMillis")[0].checked + const progBarShowB = $("#progBarShow")[0].checked + const textColorsB = $("#textColors")[0].checked + + const colors = convertColorSegments("colors1") + const colors2 = convertColorSegments("colors2") + + allPathes.push("/api/v1/set/layout/showTime?show=" + showTimeB) + allPathes.push("/api/v1/set/layout/showMillis?show=" + showMillisB) + allPathes.push("/api/v1/set/progressbar/show?show=" + progBarShowB) + allPathes.push("/api/v1/set/text/enableColoring?enable=" + textColorsB) + allPathes.push("/api/v1/set/progressbar/colors?isBase64=true&colors=" + btoa(JSON.stringify(colors))) + allPathes.push("/api/v1/set/text/colors?isBase64=true&colors=" + btoa(JSON.stringify(colors2))) + + + for (pI in allPathes) { + const path = allPathes[pI]; + saveOption(path, function (event) { + console.debug(event) + }) + } + + setTimeout(function () { + $("#applyLayout")[0].innerHTML = 'Apply settings' + }, 500) + }) + + + $("#saveLayout").click(function (event) { + $("#saveLayout")[0].innerHTML = '
' + + // Collect all data, build all paths3 + const allPathes = []; + + const showTimeB = $("#showTime")[0].checked + const showMillisB = $("#showMillis")[0].checked + const progBarShowB = $("#progBarShow")[0].checked + const textColorsB = $("#textColors")[0].checked + + allPathes.push("/api/v1/set/layout/showTime?persist=true&show=" + showTimeB) + allPathes.push("/api/v1/set/layout/showMillis?persist=true&show=" + showMillisB) + allPathes.push("/api/v1/set/progressbar/show?persist=true&show=" + progBarShowB) + allPathes.push("/api/v1/set/text/enableColoring?persist=true&enable=" + textColorsB) + + for (pI in allPathes) { + const path = allPathes[pI]; + saveOption(path, function (event) { + console.debug(event) + }) + } + + saveOption("/api/v1/storage/commit", function (event) { + console.debug(event) + setTimeout(function () { + $("#saveLayout")[0].innerHTML = 'Save as startup settings (Layout only)' + }, 500) + }) + + + + }) + + + function msToTime(s, data) { + var ms = s % 1000; + s = (s - ms) / 1000; + var secs = s % 60; + s = (s - secs) / 60; + var mins = s % 60; + var hrs = (s - mins) / 60; + let out = "" + + return [ms, secs, mins, hrs]; + } + + // Timer custom + let currentTime = 0; + $("#timerHourInc").click(function (event) { + currentTime += 3600000 + const times = msToTime(currentTime) + $("#timerHoursV")[0].innerHTML = times[3]; + $("#timerMinuteV")[0].innerHTML = times[2]; + $("#timerSecondsV")[0].innerHTML = times[1]; + }) + + $("#timerHourDec").click(function (event) { + if (currentTime >= 3600000) { + currentTime -= 3600000 + const times = msToTime(currentTime) + $("#timerHoursV")[0].innerHTML = times[3]; + $("#timerMinuteV")[0].innerHTML = times[2]; + $("#timerSecondsV")[0].innerHTML = times[1]; + } + }) + + $("#timerMinuteInc").click(function (event) { + currentTime += 60000 + const times = msToTime(currentTime) + $("#timerHoursV")[0].innerHTML = times[3]; + $("#timerMinuteV")[0].innerHTML = times[2]; + $("#timerSecondsV")[0].innerHTML = times[1]; + }) + $("#timerMinuteDec").click(function (event) { + if (currentTime >= 60000) { + currentTime -= 60000 + const times = msToTime(currentTime) + $("#timerHoursV")[0].innerHTML = times[3]; + $("#timerMinuteV")[0].innerHTML = times[2]; + $("#timerSecondsV")[0].innerHTML = times[1]; + } + }) + $("#timerSecondsInc").click(function (event) { + currentTime += 1000 + const times = msToTime(currentTime) + $("#timerHoursV")[0].innerHTML = times[3]; + $("#timerMinuteV")[0].innerHTML = times[2]; + $("#timerSecondsV")[0].innerHTML = times[1]; + }) + $("#timerSecondsDec").click(function (event) { + if (currentTime >= 1000) { + currentTime -= 1000 + const times = msToTime(currentTime) + $("#timerHoursV")[0].innerHTML = times[3]; + $("#timerMinuteV")[0].innerHTML = times[2]; + $("#timerSecondsV")[0].innerHTML = times[1]; + } + }) + + $("input[name='btnradio']").click(function (event) { + $("#sendMessage")[0].innerHTML = '
' + let value = modes[parseInt(event.currentTarget.id.replace("btnradio", "")) - 1] + console.log(value, parseInt(event.currentTarget.id.replace("btnradio", ""))) + saveOption("/api/v1/set/mode?mode=" + value, function (event) { + setTimeout(function () { + $("#sendMessage")[0].innerHTML = '' + }, 200) + + }) + }) + + + $("#sendMessage").click(function (event) { + $("#sendMessage")[0].innerHTML = '
' + let value = $("#messageContent").val() + saveOption("/api/v1/ctrl/message/show?msg=" + value, function (event) { + setTimeout(function () { + $("#sendMessage")[0].innerHTML = '' + }, 200) + + }) + }) + + $("#ctrlRemoveMessage").click(function (event) { + $("#ctrlRemoveMessage")[0].innerHTML = '
' + saveOption("/api/v1/ctrl/message/hide", function (event) { + setTimeout(function () { + $("#ctrlRemoveMessage")[0].innerHTML = '' + }, 200) + + }) + }) + + $("#funcPlay").click(function (event) { + $("#funcPlay")[0].innerHTML = '
' + saveOption("/api/v1/ctrl/timer/play", function (event) { + setTimeout(function () { + $("#funcPlay")[0].innerHTML = '' + }, 200); + }) + }) + + + $("#funcPause").click(function (event) { + $("#funcPause")[0].innerHTML = '
' + + saveOption("/api/v1/ctrl/timer/pause", function (event) { + setTimeout(function () { + $("#funcPause")[0].innerHTML = '' + }, 200); + }) + }) + + $("#funcRestart").click(function (event) { + $("#funcRestart")[0].innerHTML = '
' + saveOption("/api/v1/ctrl/timer/restart", function (event) { + setTimeout(function () { + $("#funcRestart")[0].innerHTML = '' + }, 200) + }) + }) + + $("#applyDebug").click(function (event) { + $("#applyDebug")[0].innerHTML = '
' + let value = $("#debugModeEnable")[0].checked + saveOption("/api/v1/debug?enable=" + value, function (event) { + setTimeout(function () { + $("#applyDebug")[0].innerHTML = "Apply settings" + }, 200) + + }) + }) + + $("a.nav-link").click(function (event) { + event.preventDefault(); + $("a.nav-link").removeClass("active") + + event.currentTarget.classList.add("active") + + $(".pageC").addClass("hidden") + $("#" + event.target.href.split("#")[1]).removeClass("hidden") + // console.log(event.target.href.split("#")[1]) + }); + $("#customValue").durationPicker({ + showSeconds: true, + showDays: false, + onChanged: function (newVal, test, val2) { + currentTime = newVal * 1000 + } + }) + + flatty = flatpickr("#datetimetester", { + enableTime: true, + time_24hr: true, + dateFormat: "H:i d.m.Y", + }); + + $(".goTimeGoalCountdown").on("click", function handleCountdownToTime() { + const selectTime = flatty.selectedDates[0].getTime() + const timeDiff = selectTime - new Date().getTime() + $(".goTimeGoalCountdown")[0].innerHTML = '
' + saveOption("/api/v1/set/addMillisToTimer?time=" + timeDiff, function (ev) { + setTimeout(function () { + $(".goTimeGoalCountdown")[0].innerHTML = '' + }, 200); + }) + }) +}); + +function saveOption(path, callback) { + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", path, true); // false for synchronous request + xmlHttp.send(null); + xmlHttp.onerror = function (e) { + console.log(e); // ToDo: Popup, etc. + }; + xmlHttp.onloadend = function (event) { + callback(event, xmlHttp) + } +} + +navStatus = true +function openNav() { + document.getElementById("navbarToggleExternalContent").style.width = "250px"; + document.getElementById("navbarToggleExternalContent").style.opacity = "1"; + document.getElementById("navbarToggleExternalContent").style.display = "block"; + document.getElementById("navbarToggleExternalContent").style.zIndex = 999999; + document.getElementById("pageCont").style.marginLeft = "0px"; + navStatus = true +} + +function closeNav() { + document.getElementById("navbarToggleExternalContent").style.width = "0px"; + document.getElementById("navbarToggleExternalContent").style.opacity = "0"; + document.getElementById("navbarToggleExternalContent").style.display = "none"; + document.getElementById("navbarToggleExternalContent").style.zIndex = -999999; + document.getElementById("pageCont").style.marginLeft = "0"; + navStatus = false +} + +function toogleNav() { + if (navStatus) { + closeNav() + } else { + openNav() + } + +} \ No newline at end of file diff --git a/static/js/jquery.min.js b/static/js/jquery.min.js new file mode 120000 index 0000000..08ac9f2 --- /dev/null +++ b/static/js/jquery.min.js @@ -0,0 +1 @@ +../../node_modules/jquery/dist/jquery.min.js \ No newline at end of file diff --git a/static/js/jsonview.js b/static/js/jsonview.js new file mode 100644 index 0000000..1ac71b4 --- /dev/null +++ b/static/js/jsonview.js @@ -0,0 +1 @@ +!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.jsonview=n():e.jsonview=n()}(self,(function(){return(()=>{"use strict";var e={767:(e,n,t)=>{t.d(n,{Z:()=>s});var r=t(81),o=t.n(r),i=t(645),a=t.n(i)()(o());a.push([e.id,'.json-container{font-family:"Open Sans";font-size:16px;background-color:#fff;color:gray;box-sizing:border-box}.json-container .line{margin:4px 0;display:flex;justify-content:flex-start}.json-container .caret-icon{width:18px;text-align:center;cursor:pointer}.json-container .empty-icon{width:18px}.json-container .json-type{margin-right:4px;margin-left:4px}.json-container .json-key{color:#444;margin-right:4px;margin-left:4px}.json-container .json-index{margin-right:4px;margin-left:4px}.json-container .json-value{margin-left:8px}.json-container .json-number{color:#f9ae58}.json-container .json-boolean{color:#ec5f66}.json-container .json-string{color:#86b25c}.json-container .json-size{margin-right:4px;margin-left:4px}.json-container .hidden{display:none}.json-container .fas{display:inline-block;border-style:solid;width:0;height:0}.json-container .fa-caret-down{border-width:6px 5px 0 5px;border-color:gray transparent}.json-container .fa-caret-right{border-width:5px 0 5px 6px;border-color:transparent transparent transparent gray}',""]);const s=a},645:e=>{e.exports=function(e){var n=[];return n.toString=function(){return this.map((function(n){var t="",r=void 0!==n[5];return n[4]&&(t+="@supports (".concat(n[4],") {")),n[2]&&(t+="@media ".concat(n[2]," {")),r&&(t+="@layer".concat(n[5].length>0?" ".concat(n[5]):""," {")),t+=e(n),r&&(t+="}"),n[2]&&(t+="}"),n[4]&&(t+="}"),t})).join("")},n.i=function(e,t,r,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var a={};if(r)for(var s=0;s0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=i),t&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=t):d[2]=t),o&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=o):d[4]="".concat(o)),n.push(d))}},n}},81:e=>{e.exports=function(e){return e[1]}},379:e=>{var n=[];function t(e){for(var t=-1,r=0;r{var n={};e.exports=function(e,t){var r=function(e){if(void 0===n[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}n[e]=t}return n[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(t)}},216:e=>{e.exports=function(e){var n=document.createElement("style");return e.setAttributes(n,e.attributes),e.insert(n,e.options),n}},565:(e,n,t)=>{e.exports=function(e){var n=t.nc;n&&e.setAttribute("nonce",n)}},795:e=>{e.exports=function(e){var n=e.insertStyleElement(e);return{update:function(t){!function(e,n,t){var r="";t.supports&&(r+="@supports (".concat(t.supports,") {")),t.media&&(r+="@media ".concat(t.media," {"));var o=void 0!==t.layer;o&&(r+="@layer".concat(t.layer.length>0?" ".concat(t.layer):""," {")),r+=t.css,o&&(r+="}"),t.media&&(r+="}"),t.supports&&(r+="}");var i=t.sourceMap;i&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(i))))," */")),n.styleTagTransform(r,e,n.options)}(n,e,t)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)}}}},589:e=>{e.exports=function(e,n){if(n.styleSheet)n.styleSheet.cssText=e;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(e))}}}},n={};function t(r){var o=n[r];if(void 0!==o)return o.exports;var i=n[r]={id:r,exports:{}};return e[r](i,i.exports,t),i.exports}t.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},t.d=(e,n)=>{for(var r in n)t.o(n,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},t.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var r={};return(()=>{t.r(r),t.d(r,{collapse:()=>$,create:()=>O,default:()=>I,destroy:()=>z,expand:()=>P,render:()=>A,renderJSON:()=>N});var e=t(379),n=t.n(e),o=t(795),i=t.n(o),a=t(569),s=t.n(a),c=t(565),l=t.n(c),d=t(216),p=t.n(d),u=t(589),f=t.n(u),v=t(767),y={};function h(e){return Array.isArray(e)?"array":null===e?"null":typeof e}function m(e){return document.createElement(e)}y.styleTagTransform=f(),y.setAttributes=l(),y.insert=s().bind(null,"head"),y.domAPI=i(),y.insertStyleElement=p(),n()(v.Z,y),v.Z&&v.Z.locals&&v.Z.locals;const x="hidden",g="fa-caret-right",j="fa-caret-down";function b(e){e.children.forEach((e=>{e.el.classList.add(x),e.isExpanded&&b(e)}))}function E(e){e.children.forEach((e=>{e.el.classList.remove(x),e.isExpanded&&E(e)}))}function S(e){if(e.children.length>0){const n=e.el.querySelector(".fas");n&&n.classList.replace(g,j)}}function k(e){if(e.children.length>0){const n=e.el.querySelector(".fas");n&&n.classList.replace(j,g)}}function w(e){e.isExpanded?(e.isExpanded=!1,k(e),b(e)):(e.isExpanded=!0,S(e),E(e))}function L(e,n){n(e),e.children.length>0&&e.children.forEach((e=>{L(e,n)}))}function T(e={}){return{key:e.key||null,parent:e.parent||null,value:e.hasOwnProperty("value")?e.value:null,isExpanded:e.isExpanded||!1,type:e.type||null,children:e.children||[],el:e.el||null,depth:e.depth||0,dispose:null}}function M(e,n){if("object"==typeof e)for(let t in e){const r=T({value:e[t],key:t,depth:n.depth+1,type:h(e[t]),parent:n});n.children.push(r),M(e[t],r)}}function C(e){return"string"==typeof e?JSON.parse(e):e}function O(e){const n=C(e),t=T({value:n,key:h(n),type:h(n)});return M(n,t),t}function N(e,n){const t=C(e),r=createTree(t);return A(r,n),r}function A(e,n){const t=function(){const e=m("div");return e.className="json-container",e}();L(e,(function(e){e.el=function(e){let n=m("div");const t=e=>{const n=e.children.length;return"array"===e.type?`[${n}]`:"object"===e.type?`{${n}}`:null};if(e.children.length>0){n.innerHTML=function(e={}){const{key:n,size:t}=e;return`\n
\n
\n
${n}
\n
${t}
\n
\n `}({key:e.key,size:t(e)});const r=n.querySelector(".caret-icon");e.dispose=function(e,n,t){return e.addEventListener(n,t),()=>e.removeEventListener(n,t)}(r,"click",(()=>w(e)))}else n.innerHTML=function(e={}){const{key:n,value:t,type:r}=e;return`\n
\n
\n
${n}
\n
:
\n
${t}
\n
\n `}({key:e.key,value:e.value,type:typeof e.value});const r=n.children[0];return null!==e.parent&&r.classList.add(x),r.style="margin-left: "+18*e.depth+"px;",r}(e),t.appendChild(e.el)})),n.appendChild(t)}function P(e){L(e,(function(e){e.el.classList.remove(x),e.isExpanded=!0,S(e)}))}function $(e){L(e,(function(n){n.isExpanded=!1,n.depth>e.depth&&n.el.classList.add(x),k(n)}))}function z(e){var n;L(e,(e=>{e.dispose&&e.dispose()})),(n=e.el.parentNode).parentNode.removeChild(n)}const I={render:A,create:O,renderJSON:N,expand:P,collapse:$,traverse:L,destroy:z}})(),r})()})); \ No newline at end of file diff --git a/static/js/less.min.js b/static/js/less.min.js new file mode 120000 index 0000000..9ac4459 --- /dev/null +++ b/static/js/less.min.js @@ -0,0 +1 @@ +../../node_modules/less/dist/less.min.js \ No newline at end of file diff --git a/static/js/reconnecting-websocket.min.js b/static/js/reconnecting-websocket.min.js new file mode 100644 index 0000000..3015099 --- /dev/null +++ b/static/js/reconnecting-websocket.min.js @@ -0,0 +1 @@ +!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a}); diff --git a/static/js/script.js b/static/js/script.js index 88211bb..fd06ada 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -1,14 +1,68 @@ var newDateObj = new Date(); newDateObj = new Date(newDateObj.getTime() + 1000 * 20) +backReqInt = 0; +websocketFailed = false +recoveryAttempts = 0; + +let socket = new ReconnectingWebSocket("ws://localhost:" + location.port); +let ackdSessionToken = false +let isFirstPacket = true + +let lastTime = "00:00:00"; +let timerCountdownFirst = true; + +socket.onopen = function (e) { + // alert("[open] Connection established"); + //alert("Sending to server"); + socket.send("new client"); +}; + +socket.onmessage = function (event) { + // alert(`[message] Data received from server: ${event.data}`); + let inData = JSON.parse(event.data) + if (isFirstPacket) { + isFirstPacket = false + dataFame = JSON.parse(event.data); + timeDiff = new Date().getTime() - dataFame.srvTime + } else { + if (inData.sessionToken == dataFame.sessionToken) { + dataFame = JSON.parse(event.data); + timeDiff = new Date().getTime() - dataFame.srvTime + } else { + if (ackdSessionToken == false) { + + ackdSessionToken = true + + if (confirm("Session token mismatch, reload the page?")) { + location.reload(); + } + } + } + } + +}; + +socket.onclose = function (event) { + if (event.wasClean) { + console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); + } else { + // e.g. server process killed or network down + // event.code is usually 1006 in this case + console.error('[close] Connection died'); + } +}; + allowFullscreen = true +let dataFame = {} +let timeDiff = 0 function enterFullscreen(element) { if (element.requestFullscreen) { element.requestFullscreen(); - } else if (element.msRequestFullscreen) { // for IE11 (remove June 15, 2022) + } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); - } else if (element.webkitRequestFullscreen) { // iOS Safari + } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } } @@ -28,7 +82,6 @@ function updateFullscreen() { } - function msToTime(s, data) { isSmallerThenZero = false if (s < 0) { @@ -69,93 +122,136 @@ function findProgessColor(colors, value) { return (resColor) } -function handleUpdate() { +function requestBackend() { resp = httpGet("/api/v1/data"); - resp.onloadend = function (e) { - if(resp.status == 200){ + if (resp.status == 200) { resp = resp.responseText; var data = JSON.parse(resp); - document.getElementById("incomeData").innerHTML = JSON.stringify(data) - document.getElementById("timediff").innerHTML = new Date().getTime() - data.srvTime; - if(data.debug){ - document.getElementById("timediff").style.display = "block"; - }else{ - document.getElementById("timediff").style.display = "none"; - } - - if (data.defaultFullScreen && document.getElementById("initalDate").innerHTML.includes("true") && allowFullscreen) { - enterFullscreen(document.documentElement); - document.getElementById("initalDate").innerHTML = "false" - } - - if (data.showMessage) { - document.getElementById("overlay").style.display = "block"; - document.getElementById("text").innerHTML = data.message - } else { - off() - } - - if (new Date().getTime() - data.messageAppearTime < 5000) { - if (!document.getElementById("text").classList.contains('blink')) { - document.getElementById("text").classList.add("blink") - } - } else { - if (document.getElementById("text").classList.contains('blink')) { - document.getElementById("text").classList.remove("blink") - } - } - - - if (data.mode == "clock") { - document.getElementById("timer").innerHTML = getTime(); - document.getElementById("testImg").style.display = "none"; - document.getElementById("wholeProgBar").style.display = "none"; - - } else if (data.mode == "timer") { - - document.getElementById("wholeProgBar").style.display = "block"; - - if (data.timerRunState) { - const now = new Date() - const diff = data.countdownGoal - now.getTime() - document.getElementById("timer").innerHTML = msToTime(diff, data); - if (diff > 0) { - document.getElementById("progBar").style.width = percentage(diff, data.timeAmountInital) + "%"; - } else { - document.getElementById("progBar").style.width = "0%"; - } - - document.getElementById("progBar").style.backgroundColor = findProgessColor(data.colorSegments, diff) - document.getElementById("testImg").style.display = "none"; - if (data.enableColoredText) { - document.getElementById("timer").style.color = findProgessColor(data.textColors, diff) - } else { - document.getElementById("timer").style.color = "white" - } - - } - if (data.showTimeOnCountdown) { - document.getElementById("clockSec").innerHTML = getTime(); - } else { - document.getElementById("clockSec").innerHTML = ""; - } - - } else if (data.mode == "black") { - document.getElementById("timer").innerHTML = ""; - document.getElementById("testImg").style.display = "none"; - document.getElementById("wholeProgBar").style.display = "none"; - - } else if (data.mode == "test") { - document.getElementById("timer").innerHTML = ""; - document.getElementById("testImg").style.display = "block"; - document.getElementById("progBar").style.display = "none"; - } + timeDiff = new Date().getTime() - data.srvTime + dataFame = data; + } + } +} + +let isSlowed = false + +function handleRecovery() { + var img = document.body.appendChild(document.createElement("img")); + img.onload = function () { + location.reload(); + }; + img.src = "SMPTE_Color_Bars.svg"; +} + +let recoInter = -1 + +function handleUpdate() { + var data = dataFame; + document.getElementById("incomeData").innerHTML = JSON.stringify(data) + document.getElementById("timediff").innerHTML = timeDiff + "
" + String(new Date().getTime() - data.srvTime); + + if (data.debug) { + document.getElementById("timediff").style.display = "block"; + } else { + document.getElementById("timediff").style.display = "none"; + } + + if (data.defaultFullScreen && document.getElementById("initalDate").innerHTML.includes("true") && allowFullscreen) { + enterFullscreen(document.documentElement); + document.getElementById("initalDate").innerHTML = "false" + } + + if (data.showMessage) { + document.getElementById("overlay").style.display = "block"; + document.getElementById("text").innerHTML = data.message + } else { + document.getElementById("overlay").style.display = "none"; + } + + if (data.showTimeOnCountdown && data.mode == "timer") { + document.getElementById("clockSec").innerHTML = getTime(); + } else { + document.getElementById("clockSec").innerHTML = ""; + } + + if (new Date().getTime() - data.messageAppearTime < 5000) { + if (!document.getElementById("text").classList.contains('blink')) { + document.getElementById("text").classList.add("blink") + } + } else { + if (document.getElementById("text").classList.contains('blink')) { + document.getElementById("text").classList.remove("blink") } - } + if (data.mode == "clock") { + document.getElementById("timer").innerHTML = getTime(); + document.getElementById("testImg").style.display = "none"; + document.getElementById("wholeProgBar").style.display = "none"; + document.getElementById("clockSec").innerHTML = ""; + document.getElementById("timer").style.color = "white" + timerCountdownFirst = true; + + } else if (data.mode == "timer") { + document.getElementById("wholeProgBar").style.display = "block"; + const now = new Date() + + if(timerCountdownFirst){ + const diff = data.countdownGoal - now.getTime() + timerCountdownFirst = false + document.getElementById("timer").innerHTML = msToTime(diff, data); + if (diff > 0) { + document.getElementById("progBar").style.width = percentage(diff, data.timeAmountInital) + "%"; + } else { + document.getElementById("progBar").style.width = "0%"; + } + + document.getElementById("progBar").style.backgroundColor = findProgessColor(data.colorSegments, diff) + document.getElementById("testImg").style.display = "none"; + if (data.enableColoredText) { + document.getElementById("timer").style.color = findProgessColor(data.textColors, diff) + } else { + document.getElementById("timer").style.color = "white" + } + } + + if (data.timerRunState) { + const diff = data.countdownGoal - now.getTime() + document.getElementById("timer").innerHTML = msToTime(diff, data); + lastTime = msToTime(diff, data); + if (diff > 0) { + document.getElementById("progBar").style.width = percentage(diff, data.timeAmountInital) + "%"; + } else { + document.getElementById("progBar").style.width = "0%"; + } + + document.getElementById("progBar").style.backgroundColor = findProgessColor(data.colorSegments, diff) + document.getElementById("testImg").style.display = "none"; + if (data.enableColoredText) { + document.getElementById("timer").style.color = findProgessColor(data.textColors, diff) + } else { + document.getElementById("timer").style.color = "white" + } + + } else { + document.getElementById("timer").innerHTML = lastTime + } + } else if (data.mode == "black") { + timerCountdownFirst = true; + document.getElementById("timer").innerHTML = ""; + document.getElementById("testImg").style.display = "none"; + document.getElementById("wholeProgBar").style.display = "none"; + document.getElementById("clockSec").innerHTML = ""; + + } else if (data.mode == "test") { + timerCountdownFirst = true; + document.getElementById("timer").innerHTML = ""; + document.getElementById("testImg").style.display = "block"; + document.getElementById("progBar").style.display = "none"; + document.getElementById("clockSec").innerHTML = ""; + } } function httpGet(theUrl) { @@ -182,15 +278,9 @@ function getTime() { return time; } -function on() { - document.getElementById("overlay").style.display = "block"; -} -function off() { - document.getElementById("overlay").style.display = "none"; -} - -setInterval(handleUpdate, 200); +// setInterval(requestBackend, 500); +updateInter = setInterval(handleUpdate, 2); let temp = new URLSearchParams(window.location.search).get("smaller"); diff --git a/static/mdbootstrap/css b/static/mdbootstrap/css new file mode 120000 index 0000000..5ada75b --- /dev/null +++ b/static/mdbootstrap/css @@ -0,0 +1 @@ +../../node_modules/mdbootstrap/css/ \ No newline at end of file diff --git a/static/mdbootstrap/js b/static/mdbootstrap/js new file mode 120000 index 0000000..6a0f520 --- /dev/null +++ b/static/mdbootstrap/js @@ -0,0 +1 @@ +../../node_modules/mdbootstrap/js \ No newline at end of file diff --git a/templates/adminPanel.html b/templates/adminPanel.html deleted file mode 100644 index 3e474ac..0000000 --- a/templates/adminPanel.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - openCountdown - Admin - - - - - - - - - -
- - -
- -
-
- - -
- -
- - -
- -
- - - -
- -
- - -
- - Play controls: -
- -
-
- -
-
- -
- - Message: -
- - -
-
- -
- - - - - - - - \ No newline at end of file diff --git a/templates/errorPages/404.html b/templates/errorPages/404.html new file mode 100644 index 0000000..edbc836 --- /dev/null +++ b/templates/errorPages/404.html @@ -0,0 +1,47 @@ + + + + + + + + openCountdown - Not found + + + + + + + + + + + + + + + + + +
+

+
+ 404 +
+

+

+ We're sorry, the page you were looking for isn't found here.
+ The link you followed may either be broken or no longer exists. Please + check your spelling.
+ + Back home +

+ +
+ + + \ No newline at end of file diff --git a/templates/newAdminPanel.html b/templates/newAdminPanel.html new file mode 100644 index 0000000..6282241 --- /dev/null +++ b/templates/newAdminPanel.html @@ -0,0 +1,434 @@ + + + + + + + + openCountdown - Admin + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + +

Home page

+ +
+
+
+

Preview + + +
+ + + + + + + +
+

+ +
+
+

Mode + +

+
+ + + + + + + + + + + +
+
+
+
+ + + + + + +
+
+
+

Messaging + +

+ + + +
+ + +
+
+
+


+

Timer

+
+ + +
+ + + + + + + + + + + + +
+ + +
+ + + +
+
+
+ Countdown to time +
+
+ + + + + + +
+ +
+
+
+ + + + + + + + + +
+
+ + + + + + + \ No newline at end of file diff --git a/templates/timerPage.html b/templates/timerPage.html index a08a0a1..faa3e30 100644 --- a/templates/timerPage.html +++ b/templates/timerPage.html @@ -11,14 +11,21 @@ + + +
Message here
+ + @@ -39,7 +46,7 @@ - +