Merge branch 'ui-rework'
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,3 @@ | ||||
| node_modules | ||||
| rename.sh | ||||
| data-persistence.json | ||||
|   | ||||
							
								
								
									
										17
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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": [ | ||||
|                 "<node_internals>/**" | ||||
|             ], | ||||
|             "program": "${workspaceFolder}/index.js" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										165
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
|                    GNU LESSER GENERAL PUBLIC LICENSE | ||||
|                        Version 3, 29 June 2007 | ||||
|  | ||||
|  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> | ||||
|  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. | ||||
| @@ -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 | ||||
							
								
								
									
										32
									
								
								helpers.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								helpers.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 }; | ||||
							
								
								
									
										207
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										207
									
								
								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'); | ||||
|   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) { | ||||
|  | ||||
|   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'); | ||||
|   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); | ||||
|     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; | ||||
|       currentState.textColors = currentState.colorSegments | ||||
|       res.json({ status: "ok" }); | ||||
|     } else { | ||||
|       currentState.textColors = JSON.parse(req.query.colors); | ||||
|       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"); | ||||
|   | ||||
							
								
								
									
										387
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										387
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -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": {} | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										15
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								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" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										1
									
								
								static/bootstrap-duration-picker
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/bootstrap-duration-picker
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../node_modules/bootstrap-duration-picker/dist/ | ||||
							
								
								
									
										1
									
								
								static/bootstrap/dist
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/bootstrap/dist
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/bootstrap/dist | ||||
							
								
								
									
										1
									
								
								static/coloris/coloris.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/coloris/coloris.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								static/coloris/coloris.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								static/coloris/coloris.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/colorpicker/css
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/colorpicker/css
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/bootstrap-colorpicker/dist/css | ||||
							
								
								
									
										1
									
								
								static/colorpicker/js
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/colorpicker/js
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/bootstrap-colorpicker/dist/js | ||||
							
								
								
									
										1
									
								
								static/css/bootstrap-icons.css
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/css/bootstrap-icons.css
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/bootstrap-icons/font/bootstrap-icons.css | ||||
							
								
								
									
										86
									
								
								static/css/errorPage/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								static/css/errorPage/style.css
									
									
									
									
									
										Normal file
									
								
							| @@ -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;} | ||||
| } | ||||
							
								
								
									
										62
									
								
								static/css/errorPage/styles.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								static/css/errorPage/styles.less
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
| } | ||||
							
								
								
									
										1
									
								
								static/css/fonts/bootstrap-icons.woff
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/css/fonts/bootstrap-icons.woff
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff | ||||
							
								
								
									
										1
									
								
								static/css/fonts/bootstrap-icons.woff2
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/css/fonts/bootstrap-icons.woff2
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../../node_modules/bootstrap-icons/font/fonts/bootstrap-icons.woff2 | ||||
							
								
								
									
										129
									
								
								static/css/mainStyle.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								static/css/mainStyle.css
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
| } | ||||
| @@ -1,3 +1,3 @@ | ||||
| body { | ||||
|     font-size:0.5em; | ||||
|     font-size:0.3em; | ||||
| } | ||||
| @@ -104,3 +104,10 @@ html, body { | ||||
| animation:fade 1000ms infinite; | ||||
| -webkit-animation:fade 1000ms infinite; | ||||
| } | ||||
|  | ||||
| .connectionWarning{ | ||||
|   background-color: red; | ||||
|   width: 100vw; | ||||
|   font-size: x-large; | ||||
|   font-family: sans-serif; | ||||
| } | ||||
							
								
								
									
										1
									
								
								static/flatpickr/dist
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/flatpickr/dist
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/flatpickr/dist/ | ||||
							
								
								
									
										1
									
								
								static/js/cookie.js
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/js/cookie.js
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/js-cookie/dist/js.cookie.min.js | ||||
| @@ -1 +0,0 @@ | ||||
| ../../node_modules/countdown/countdown.js | ||||
							
								
								
									
										1
									
								
								static/js/darkreader.js
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/js/darkreader.js
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/darkreader/darkreader.js | ||||
| @@ -2,3 +2,528 @@ 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 = '<i class="bi bi-flag-fill"></i> 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 = '<i class="bi bi-flag-fill"></i> 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 = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         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 = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         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 = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|  | ||||
|         // 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 = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|  | ||||
|         // 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 = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         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 = '<i class="bi bi-send"></i>' | ||||
|             }, 200) | ||||
|  | ||||
|         }) | ||||
|     }) | ||||
|  | ||||
|  | ||||
|     $("#sendMessage").click(function (event) { | ||||
|         $("#sendMessage")[0].innerHTML = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         let value = $("#messageContent").val() | ||||
|         saveOption("/api/v1/ctrl/message/show?msg=" + value, function (event) { | ||||
|             setTimeout(function () { | ||||
|                 $("#sendMessage")[0].innerHTML = '<i class="bi bi-send"></i>' | ||||
|             }, 200) | ||||
|  | ||||
|         }) | ||||
|     }) | ||||
|  | ||||
|     $("#ctrlRemoveMessage").click(function (event) { | ||||
|         $("#ctrlRemoveMessage")[0].innerHTML = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         saveOption("/api/v1/ctrl/message/hide", function (event) { | ||||
|             setTimeout(function () { | ||||
|                 $("#ctrlRemoveMessage")[0].innerHTML = '<i class="bi bi-eye-slash-fill"></i>' | ||||
|             }, 200) | ||||
|  | ||||
|         }) | ||||
|     }) | ||||
|  | ||||
|     $("#funcPlay").click(function (event) { | ||||
|         $("#funcPlay")[0].innerHTML = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         saveOption("/api/v1/ctrl/timer/play", function (event) { | ||||
|             setTimeout(function () { | ||||
|                 $("#funcPlay")[0].innerHTML = '<i class="bi bi-play-fill"></i>' | ||||
|             }, 200); | ||||
|         }) | ||||
|     }) | ||||
|  | ||||
|  | ||||
|     $("#funcPause").click(function (event) { | ||||
|         $("#funcPause")[0].innerHTML = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|  | ||||
|         saveOption("/api/v1/ctrl/timer/pause", function (event) { | ||||
|             setTimeout(function () { | ||||
|                 $("#funcPause")[0].innerHTML = '<i class="bi bi-pause"></i>' | ||||
|             }, 200); | ||||
|         }) | ||||
|     }) | ||||
|  | ||||
|     $("#funcRestart").click(function (event) { | ||||
|         $("#funcRestart")[0].innerHTML = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         saveOption("/api/v1/ctrl/timer/restart", function (event) { | ||||
|             setTimeout(function () { | ||||
|                 $("#funcRestart")[0].innerHTML = '<i class="bi bi-arrow-clockwise"></i>' | ||||
|             }, 200) | ||||
|         }) | ||||
|     }) | ||||
|  | ||||
|     $("#applyDebug").click(function (event) { | ||||
|         $("#applyDebug")[0].innerHTML = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         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 = '<div class="spinner-border-sm spinner-border"></div>' | ||||
|         saveOption("/api/v1/set/addMillisToTimer?time=" + timeDiff, function (ev) { | ||||
|             setTimeout(function () { | ||||
|                 $(".goTimeGoalCountdown")[0].innerHTML = '<i class="bi bi-check2-circle"></i>' | ||||
|             }, 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() | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										1
									
								
								static/js/jquery.min.js
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/js/jquery.min.js
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/jquery/dist/jquery.min.js | ||||
							
								
								
									
										1
									
								
								static/js/jsonview.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/js/jsonview.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								static/js/less.min.js
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/js/less.min.js
									
									
									
									
										vendored
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/less/dist/less.min.js | ||||
							
								
								
									
										1
									
								
								static/js/reconnecting-websocket.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/js/reconnecting-websocket.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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}); | ||||
| @@ -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,15 +122,35 @@ function findProgessColor(colors, value) { | ||||
|   return (resColor) | ||||
| } | ||||
|  | ||||
| function handleUpdate() { | ||||
| function requestBackend() { | ||||
|   resp = httpGet("/api/v1/data"); | ||||
|  | ||||
|   resp.onloadend = function (e) { | ||||
|     if (resp.status == 200) { | ||||
|       resp = resp.responseText; | ||||
|       var data = JSON.parse(resp); | ||||
|       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 = new Date().getTime() - data.srvTime; | ||||
|   document.getElementById("timediff").innerHTML = timeDiff + "<br>" + String(new Date().getTime() - data.srvTime); | ||||
|   | ||||
|   if (data.debug) { | ||||
|     document.getElementById("timediff").style.display = "block"; | ||||
|   } else { | ||||
| @@ -93,7 +166,13 @@ function handleUpdate() { | ||||
|     document.getElementById("overlay").style.display = "block"; | ||||
|     document.getElementById("text").innerHTML = data.message | ||||
|   } else { | ||||
|         off() | ||||
|     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) { | ||||
| @@ -111,14 +190,17 @@ function handleUpdate() { | ||||
|     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"; | ||||
|    | ||||
|         if (data.timerRunState) { | ||||
|     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) + "%"; | ||||
| @@ -133,31 +215,45 @@ function handleUpdate() { | ||||
|       } else { | ||||
|         document.getElementById("timer").style.color = "white" | ||||
|       } | ||||
|    | ||||
|     } | ||||
|         if (data.showTimeOnCountdown) { | ||||
|           document.getElementById("clockSec").innerHTML = getTime(); | ||||
|  | ||||
|     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("clockSec").innerHTML = ""; | ||||
|         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) { | ||||
|   var xmlHttp = new XMLHttpRequest(); | ||||
|   xmlHttp.open("GET", theUrl, true); // false for synchronous request | ||||
| @@ -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"); | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								static/mdbootstrap/css
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/mdbootstrap/css
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/mdbootstrap/css/ | ||||
							
								
								
									
										1
									
								
								static/mdbootstrap/js
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								static/mdbootstrap/js
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| ../../node_modules/mdbootstrap/js | ||||
| @@ -1,94 +0,0 @@ | ||||
| <!doctype html> | ||||
|  | ||||
| <html lang="en"> | ||||
|  | ||||
| <head> | ||||
|   <meta charset="utf-8"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|  | ||||
|   <title>openCountdown - Admin</title> | ||||
|   <meta name="description" content="openCountdown"> | ||||
|   <meta name="author" content="TheGreydiamond"> | ||||
|  | ||||
|   <link rel="stylesheet" href="css/styles.css?v=1.1"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|  | ||||
|  | ||||
|   <form action="/api/v1/set/mode" target="hiddenFrame"> | ||||
|     <select id="mode" name="mode"> | ||||
|       <option value="timer">Timer</option> | ||||
|       <option value="clock">Clock</option> | ||||
|       <option value="black">Black</option> | ||||
|       <option value="test">Test</option> | ||||
|     </select> | ||||
|     <button type="submit">Submit</button> | ||||
|   </form> | ||||
|  | ||||
|   <br> | ||||
|   <form action="/api/v1/set/layout/showMillis" target="hiddenFrame"> | ||||
|     <select id="show" name="show"> | ||||
|       <option value="true">Enable Milliseconds</option> | ||||
|       <option value="false">Disable Milliseconds</option> | ||||
|     </select> | ||||
|     <button type="submit">Submit</button> | ||||
|   </form> | ||||
|  | ||||
|   <form action="/api/v1/set/layout/showTime" target="hiddenFrame"> | ||||
|     <select id="show" name="show"> | ||||
|       <option value="true">Show Clock on Countdown page</option> | ||||
|       <option value="false">Do not show Clock on Countdown page</option> | ||||
|     </select> | ||||
|     <button type="submit">Submit</button> | ||||
|   </form> | ||||
|  | ||||
|   <form action="/api/v1/set/addMillisToTimer" target="hiddenFrame"> | ||||
|     <input type="time" step="0.001" name="time2" id="time2" onchange="updateHiddenForm()"></input> | ||||
|     <input type="hidden" step="0.001" name="time" id="time"></input> | ||||
|     <button type="submit">Submit</button> | ||||
|   </form> | ||||
|  | ||||
|   <form action="/api/v1/set/addMillisToTimer" target="hiddenFrame"> | ||||
|     <select id="time" name="time" onchange=""> | ||||
|       <option value="20000">debug 1 (20 secs)</option> | ||||
|       <option value="300000">00:05:00</option> | ||||
|       <option value="600000">00:10:00</option> | ||||
|       <option value="900000">00:15:00</option> | ||||
|       <option value="1200000">00:20:00</option> | ||||
|       <option value="1500000">00:25:00</option> | ||||
|       <option value="1800000">00:30:00</option> | ||||
|       <option value="2100000">00:35:00</option> | ||||
|       <option value="2400000">00:40:00</option> | ||||
|       <option value="2700000">00:45:00</option> | ||||
|     </select> | ||||
|     <button type="submit">Submit</button> | ||||
|   </form> | ||||
|  | ||||
|   Play controls: | ||||
|   <form action="/api/v1/ctrl/timer/play" target="hiddenFrame"> | ||||
|     <button type="submit">Play</button> | ||||
|   </form> | ||||
|   <form action="/api/v1/ctrl/timer/pause" target="hiddenFrame"> | ||||
|     <button type="submit">Pause</button> | ||||
|   </form> | ||||
|   <form action="/api/v1/ctrl/timer/restart" target="hiddenFrame"> | ||||
|     <button type="submit">Restart</button> | ||||
|   </form> | ||||
|  | ||||
|   Message:  | ||||
|   <form action="/api/v1/ctrl/message/show" target="hiddenFrame"> | ||||
|     <input type="text" name="msg"> | ||||
|     <button type="submit">Submit</button> | ||||
|   </form> | ||||
|   <form action="/api/v1/ctrl/message/hide" target="hiddenFrame"> | ||||
|     <button type="submit">Hide</button> | ||||
|   </form> | ||||
|   <iframe name="hiddenFrame" style="display: none"></iframe> | ||||
|  | ||||
|  | ||||
|   <iframe src="/timer?smaller=true" height="80%" width="80%"> </iframe> | ||||
| </body> | ||||
| <script src="js/interface.js"></script> | ||||
|  | ||||
| </html> | ||||
							
								
								
									
										47
									
								
								templates/errorPages/404.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								templates/errorPages/404.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|  | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|  | ||||
|     <title>openCountdown - Not found</title> | ||||
|     <meta name="description" content="openCountdown"> | ||||
|     <meta name="author" content="TheGreydiamond"> | ||||
|  | ||||
|     <script type="text/javascript" src="/js/cookie.js"></script> | ||||
|  | ||||
|     <link rel="stylesheet/less" type="text/css" href="/css/errorPage/styles.less" /> | ||||
|  | ||||
|  | ||||
|     <script> | ||||
|         less = { | ||||
|             javascriptEnabled: true | ||||
|         }; | ||||
|     </script> | ||||
|     <script src="/js/less.min.js"></script> | ||||
|  | ||||
|     <link rel="stylesheet" href="/css/errorPage/style.css"> | ||||
|  | ||||
|     <link rel="stylesheet" href="/css/bootstrap-icons.css"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <div class="container"> | ||||
|         <h1 class="rainbow"> | ||||
|             <div class="glitch" data-text="404"> | ||||
|                 404 | ||||
|             </div> | ||||
|         </h1> | ||||
|         <h2 class="color-foreground"> | ||||
|             We're sorry, the page you were looking for isn't found here.<br> | ||||
|             The link you followed may either be broken or no longer exists. Please | ||||
|             check your spelling.<br> | ||||
|  | ||||
|             <a href="/"><i class="bi bi-house-door"></i> Back home</a> | ||||
|         </h2> | ||||
|  | ||||
|     </div> | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
							
								
								
									
										434
									
								
								templates/newAdminPanel.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								templates/newAdminPanel.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,434 @@ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
|  | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|  | ||||
|     <title>openCountdown - Admin</title> | ||||
|     <meta name="description" content="openCountdown"> | ||||
|     <meta name="author" content="TheGreydiamond"> | ||||
|  | ||||
|  | ||||
|     <link rel="stylesheet" href="/css/bootstrap-icons.css"> | ||||
|     <link rel="stylesheet" href="/mdbootstrap/css/style.css"> | ||||
|  | ||||
|     <script src="/bootstrap/dist/js/bootstrap.bundle.min.js"></script> | ||||
|     <script src="/js/jquery.min.js"></script> | ||||
|     <script type="text/javascript" src="/mdbootstrap/js/mdb.min.js"></script> | ||||
|  | ||||
|     <script type="text/javascript" src="/js/darkreader.js"></script> | ||||
|     <script type="text/javascript" src="/js/cookie.js"></script> | ||||
|     <link href="/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> | ||||
|     <link href="/css/mainStyle.css" rel="stylesheet"> | ||||
|     <link rel="stylesheet" href="/coloris/coloris.min.css" /> | ||||
|     <link rel="stylesheet" href="/bootstrap-duration-picker/bootstrap-duration-picker.css" /> | ||||
|     <link rel="stylesheet" href="/flatpickr/dist/flatpickr.min.css" /> | ||||
|     <script src="/bootstrap-duration-picker/bootstrap-duration-picker-debug.js"></script> | ||||
|     <script src="/coloris/coloris.min.js"></script> | ||||
|  | ||||
|     <script type="text/javascript" src="/flatpickr/dist/flatpickr.js"> </script> | ||||
|  | ||||
|     <link rel="stylesheet" href="/css/bootstrap-icons.css"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|  | ||||
|     <main> | ||||
|         <div class="d-flex flex-column flex-shrink-0 p-3 text-white bg-dark trans" style="width: 250px;" | ||||
|             id="navbarToggleExternalContent"> | ||||
|             <a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none"> | ||||
|                 <svg class="bi me-2" width="40" height="32"> | ||||
|                     <use xlink:href="#bootstrap" /> | ||||
|                 </svg> | ||||
|                 <span class="fs-4">Sidebar</span> | ||||
|             </a> | ||||
|             <hr> | ||||
|             <ul class="nav nav-pills flex-column mb-auto"> | ||||
|                 <li class="nav-item"> | ||||
|                     <a href="#homeP" class="nav-link active" aria-current="page" id="PageIndex"> | ||||
|                         <i class="bi bi-house-door"></i> | ||||
|                         Home | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a href="#settings" class="nav-link text-white" id="PageSettings"> | ||||
|                         <i class="bi bi-gear"></i> | ||||
|                         Settings | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a href="#debug" class="nav-link text-white" id="PageDebug"> | ||||
|                         <i class="bi bi-bug"></i> | ||||
|                         Debug | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a href="#about" class="nav-link text-white" id="PageAbout"> | ||||
|                         <i class="bi bi-info-circle"></i> | ||||
|                         About | ||||
|                     </a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|             <hr> | ||||
|             openCountdown <div class="btn-group" role="group" aria-label="Basic radio toggle button group"> | ||||
|  | ||||
|                 <input type="radio" class="btn-check" name="btnradio2" id="Mbtnradio1" autocomplete="off" checked> | ||||
|                 <label class="btn btn-outline-primary" for="Mbtnradio1"><svg xmlns="http://www.w3.org/2000/svg" | ||||
|                         width="16" height="16" fill="currentColor" class="bi bi-brightness-high-fill" | ||||
|                         viewBox="0 0 16 16"> | ||||
|                         <path | ||||
|                             d="M12 8a4 4 0 1 1-8 0 4 4 0 0 1 8 0zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" /> | ||||
|                     </svg> | ||||
|                 </label> | ||||
|  | ||||
|                 <input type="radio" class="btn-check" name="btnradio2" id="Mbtnradio2" autocomplete="off"> | ||||
|                 <label class="btn btn-outline-primary" for="Mbtnradio2"> | ||||
|                     <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" | ||||
|                         class="bi bi-moon-fill" viewBox="0 0 16 16"> | ||||
|                         <path | ||||
|                             d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" /> | ||||
|                     </svg> | ||||
|                 </label> | ||||
|  | ||||
|                 <input type="radio" class="btn-check" name="btnradio2" id="Mbtnradio3" autocomplete="off"> | ||||
|                 <label class="btn btn-outline-primary" for="Mbtnradio3"> | ||||
|                     Auto | ||||
|                 </label> | ||||
|  | ||||
|             </div> | ||||
|         </div> | ||||
|         <pages class="d-flex flex-fill" id="pageCont" class="z-index: 50;"> | ||||
|             <div class=""> | ||||
|                 <button type="button" | ||||
|                     class="opacity mx-2 svg-center btn btn-outline-transparent rounded-circle border-0" | ||||
|                     onclick="toogleNav()"> | ||||
|                     <i class="bi bi-list"></i> | ||||
|                 </button> | ||||
|             </div> | ||||
|  | ||||
|  | ||||
|             <page id="homeP" class="pageC flex-fill overflow-auto"> | ||||
|                 <h1>Home page</h1> | ||||
|  | ||||
|                 <div class="container"> | ||||
|                     <div class="row"> | ||||
|                         <div class="col"> | ||||
|                             <h3 class="d-flex">Preview <span class="helperTip" data-placement="right" title="A preview of what is currently visible on the countdown view" data-toggle="tooltip"> | ||||
|                                     <i class="bi bi-question-circle-fill"></i> | ||||
|                                 </span> | ||||
|                                  <div class="ms-auto"> | ||||
|                                     <span class="helperTip" data-placement="right" | ||||
|                                         title="Copies the link to the timer view" data-toggle="tooltip"> | ||||
|                                         <button class="btn" onclick="navigator.clipboard.writeText(window.location.toString() + 'timer')"> | ||||
|                                             <i class="bi bi-clipboard"></i> | ||||
|                                         </button> | ||||
|                                     </span> | ||||
|  | ||||
|                                     <span class="helperTip" data-placement="right" | ||||
|                                         title="Open the countdown view in a new tab" data-toggle="tooltip"> | ||||
|                                         <a href="/timer" class="btn " target="_blank"><i | ||||
|                                                 class="bi bi-box-arrow-up-right"></i></a> | ||||
|                                     </span> | ||||
|                                 </div> | ||||
|                             </h3> | ||||
|                             <iframe src="/timer?smaller=true" height="100%" width="100%" style="min-height: 400px;"> | ||||
|                             </iframe> | ||||
|                         </div> | ||||
|                         <div class="col"> | ||||
|                             <h3>Mode <span class="helperTip" data-placement="right" | ||||
|                                     title="Select what to show on the countdown view" data-toggle="tooltip"> | ||||
|                                     <i class="bi bi-question-circle-fill"></i> | ||||
|                                 </span></h3> | ||||
|                             <div class="btn-group" role="group" aria-label="Basic radio toggle button group"> | ||||
|                                 <input type="radio" class="btn-check" name="btnradio" id="btnradio1" autocomplete="off" | ||||
|                                     checked> | ||||
|                                 <label class="btn btn-outline-primary" for="btnradio1">Timer</label> | ||||
|  | ||||
|                                 <input type="radio" class="btn-check" name="btnradio" id="btnradio2" autocomplete="off"> | ||||
|                                 <label class="btn btn-outline-primary" for="btnradio2">Clock</label> | ||||
|  | ||||
|                                 <input type="radio" class="btn-check" name="btnradio" id="btnradio3" autocomplete="off"> | ||||
|                                 <label class="btn btn-outline-primary" for="btnradio3">Black</label> | ||||
|  | ||||
|                                 <input type="radio" class="btn-check" name="btnradio" id="btnradio4" autocomplete="off"> | ||||
|                                 <label class="btn btn-outline-primary" for="btnradio4">Testimage</label> | ||||
|                             </div> | ||||
|                             <br> | ||||
|                             <br> | ||||
|                             <br> | ||||
|                             <controls> | ||||
|                                 <button class="btn btn-outline-success p-4" id="funcPlay"><svg | ||||
|                                         xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" | ||||
|                                         class="bi bi-play-fill" viewBox="0 0 16 16"> | ||||
|                                         <path | ||||
|                                             d="m11.596 8.697-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z" /> | ||||
|                                     </svg></button> | ||||
|  | ||||
|                                 <button class="btn btn-outline-warning p-4" id="funcPause"><svg | ||||
|                                         xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" | ||||
|                                         class="bi bi-pause" viewBox="0 0 16 16"> | ||||
|                                         <path | ||||
|                                             d="M6 3.5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5z" /> | ||||
|                                     </svg></button> | ||||
|                                 <button class="btn btn-outline-info p-4" id="funcRestart"><svg | ||||
|                                         xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" | ||||
|                                         class="bi bi-arrow-clockwise" viewBox="0 0 16 16"> | ||||
|                                         <path fill-rule="evenodd" | ||||
|                                             d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" /> | ||||
|                                         <path | ||||
|                                             d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" /> | ||||
|                                     </svg></button> | ||||
|                             </controls> | ||||
|                             <br> | ||||
|                             <br> | ||||
|                             <br> | ||||
|                             <h3>Messaging <span class="helperTip" data-placement="right" | ||||
|                                     title="Shows a given message on the timer view" data-toggle="tooltip"> | ||||
|                                     <i class="bi bi-question-circle-fill"></i> | ||||
|                                 </span></h3> | ||||
|  | ||||
|                             <input type="text" id="messageContent" class="form-control" placeholder="Message here" | ||||
|                                 style="width: 50%;"> | ||||
|  | ||||
|                             <br> | ||||
|                             <button class="btn btn-outline-primary m-1" id="sendMessage"><i | ||||
|                                     class="bi bi-send"></i></button> | ||||
|                             <button class="btn btn-outline-primary m-1" id="ctrlRemoveMessage"> | ||||
|                                 <i class="bi bi-eye-slash-fill"></i> | ||||
|                             </button> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <br><br><br> | ||||
|                 <h3>Timer</h3> | ||||
|                 <div class="container"> | ||||
|  | ||||
|  | ||||
|                     <div class="row"> | ||||
|                         <presets class="d-inline-flex justify-content-center"> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="300000">00:05:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="600000">00:10:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="900000">00:15:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="1200000">00:20:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="1500000">00:25:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="1800000">00:30:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="2100000">00:35:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="2400000">00:40:00</button> | ||||
|                             <button class="btn btn-outline-secondary m-1 pres" value="2700000">00:45:00</button> | ||||
|                         </presets> | ||||
|                          | ||||
|                         <div class="row" style="width: 100%;"> | ||||
|                              | ||||
|                              | ||||
|                             <div class="d-inline-flex justify-content-center p-2 col"> | ||||
|                                 <input type="text" class="form-control m-1" id="customValue"> | ||||
|                                  | ||||
|                                 <button class="btn btn-outline-primary m-1 mt-0 goTimer"><i | ||||
|                                         class="bi bi-check2-circle"></i></button> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <h5> | ||||
|                             Countdown to time | ||||
|                         </h5> | ||||
|                         <div class="row justify-content-center flex-nowrap d-flex" > | ||||
|                             <input id="datetimetester" class="form-control input-sm" style="width: 20%;"> | ||||
|                             <button class="btn btn-outline-primary goTimeGoalCountdown ms-2" style="width: 5%;"><i | ||||
|                                 class="bi bi-check2-circle"></i></button> | ||||
|  | ||||
|                              | ||||
|  | ||||
|                         </input> | ||||
|                         </div> | ||||
|                          | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </page> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|             <page id="settings" class="pageC hidden flex-fill overflow-auto"> | ||||
|                 <h1>Settings</h1> | ||||
|                 <label for="showTime">Show clock on Timer:</label> | ||||
|                 <input type="checkbox" name="showTime" id="showTime"><br> | ||||
|  | ||||
|                 <label for="showMillis">Show Milliseconds on Timer:</label> | ||||
|                 <input type="checkbox" name="showMillis" id="showMillis"><br> | ||||
|  | ||||
|                 <label for="progBarShow">Show progressbar:</label> | ||||
|                 <input type="checkbox" name="progBarShow" id="progBarShow"><br> | ||||
|                 <details> | ||||
|                     <summary>Progressbar Colors</summary> | ||||
|                     <p> | ||||
|                     <div id="table" class="table-editable"> | ||||
|                         <span class="table-add float-right mb-3 mr-2"><a href="#!" class="text-success"><i | ||||
|                                     class="fas fa-plus fa-2x" aria-hidden="true"></i></a></span> | ||||
|                         <table class="table table-bordered table-responsive-md table-striped text-center" id="colors1"> | ||||
|                             <thead> | ||||
|                                 <tr> | ||||
|                                     <th class="text-center">Time</th> | ||||
|                                     <th class="text-center">Color</th> | ||||
|                                     <th class="text-center">Remove</th> | ||||
|                                 </tr> | ||||
|                             </thead> | ||||
|                             </tbody> | ||||
|                         </table> | ||||
|                     </div> | ||||
|  | ||||
|                     <button type="button" class="btn btn-outline-success" id="addRow">Add row</button> | ||||
|                     </p> | ||||
|                 </details> | ||||
|                 <hr> | ||||
|                 <label for="textColors">Enable Text Colors:</label> | ||||
|                 <input type="checkbox" name="textColors" id="textColors"><br> | ||||
|                 <details> | ||||
|                     <summary>Text Colors</summary> | ||||
|                     <p> | ||||
|  | ||||
|                     <div id="table2" class="table-editable"> | ||||
|                         <table class="table table-bordered table-responsive-md table-striped text-center" id="colors2"> | ||||
|                             <thead> | ||||
|                                 <tr> | ||||
|                                     <th class="text-center">Time</th> | ||||
|                                     <th class="text-center">Color</th> | ||||
|                                     <th class="text-center">Remove</th> | ||||
|                                 </tr> | ||||
|                             </thead> | ||||
|                             </tbody> | ||||
|                         </table> | ||||
|                     </div> | ||||
|                     <button type="button" class="btn btn-outline-success" id="copyColors">Copy from progressbar | ||||
|                         colors</button> | ||||
|  | ||||
|                     <button type="button" class="btn btn-outline-success" id="addRow2">Add row</button> | ||||
|                     </p> | ||||
|                 </details> | ||||
|                 <br> | ||||
|  | ||||
|  | ||||
|                 <button type="button" class="btn btn-outline-success" id="applyLayout"><i class="bi bi-save"></i> Apply | ||||
|                     settings</button> | ||||
|  | ||||
|                 <button type="button" class="btn btn-outline-success" id="saveLayout"><i class="bi bi-save"></i> Save as | ||||
|                     startup settings (Layout | ||||
|                     only)</button> | ||||
|             </page> | ||||
|  | ||||
|             <page id="debug" class="pageC hidden flex-fill overflow-auto"> | ||||
|                 <h1>Debug page</h1> | ||||
|                 <div class="alert alert-danger" role="alert"> | ||||
|                     <h4 class="alert-heading">Attention</h4> | ||||
|                     <p>This is a debug page which should only be used by professionals. Changing any options below might | ||||
|                         impact operation.</p> | ||||
|                     <hr> | ||||
|                     <p class="mb-0"><b>Proceed with caution.</b></p> | ||||
|                 </div> | ||||
|                 <label for="debugMode">Enable time variance display:</label> | ||||
|                 <input type="checkbox" name="debugMode" id="debugModeEnable" value="true"><br><br> | ||||
|  | ||||
|                 <button type="button" class="btn btn-outline-success" id="applyDebug">Apply settings</button> | ||||
|                 <br> | ||||
|                 <hr> | ||||
|                 <br> | ||||
|  | ||||
|                 <h3>Host information</h3> | ||||
|                 <code id="systemInfo" class="overflow-auto"> | ||||
|  | ||||
|                 </code> | ||||
|                 <h3>Raw server reponse</h3> | ||||
|                 <code id="responeSnippet" style="width: 40%; display: inline-block;" class="overflow-auto"> | ||||
|  | ||||
|                 </code> | ||||
|                 <br> | ||||
|                 <hr> | ||||
|                 <br> | ||||
|                 <templateObj> | ||||
|                     <table class="table table-bordered table-responsive-md table-striped text-center" id="colors1"> | ||||
|                         <thead> | ||||
|                             <tr> | ||||
|                                 <th class="text-center">Time</th> | ||||
|                                 <th class="text-center">Color</th> | ||||
|                                 <th class="text-center">Remove</th> | ||||
|                             </tr> | ||||
|                         </thead> | ||||
|                         <tr id="tableCopySource"> | ||||
|                             <td contenteditable="false" class="time"> | ||||
|                                 <input type="text" class="form-control " id="timeValue-ID" value="#VALUE#"> | ||||
|                                 <value></value> | ||||
|                             </td> | ||||
|                             <td class="pt-3-half full" contenteditable="false"> | ||||
|                                 <div class="clr-field" style="color: #bg-COLOR#;"> | ||||
|                                     <button aria-labelledby="clr-open-label"></button> | ||||
|                                     <input type="text" class="coloris" value="#COLOR#"> | ||||
|                                 </div> | ||||
|                                 </div> | ||||
|                             </td> | ||||
|                             <td> | ||||
|                                 <span class="table-remove"><button type="button" | ||||
|                                         class="btn btn-danger btn-rounded btn-sm my-0 deleteRow1"> | ||||
|                                         Remove | ||||
|                                     </button></span> | ||||
|                             </td> | ||||
|                         </tr> | ||||
|                         </tbody> | ||||
|                     </table> | ||||
|  | ||||
|  | ||||
|  | ||||
|                     <tr> | ||||
|                         <td class="pt-3-half numVal" contenteditable="true">#VALUE#</td> | ||||
|                         <td class="pt-3-half full" contenteditable="false"> | ||||
|                             <div class="clr-field" style="color: #bg-COLOR#;"> | ||||
|                                 <button aria-labelledby="clr-open-label"></button> | ||||
|                                 <input id="demo-input1" type="text" class="coloris" value="#COLOR#"> | ||||
|                             </div> | ||||
|                             </div> | ||||
|                         </td> | ||||
|                         <td> | ||||
|                             <span class="table-remove"><button type="button" | ||||
|                                     class="btn btn-danger btn-rounded btn-sm my-0 deleteRow1"> | ||||
|                                     Remove | ||||
|                                 </button></span> | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                 </templateObj> | ||||
|                 <script> | ||||
|                     $('#duration2').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) | ||||
|                         } | ||||
|                     }); | ||||
|                 </script> | ||||
|  | ||||
|                 <br><br> | ||||
|             </page> | ||||
|             <page id="about" class="pageC hidden flex-fill overflow-auto"> | ||||
|                 <h1>About</h1> | ||||
|                 Version: <b id="nodeSwVers"></b><br> | ||||
|                 NodeJS Version: <b id="nodejsVers"></b><br> | ||||
|  | ||||
|             </page> | ||||
|         </pages> | ||||
|     </main> | ||||
|     <script type="text/javascript" src="js/jsonview.js"></script> | ||||
|     <script type="text/javascript" src="/js/interface.js"> </script> | ||||
|     <script type="text/javascript"> | ||||
|  | ||||
|         Coloris({ | ||||
|             el: '.coloris', | ||||
|             alpha: false, | ||||
|         }); | ||||
|         $(function () { | ||||
|             $('[data-toggle="tooltip"]').tooltip({ container: "body" }) | ||||
|         })                         | ||||
|     </script> | ||||
|  | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
| @@ -11,14 +11,21 @@ | ||||
|     <meta name="author" content="TheGreydiamond"> | ||||
|  | ||||
|     <link rel="stylesheet" href="css/styles.css?v=1.1"> | ||||
|      | ||||
| </head> | ||||
|  | ||||
| <body onclick="updateFullscreen()"> | ||||
|  | ||||
|  | ||||
|  | ||||
|     <div id="overlay" onclick="off()"> | ||||
|         <div id="text">Message here</div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="connectionWarning" id="warningBanner" style="display: none;"> | ||||
|         Connection lost. Trying to reconnect... | ||||
|     </div> | ||||
|  | ||||
|     <img src='SMPTE_Color_Bars.svg' class='testImg' id="testImg" style="display: none;"> | ||||
|     </img> | ||||
|     <valueStore style="display: none;"> | ||||
| @@ -39,7 +46,7 @@ | ||||
|              | ||||
|         </div> | ||||
|     </div> | ||||
|     <script src="js/countdown.js"></script> | ||||
|     <script src="js/reconnecting-websocket.min.js"></script> | ||||
|     <script src="js/script.js"></script> | ||||
| </body> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user