Initial commit
This commit is contained in:
416
Frontend/pages/home/index.handlebars
Normal file
416
Frontend/pages/home/index.handlebars
Normal file
@@ -0,0 +1,416 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Undecked</title>
|
||||
|
||||
<link rel="stylesheet" href="/pd/home/style">
|
||||
<script src="/stc/libs/socket.io.min.js"></script>
|
||||
<script src="/pd/home/script" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="tabcontainer">
|
||||
<div class="tabmenu">
|
||||
<div class="item" tab="pages">Pages</div>
|
||||
<div class="item" tab="connections">Connections</div>
|
||||
<div class="item" tab="decks">Decks</div>
|
||||
</div>
|
||||
<div class="tabpages">
|
||||
<div class="item" tab="pages">
|
||||
<div class="pageselector">
|
||||
<div class="pageinfo">
|
||||
<input type="text" disabled class="pagename">
|
||||
<div class="controls">
|
||||
<div class="box left">
|
||||
<img src="/stc/icon/left.png">
|
||||
</div>
|
||||
<div class="centered">
|
||||
|
||||
<div class="box add">
|
||||
<img src="/stc/icon/add.png">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="box right">
|
||||
<img src="/stc/icon/right.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview">
|
||||
<div class="deck">
|
||||
<div class="group small fs">
|
||||
<div class="key" x="0" y="0"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="1" y="0"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="2" y="0"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="3" y="0"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="4" y="0"><canvas class="ready"></canvas></div>
|
||||
</div>
|
||||
<div class="group large">
|
||||
<div class="key" x="5" y="0"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="6" y="0"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="7" y="0"><canvas class="ready"></canvas></div>
|
||||
</div>
|
||||
<div class="group small">
|
||||
<div class="key" x="0" y="1"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="1" y="1"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="2" y="1"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="3" y="1"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="4" y="1"><canvas class="ready"></canvas></div>
|
||||
</div>
|
||||
<div class="group large">
|
||||
<div class="key" x="5" y="1"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="6" y="1"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="7" y="1"><canvas class="ready"></canvas></div>
|
||||
</div>
|
||||
<div class="group small ls">
|
||||
<div class="key" x="0" y="2"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="1" y="2"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="2" y="2"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="3" y="2"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="4" y="2"><canvas class="ready"></canvas></div>
|
||||
</div>
|
||||
<div class="group large">
|
||||
<div class="key" x="5" y="2"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="6" y="2"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="7" y="2"><canvas class="ready"></canvas></div>
|
||||
</div>
|
||||
<div class="group large">
|
||||
<div class="key" x="0" y="3"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="1" y="3"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="2" y="3"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="3" y="3"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="4" y="3"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="5" y="3"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="6" y="3"><canvas class="ready"></canvas></div>
|
||||
<div class="key" x="7" y="3"><canvas class="ready"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editcontainer">
|
||||
<div class="edit disabled">
|
||||
<div class="split">
|
||||
<div class="buttontype">
|
||||
<div class="types">
|
||||
<div class="buttonitem selected" type="empty">
|
||||
<div class="text">Empty</div>
|
||||
</div>
|
||||
<div class="buttonitem" type="custom">
|
||||
<div class="text">Custom</div>
|
||||
</div>
|
||||
<div class="buttonitem" type="pageup">
|
||||
<div class="text">Page Up</div>
|
||||
</div>
|
||||
<div class="buttonitem" type="pagedown">
|
||||
<div class="text">Page Down</div>
|
||||
</div>
|
||||
<div class="buttonitem" type="currentpage">
|
||||
<div class="text">Current Page</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editinner">
|
||||
<div class="containertitle blockheader">
|
||||
Appearence
|
||||
</div>
|
||||
<div class="container appearence">
|
||||
|
||||
<div class="vis">
|
||||
<div class="row75">
|
||||
<div class="row text">
|
||||
<div class="label">Text</div>
|
||||
<div class="inputs">
|
||||
<input type="text" class="value ap_text_value">
|
||||
<select class="size ap_text_size">
|
||||
<option value="15">15px</option>
|
||||
<option value="16">16px</option>
|
||||
<option value="17">17px</option>
|
||||
<option value="18">18px</option>
|
||||
<option value="19">19px</option>
|
||||
<option value="20">20px</option>
|
||||
<option value="21">21px</option>
|
||||
<option value="22">22px</option>
|
||||
<option value="23">23px</option>
|
||||
<option value="24">24px</option>
|
||||
<option value="25">25px</option>
|
||||
<option value="26">26px</option>
|
||||
<option value="27">27px</option>
|
||||
<option value="28">28px</option>
|
||||
<option value="29">29px</option>
|
||||
<option value="30">30px</option>
|
||||
<option value="33">33px</option>
|
||||
<option value="36">36px</option>
|
||||
<option value="40">40px</option>
|
||||
<option value="45">45px</option>
|
||||
<option value="50">50px</option>
|
||||
</select>
|
||||
<input type="color" class="color ap_text_color">
|
||||
</div>
|
||||
<div class="advanced">
|
||||
<div class="subrow ap_text_offsetx">
|
||||
<div class="label">Offset X</div>
|
||||
<input type="range" value="0" min="-50" max="50" step="0.1">
|
||||
<input type="number" value="0" min="-50" max="50" step="0.1">
|
||||
</div>
|
||||
<div class="subrow ap_text_offsety">
|
||||
<div class="label">Offset Y</div>
|
||||
<input type="range" value="0" min="-50" max="50" step="0.1">
|
||||
<input type="number" value="0" min="-50" max="50" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row background">
|
||||
<div class="label">Background</div>
|
||||
<input type="color" class="color ap_background_color">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row image">
|
||||
<div class="label">Image</div>
|
||||
<div class="imagemenu">
|
||||
<div class="imagetype">
|
||||
<div class="selectoritem selected" panel="none">
|
||||
<div class="text">None</div>
|
||||
</div>
|
||||
<div class="selectoritem" panel="icon">
|
||||
<div class="text">Icon</div>
|
||||
</div>
|
||||
<div class="selectoritem" panel="upload">
|
||||
<div class="text">Upload</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<div class="infopanel" panel="none">
|
||||
</div>
|
||||
<div class="infopanel" panel="icon">
|
||||
Icons
|
||||
</div>
|
||||
<div class="infopanel" panel="upload">
|
||||
Upload
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panels">
|
||||
<div class="panel" panel="none">
|
||||
</div>
|
||||
<div class="panel icons" panel="icon">
|
||||
<div class="list">
|
||||
{{#each icons}}
|
||||
<div class="icon" notloaded iconID="{{this.id}}">
|
||||
<img class="white">
|
||||
<img class="black">
|
||||
<div class="name">{{this.name}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel" panel="upload">
|
||||
Upload
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="advanced">
|
||||
<div class="subrow ap_image_size">
|
||||
<div class="label">Size</div>
|
||||
<input type="range" value="100" min="0" max="100" step="0.1">
|
||||
<input type="number" value="100" min="0" max="100" step="0.1">
|
||||
</div>
|
||||
<div class="subrow ap_image_offsetx">
|
||||
<div class="label">Offset X</div>
|
||||
<input type="range" value="0" min="-50" max="50" step="0.1">
|
||||
<input type="number" value="0" min="-50" max="50" step="0.1">
|
||||
</div>
|
||||
<div class="subrow ap_image_offsety">
|
||||
<div class="label">Offset Y</div>
|
||||
<input type="range" value="0" min="-50" max="50" step="0.1">
|
||||
<input type="number" value="0" min="-50" max="50" step="0.1">
|
||||
</div>
|
||||
<div class="subrow ap_image_rotation">
|
||||
<div class="label">Rotation</div>
|
||||
<input type="range" value="0" min="0" max="360" step="1">
|
||||
<input type="number" value="0" min="0" max="360" step="1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="containertitle checktitle blockheader">
|
||||
<div class="text">
|
||||
Actions
|
||||
</div>
|
||||
|
||||
<div class="checks">
|
||||
<div class="check toggle">
|
||||
<input type="checkbox">
|
||||
<div class="checklabel">Toggle</div>
|
||||
</div>
|
||||
<div class="check confirm">
|
||||
<input type="checkbox">
|
||||
<div class="checklabel">Confirm</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container actions">
|
||||
<div class="actioninner">
|
||||
<div class="row event up">
|
||||
<div class="label">Key Up</div>
|
||||
<div class="actions"></div>
|
||||
<div class="newaction">
|
||||
<input type="text" placeholder="Search for action"
|
||||
class="actionselector">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row event toggle">
|
||||
<div class="tlatch">
|
||||
<div class="label">Key Latch</div>
|
||||
<div class="actions latch"></div>
|
||||
<div class="newaction">
|
||||
<input type="text" placeholder="Search for action"
|
||||
class="actionselector">
|
||||
</div>
|
||||
</div>
|
||||
<div class="tunlatch">
|
||||
<div class="label">Key Unlatch</div>
|
||||
<div class="actions unlatch"></div>
|
||||
<div class="newaction">
|
||||
<input type="text" placeholder="Search for action"
|
||||
class="actionselector">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row event down">
|
||||
<div class="label">Key Down</div>
|
||||
<div class="actions down"></div>
|
||||
<div class="newaction">
|
||||
<input type="text" placeholder="Search for action"
|
||||
class="actionselector">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" tab="connections">
|
||||
<div class="connections">
|
||||
|
||||
<div class="connected">
|
||||
<div class="blockheader">Connections</div>
|
||||
<table class="connectedtable" cellspacing="0">
|
||||
<tr>
|
||||
<th class="status">Status</th>
|
||||
<th class="name">Name</th>
|
||||
<th class="integration">Integration</th>
|
||||
<th class="type">Device Type</th>
|
||||
</tr>
|
||||
{{#each connected}}
|
||||
<tr connectionID="{{this.connectionID}}">
|
||||
<td class="status">
|
||||
<div class="statuscontainer">
|
||||
<div class="value online"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="name">{{this.name}}</td>
|
||||
<td class="integration">{{this.integrationName}}</td>
|
||||
<td class="type">{{this.connectionType}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
</div>
|
||||
<div class="connectionbrowser">
|
||||
{{#each connections}}
|
||||
<div class="available" connectionType="{{this.connectionType}}"
|
||||
integrationID="{{this.integrationID}}">
|
||||
<div class="integration">{{this.integrationName}}</div>
|
||||
<div class="connectionName">{{this.connectionName}}</div>
|
||||
|
||||
<div class="button">Add</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" tab="decks">Decks</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="connectiondialog">
|
||||
<div class="dialog">
|
||||
<div class="title">New Connection</div>
|
||||
<div class="message"></div>
|
||||
<a class="link" target="_blank" href=""></a>
|
||||
<div class="fields"></div>
|
||||
<div class="buttons">
|
||||
<div class="button secondary cn">Cancel</div>
|
||||
<div class="button co">Connect</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contextmenu">
|
||||
<div class="item copy">
|
||||
<div class="content">
|
||||
<img src="/stc/icon/copy.png">
|
||||
<div class="title">Copy</div>
|
||||
|
||||
</div>
|
||||
<div class="shortcut"><span class="shortcutKey"></span>+C</div>
|
||||
</div>
|
||||
<div class="item paste">
|
||||
<div class="content">
|
||||
<img src="/stc/icon/paste.png">
|
||||
<div class="title">Paste</div>
|
||||
|
||||
</div>
|
||||
<div class="shortcut"><span class="shortcutKey"></span>+V</div>
|
||||
</div>
|
||||
<div class="item cut">
|
||||
<div class="content">
|
||||
<img src="/stc/icon/cut.png">
|
||||
<div class="title">Cut</div>
|
||||
</div>
|
||||
<div class="shortcut"><span class="shortcutKey"></span>+X</div>
|
||||
</div>
|
||||
<div class="seperator"></div>
|
||||
<div class="item ghost">
|
||||
<div class="content">
|
||||
<img src="/stc/icon/ghost.png">
|
||||
<div class="title">Create ghost</div>
|
||||
</div>
|
||||
<div class="shortcut"><span class="shortcutKey"></span>+G</div>
|
||||
</div>
|
||||
<div class="seperator"></div>
|
||||
<div class="item osc disabled">
|
||||
<div class="content">
|
||||
<img src="">
|
||||
<div class="title">Get OSC address</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item http disabled">
|
||||
<div class="content">
|
||||
<img src="">
|
||||
<div class="title">Get HTTP trigger address</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actiondialog">
|
||||
{{#each actions}}
|
||||
<div class="item" actionID="{{this.actionID}}" integrationID="{{this.integrationID}}">
|
||||
<div class="integration">{{this.integrationName}}</div>
|
||||
<div class="action">{{this.actionName}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
265
Frontend/pages/home/package-lock.json
generated
Normal file
265
Frontend/pages/home/package-lock.json
generated
Normal file
@@ -0,0 +1,265 @@
|
||||
{
|
||||
"name": "home",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "home",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"socket.io-client": "^4.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
|
||||
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
|
||||
},
|
||||
"node_modules/backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
|
||||
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.0",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~8.2.3",
|
||||
"xmlhttprequest-ssl": "~2.0.0",
|
||||
"yeast": "0.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
|
||||
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
|
||||
"dependencies": {
|
||||
"@socket.io/base64-arraybuffer": "~1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/parseqs": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||
},
|
||||
"node_modules/parseuri": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
|
||||
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"backo2": "~1.0.2",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.1.1",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
|
||||
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
|
||||
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@socket.io/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
|
||||
},
|
||||
"@socket.io/component-emitter": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
|
||||
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
|
||||
},
|
||||
"backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
|
||||
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.0",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~8.2.3",
|
||||
"xmlhttprequest-ssl": "~2.0.0",
|
||||
"yeast": "0.1.2"
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
|
||||
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
|
||||
"requires": {
|
||||
"@socket.io/base64-arraybuffer": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"parseqs": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||
},
|
||||
"parseuri": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
|
||||
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"backo2": "~1.0.2",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.1.1",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.1.1"
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
|
||||
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
|
||||
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
|
||||
"requires": {}
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
|
||||
},
|
||||
"yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Frontend/pages/home/package.json
Normal file
15
Frontend/pages/home/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "home",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "script.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"socket.io-client": "^4.4.1"
|
||||
}
|
||||
}
|
||||
51
Frontend/pages/home/sass/ActionDialog.scss
Normal file
51
Frontend/pages/home/sass/ActionDialog.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
.actionselector {
|
||||
width: calc(100% - 24px);
|
||||
margin: 5px 0px;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
border-bottom-right-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.actiondialog {
|
||||
position: absolute;
|
||||
border: solid var(--main-color);
|
||||
border-width: 0px 2px 2px 2px;
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
overflow: auto;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
|
||||
.item {
|
||||
width: calc(100% - 20px);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 5px 10px;
|
||||
background: var(--main-secondary-color);
|
||||
border-bottom: 1px solid var(--main-color);
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--main-hover-color);
|
||||
}
|
||||
|
||||
.integration {
|
||||
margin-right: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.item.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
152
Frontend/pages/home/sass/ActionEditor.scss
Normal file
152
Frontend/pages/home/sass/ActionEditor.scss
Normal file
@@ -0,0 +1,152 @@
|
||||
.actioncontainer {
|
||||
width: calc(100% - 20px);
|
||||
margin: 10px 10px 0px 10px;
|
||||
border-bottom: 1px solid #424242;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
|
||||
.integration {
|
||||
font-weight: 500;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.action {
|
||||
color: #d9d9d9;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--border-radius);
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn.logs {
|
||||
background: #707070;
|
||||
|
||||
&:hover {
|
||||
background: #4b4b4b;
|
||||
}
|
||||
}
|
||||
|
||||
.btn.remove {
|
||||
background: var(--color-red);
|
||||
margin-left: 5px;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-red-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fields {
|
||||
width: 100%;
|
||||
|
||||
.field {
|
||||
width: calc(100% - 5px);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
|
||||
.fieldlabel {
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
margin-right: 10px;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
position: relative;
|
||||
width: calc(100% - 14px);
|
||||
margin: 0;
|
||||
padding: 2px 5px;
|
||||
|
||||
select {
|
||||
width: calc(100% + 4px);
|
||||
margin: 0;
|
||||
padding: 2px 5px;
|
||||
|
||||
&.open {
|
||||
border-bottom-left-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
border-color: var(--main-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
left: 5px;
|
||||
right: 1px;
|
||||
height: 300px;
|
||||
display: none;
|
||||
|
||||
.inner {
|
||||
width: 100%;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border: 2px solid var(--main-color);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// flex-direction: column;
|
||||
|
||||
.option {
|
||||
width: calc(100% - 20px);
|
||||
height: 18px;
|
||||
padding: 4px 10px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--main-color);
|
||||
background: #323232;
|
||||
user-select: none;
|
||||
font-size: 12px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Frontend/pages/home/sass/ConnectionDialog.scss
Normal file
78
Frontend/pages/home/sass/ConnectionDialog.scss
Normal file
@@ -0,0 +1,78 @@
|
||||
.connectiondialog {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
z-index: 3;
|
||||
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
background: #0000005e;
|
||||
|
||||
.dialog {
|
||||
width: 300px;
|
||||
padding: 20px;
|
||||
border-radius: var(--border-radius);
|
||||
border: 2px solid var(--main-color);
|
||||
background: var(--panel-color);
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.message {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.fields {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
|
||||
.field {
|
||||
width: calc(100% - 5px);
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
|
||||
.fieldlabel {
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
margin-right: 10px;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
|
||||
.secondary {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
Frontend/pages/home/sass/Connections.scss
Normal file
102
Frontend/pages/home/sass/Connections.scss
Normal file
@@ -0,0 +1,102 @@
|
||||
.connections {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.connected {
|
||||
width: calc(80% - 20px);
|
||||
height: calc(100% - 20px);
|
||||
padding: 10px;
|
||||
border-right: 2px solid var(--main-color);
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
tr {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #323232;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status {
|
||||
width: 100px;
|
||||
|
||||
.statuscontainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.value {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 2px solid #00000044;
|
||||
border-radius: 100%;
|
||||
|
||||
&.online {
|
||||
background: green;
|
||||
}
|
||||
|
||||
&.offline {
|
||||
background: var(--color-red);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.name {}
|
||||
|
||||
.integration {}
|
||||
|
||||
.type {}
|
||||
}
|
||||
}
|
||||
|
||||
.connectionbrowser {
|
||||
width: calc(20% - 20px);
|
||||
height: calc(100% - 20px);
|
||||
min-width: 200px;
|
||||
padding: 10px;
|
||||
background: var(--subpanel-color);
|
||||
|
||||
.available {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
border-bottom: 1px solid #323232;
|
||||
|
||||
|
||||
|
||||
.integration {
|
||||
margin-right: 10px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.connectionName {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Frontend/pages/home/sass/ContextMenu.scss
Normal file
106
Frontend/pages/home/sass/ContextMenu.scss
Normal file
@@ -0,0 +1,106 @@
|
||||
.contextmenu {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
min-width: 150px;
|
||||
|
||||
display: none;
|
||||
// flex-direction: column;
|
||||
|
||||
border: 3px solid var(--main-color);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
background: var(--panel-color);
|
||||
box-shadow: 0px 0px 5px #727272;
|
||||
overflow: hidden;
|
||||
|
||||
&:first-child {
|
||||
border-top: 0px;
|
||||
}
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
|
||||
|
||||
border-top: 1px solid var(--main-hover-color);
|
||||
cursor: pointer;
|
||||
transition-duration: .2s;
|
||||
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: var(--main-hover-color);
|
||||
|
||||
img {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: .7;
|
||||
pointer-events: none;
|
||||
|
||||
img {
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
inset: 0px;
|
||||
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
padding: 0px 5px;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
|
||||
transition-duration: .2s;
|
||||
filter: grayscale(0);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 5px;
|
||||
font-size: 9px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
font-size: 8px;
|
||||
font-weight: 400;
|
||||
box-sizing: border-box;
|
||||
padding-right: 5px;
|
||||
color: #adadad;
|
||||
}
|
||||
}
|
||||
|
||||
.seperator {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: var(--main-color);
|
||||
}
|
||||
}
|
||||
75
Frontend/pages/home/sass/Deck.scss
Normal file
75
Frontend/pages/home/sass/Deck.scss
Normal file
@@ -0,0 +1,75 @@
|
||||
.overview {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 202px;
|
||||
right: 502px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.deck {
|
||||
width: 960px;
|
||||
height: 480px;
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
|
||||
.key {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 2px solid #ffffff8c;
|
||||
margin: 8px;
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
transition-duration: .1s;
|
||||
cursor: pointer;
|
||||
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--main-hover-color);
|
||||
}
|
||||
}
|
||||
|
||||
.key.selected,
|
||||
.key.selected:hover {
|
||||
border-color: var(--main-color);
|
||||
border-width: 4px;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.key.context,
|
||||
.key.context:hover {
|
||||
border-color: var(--main-color);
|
||||
border-width: 2px;
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.group.small {
|
||||
background: #2a2a2a;
|
||||
}
|
||||
|
||||
.group.fs {
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-top-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.group.ls {
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
418
Frontend/pages/home/sass/Edit.scss
Normal file
418
Frontend/pages/home/sass/Edit.scss
Normal file
@@ -0,0 +1,418 @@
|
||||
.editcontainer {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
width: 500px;
|
||||
border-left: 2px solid var(--main-color);
|
||||
background: var(--subpanel-color);
|
||||
|
||||
.edit {
|
||||
|
||||
.split {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
bottom: 5px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
opacity: 1;
|
||||
transition-duration: .2s;
|
||||
|
||||
.row {
|
||||
width: calc(100% - 10px);
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
background: #363636;
|
||||
|
||||
.label {
|
||||
color: #fff;
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 13px;
|
||||
border-bottom: 1px solid #525252;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.inputs {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.row75 {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.row {
|
||||
width: calc(25% - 15px);
|
||||
margin: 0px 0px 0px 5px;
|
||||
|
||||
&:first-child {
|
||||
width: calc(75% - 10px);
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttontype {
|
||||
width: calc(100% + 10px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid var(--main-color);
|
||||
padding-bottom: 5px;
|
||||
margin-left: -5px;
|
||||
|
||||
.types {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.buttonitem {
|
||||
border: solid var(--main-hover-color);
|
||||
border-width: 2px 2px 2px 0px;
|
||||
transition-duration: .2s;
|
||||
padding: 5px 10px;
|
||||
background: var(--background);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--main-hover-color);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-left-width: 2px;
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: var(--border-radius);
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
.buttonitem.selected,
|
||||
.buttonitem.selected:hover {
|
||||
background: var(--main-color);
|
||||
border-color: var(--main-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editinner {
|
||||
width: 100%;
|
||||
height: calc(100% - 36px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
.container {
|
||||
// position: absolute;
|
||||
// top: 0px;
|
||||
// bottom: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.containertitle {
|
||||
width: calc(100% - 10px);
|
||||
padding-left: 10px;
|
||||
margin: 10px 0 5px 0;
|
||||
|
||||
&.checktitle {
|
||||
display: flex;
|
||||
|
||||
.text {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.checks {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
user-select: none;
|
||||
|
||||
input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
pointer-events: none;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.checklabel {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container.appearence {
|
||||
// left: 0px;
|
||||
// right: 50%;
|
||||
// padding-right: 10px;
|
||||
|
||||
|
||||
.advanced {
|
||||
width: calc(100% + 10px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-left: -5px;
|
||||
margin-top: 5px;
|
||||
|
||||
.subrow {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: 0px 5px;
|
||||
|
||||
.label {
|
||||
width: 100%;
|
||||
color: #a5a5a5;
|
||||
margin: 0px 0px -3px 5px;
|
||||
font-size: 10px;
|
||||
border-bottom: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
width: calc(100% - 42px);
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
padding: 2px 0px;
|
||||
width: 30px;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
|
||||
&::-webkit-inner-spin-button,
|
||||
&::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.row.text {
|
||||
.value {
|
||||
height: 17px;
|
||||
width: calc(100% - 104px);
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
|
||||
.size {
|
||||
width: 80px;
|
||||
border-radius: 0px;
|
||||
border-width: 2px 0px;
|
||||
}
|
||||
|
||||
.color {
|
||||
height: 31px;
|
||||
width: 60px;
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.row.background {
|
||||
.color {
|
||||
height: 31px;
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.row.image {
|
||||
// width: 100%;
|
||||
// height: calc(100% - 72px);
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: flex-start;
|
||||
// flex-wrap: wrap;
|
||||
|
||||
.imagemenu {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.imagetype {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.selectoritem {
|
||||
border: solid var(--main-hover-color);
|
||||
border-width: 2px 2px 2px 0px;
|
||||
transition-duration: .2s;
|
||||
padding: 5px 10px;
|
||||
background: var(--background);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--main-hover-color);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-left-width: 2px;
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-bottom-left-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: var(--border-radius);
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
.selectoritem.selected,
|
||||
.selectoritem.selected:hover {
|
||||
background: var(--main-color);
|
||||
border-color: var(--main-color);
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
|
||||
.infopanel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.infopanel.selected {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panels {
|
||||
width: 100%;
|
||||
|
||||
.panel {
|
||||
width: 100%;
|
||||
height: calc(100% - 10px);
|
||||
padding: 5px 0px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.panel.selected {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.panel.icons {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.list {
|
||||
width: calc(100% + 4px);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
margin-left: -2px;
|
||||
cursor: pointer;
|
||||
overflow-y: auto;
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
overflow: hidden;
|
||||
background: #343434;
|
||||
margin: 1px;
|
||||
transition-duration: .2s;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--main-hover-color);
|
||||
|
||||
.name {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
width: calc(100% - 20px);
|
||||
height: calc(100% - 20px);
|
||||
opacity: 0;
|
||||
transition-duration: .2s;
|
||||
}
|
||||
|
||||
.name {
|
||||
position: absolute;
|
||||
max-height: 20%;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
font-size: 9px;
|
||||
padding: 2px;
|
||||
background: #00000096;
|
||||
transition-duration: .3s;
|
||||
}
|
||||
}
|
||||
|
||||
.icon.selected,
|
||||
.icon.selected:hover {
|
||||
border-color: var(--main-color);
|
||||
background: var(--main-secondary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.container.actions {}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.edit.disabled {
|
||||
pointer-events: none;
|
||||
|
||||
.split {
|
||||
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Frontend/pages/home/sass/Home.scss
Normal file
6
Frontend/pages/home/sass/Home.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
.blockheader {
|
||||
font-size: 18px;
|
||||
letter-spacing: 0px;
|
||||
font-weight: 500;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
182
Frontend/pages/home/sass/Pages.scss
Normal file
182
Frontend/pages/home/sass/Pages.scss
Normal file
@@ -0,0 +1,182 @@
|
||||
.item[tab="pages"] {
|
||||
|
||||
.pageselector {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 200px;
|
||||
border-right: 2px solid var(--main-color);
|
||||
background: var(--subpanel-color);
|
||||
|
||||
.pageinfo {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: calc(100% - 90px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-bottom: 2px solid var(--main-color);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.pagename {
|
||||
width: calc(100% - 24px);
|
||||
}
|
||||
|
||||
.controls {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
margin-top: 10px;
|
||||
|
||||
.box {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
img {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
}
|
||||
|
||||
&.left,
|
||||
&.right {
|
||||
img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
filter: grayscale(0);
|
||||
transition-duration: .2s;
|
||||
}
|
||||
}
|
||||
|
||||
.centered {
|
||||
width: calc(100% - 60px);
|
||||
height: 30px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.box img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
position: absolute;
|
||||
top: 90px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
|
||||
.pageitem {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #363636;
|
||||
border-bottom: 1px solid #2c2c2c;
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #3e3d3d;
|
||||
}
|
||||
|
||||
|
||||
.name {
|
||||
width: calc(100% - 35px);
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.move {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 6px;
|
||||
|
||||
.moveitem {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
|
||||
img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
filter: grayscale(0);
|
||||
transition-duration: .2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--subpanel-color);
|
||||
|
||||
img {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&.selected {
|
||||
background: var(--main-color);
|
||||
|
||||
.moveitem .normal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.moveitem .selected {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.moveitem:hover {
|
||||
background: var(--main-hover-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.buttoneditor {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 202px;
|
||||
right: 0px;
|
||||
|
||||
|
||||
.edit {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
height: 400px;
|
||||
|
||||
border-top: 2px solid var(--main-color);
|
||||
background: var(--subpanel-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Frontend/pages/home/sass/Scrollbar.scss
Normal file
20
Frontend/pages/home/sass/Scrollbar.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
background: #ffffff0f;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #b8b8b8;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #d6d6d6;
|
||||
}
|
||||
78
Frontend/pages/home/sass/Tabs.scss
Normal file
78
Frontend/pages/home/sass/Tabs.scss
Normal file
@@ -0,0 +1,78 @@
|
||||
.tabcontainer {
|
||||
border: 2px solid var(--main-color);
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
|
||||
background: #222222;
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
|
||||
.tabmenu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 30px;
|
||||
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
background: var(--panel-color);
|
||||
border-bottom: 2px solid var(--main-color);
|
||||
|
||||
.item {
|
||||
height: 100%;
|
||||
padding: 0px 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
background: var(--main-secondary-color);
|
||||
border-right: 2px solid var(--main-color);
|
||||
|
||||
user-select: none;
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--main-hover-color);
|
||||
}
|
||||
}
|
||||
|
||||
.item.active,
|
||||
.item.active :hover {
|
||||
background: var(--main-color);
|
||||
cursor: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.tabpages {
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
|
||||
.item {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
|
||||
transition-duration: .2s;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item.padding {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.item.active {
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Frontend/pages/home/script.js
Normal file
1
Frontend/pages/home/script.js
Normal file
File diff suppressed because one or more lines are too long
11
Frontend/pages/home/style.css
Normal file
11
Frontend/pages/home/style.css
Normal file
File diff suppressed because one or more lines are too long
583
Frontend/pages/home/ts/ActionEditor.ts
Normal file
583
Frontend/pages/home/ts/ActionEditor.ts
Normal file
@@ -0,0 +1,583 @@
|
||||
var ActionEditor: ActionEditor = {
|
||||
elements: {
|
||||
up: {
|
||||
container: document.querySelector('.event.up'),
|
||||
selector: document.querySelector('.event.up').querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.up').querySelector('.actions')
|
||||
},
|
||||
down: {
|
||||
container: document.querySelector('.event.down'),
|
||||
selector: document.querySelector('.event.down').querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.down').querySelector('.actions')
|
||||
},
|
||||
latch: {
|
||||
container: document.querySelector('.event.toggle'),
|
||||
selector: document.querySelector('.event.toggle').querySelector('.tlatch').querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.toggle').querySelector('.tlatch').querySelector('.actions')
|
||||
},
|
||||
unlatch: {
|
||||
container: document.querySelector('.event.toggle'),
|
||||
selector: document
|
||||
.querySelector('.event.toggle')
|
||||
.querySelector('.tunlatch')
|
||||
.querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.toggle').querySelector('.tunlatch').querySelector('.actions')
|
||||
}
|
||||
},
|
||||
|
||||
openEditors: { up: {}, down: {}, latch: {}, unlatch: {} },
|
||||
|
||||
listeners() {
|
||||
if (ActionSelector) {
|
||||
ActionSelector.register(ActionEditor.elements.up.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'up')
|
||||
);
|
||||
ActionSelector.register(ActionEditor.elements.down.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'down')
|
||||
);
|
||||
ActionSelector.register(ActionEditor.elements.latch.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'latch')
|
||||
);
|
||||
ActionSelector.register(ActionEditor.elements.unlatch.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'unlatch')
|
||||
);
|
||||
|
||||
function handleNewAction(
|
||||
integrationID: string,
|
||||
actionID: string,
|
||||
type: 'up' | 'down' | 'latch' | 'unlatch'
|
||||
) {
|
||||
socket.emit(
|
||||
'actioneditor',
|
||||
'create',
|
||||
PageHandler.currentPageID,
|
||||
Editor.currentX,
|
||||
Editor.currentY,
|
||||
type,
|
||||
integrationID,
|
||||
actionID,
|
||||
(actionInstance: Page_Key_Action) => {
|
||||
ActionEditor.openAction(actionInstance, type);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else setTimeout(ActionEditor.listeners, 100);
|
||||
},
|
||||
|
||||
open(actions: Page_Key_ActionTypes, x: string, y: string, key: Page_Key, pageID: string) {
|
||||
for (var type in actions) {
|
||||
ActionEditor.elements[type].actions.innerHTML = '';
|
||||
for (var actionInstanceID in actions[type]) {
|
||||
ActionEditor.openAction(actions[type][actionInstanceID], type);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openAction(actionInstance: Page_Key_Action, type: string) {
|
||||
var actionInstanceID = actionInstance.actionInstanceID;
|
||||
var container = <HTMLDivElement>ce('div', 'actioncontainer', { actionInstanceID });
|
||||
|
||||
var header = ce('div', 'header');
|
||||
header.appendChild(
|
||||
ce('div', 'integration', null, ActionSelector.maps.integrationNames[actionInstance.integrationID])
|
||||
);
|
||||
header.appendChild(ce('div', 'action', null, ActionSelector.maps.actionNames[actionInstance.actionID]));
|
||||
|
||||
var buttons = ce('div', 'buttons');
|
||||
var actionLogs = ce(
|
||||
'div',
|
||||
[
|
||||
'btn',
|
||||
'logs'
|
||||
],
|
||||
null,
|
||||
'Logs'
|
||||
);
|
||||
actionLogs.onclick = () => UndeckedNotification('Not implented yet', 'error');
|
||||
var actionRemove = ce(
|
||||
'div',
|
||||
[
|
||||
'btn',
|
||||
'remove'
|
||||
],
|
||||
null,
|
||||
'Remove'
|
||||
);
|
||||
actionRemove.onclick = () => {
|
||||
if (container.parentElement) container.parentElement.removeChild(container);
|
||||
if (
|
||||
ActionEditor.openEditors[type] != undefined &&
|
||||
ActionEditor.openEditors[type][actionInstanceID] != undefined
|
||||
) {
|
||||
ActionEditor.openEditors[type][actionInstanceID].destroy();
|
||||
}
|
||||
socket.emit(
|
||||
'actioneditor',
|
||||
'remove',
|
||||
PageHandler.currentPageID,
|
||||
Editor.currentX,
|
||||
Editor.currentY,
|
||||
actionInstanceID,
|
||||
type
|
||||
);
|
||||
Editor.registerChange();
|
||||
};
|
||||
buttons.appendChild(actionLogs);
|
||||
buttons.appendChild(actionRemove);
|
||||
header.appendChild(buttons);
|
||||
|
||||
container.appendChild(header);
|
||||
|
||||
var fields = <HTMLDivElement>ce('div', 'fields');
|
||||
container.appendChild(fields);
|
||||
|
||||
ActionEditor.elements[type].actions.appendChild(container);
|
||||
|
||||
ActionEditor.openEditors[type][actionInstanceID] = new Action(
|
||||
{
|
||||
integrationID: actionInstance.integrationID,
|
||||
actionID: actionInstance.actionID,
|
||||
actionType: <any>type,
|
||||
keyX: Editor.currentX,
|
||||
keyY: Editor.currentY,
|
||||
actionInstanceID,
|
||||
pageID: PageHandler.currentPageID
|
||||
},
|
||||
fields
|
||||
);
|
||||
},
|
||||
|
||||
close() {
|
||||
for (var type in ActionEditor.openEditors) {
|
||||
for (var actionInstanceID in ActionEditor.openEditors[type]) {
|
||||
ActionEditor.openEditors[type][actionInstanceID].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
ActionEditor.elements.up.actions.innerHTML = '';
|
||||
ActionEditor.elements.down.actions.innerHTML = '';
|
||||
}
|
||||
};
|
||||
ActionEditor.listeners();
|
||||
|
||||
var Action = class Action {
|
||||
settings: Action_Settings;
|
||||
container: HTMLDivElement;
|
||||
|
||||
actionEditorID: string;
|
||||
|
||||
lastFields: EditorAPI_Field[];
|
||||
|
||||
constructor(settings: Action_Settings, container: HTMLDivElement) {
|
||||
this.settings = settings;
|
||||
this.container = container;
|
||||
|
||||
socket.emit(
|
||||
'actioneditor',
|
||||
'start',
|
||||
this.settings,
|
||||
(error: string, actionEditorID: string, ready: () => void) => {
|
||||
if (error) return UndeckedNotification(error, 'error', 5000);
|
||||
this.actionEditorID = actionEditorID;
|
||||
this.listeners();
|
||||
this.emit('ready');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
emit(query: string, ...args: any[]) {
|
||||
socket.emit('actioneditor', 'instance', this.actionEditorID, query, ...args);
|
||||
}
|
||||
|
||||
listeners() {
|
||||
socket.on(`AE_${this.actionEditorID}`, (query: string, ...args: any[]) => {
|
||||
switch (query) {
|
||||
case 'fields':
|
||||
var fields: EditorAPI_Field[] = args[0];
|
||||
this.render(fields);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.container && this.container.parentElement) this.container.parentElement.removeChild(this.container);
|
||||
this.emit('close');
|
||||
}
|
||||
|
||||
render(fields: EditorAPI_Field[]) {
|
||||
var valueNameMap = {};
|
||||
|
||||
var updateMulti = (selected: string[], input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => {
|
||||
var text: string =
|
||||
selected != undefined && selected.length > 0
|
||||
? selected
|
||||
.map((value) => {
|
||||
return valueNameMap[value];
|
||||
})
|
||||
.join(', ')
|
||||
: 'None';
|
||||
|
||||
input.innerHTML = '';
|
||||
input.appendChild(ce('option', 'mutli', { value: selected.join(',') }, text));
|
||||
};
|
||||
|
||||
var getMultiValues = (dropdowninner: HTMLDivElement): string[] => {
|
||||
var values: string[] = [];
|
||||
dropdowninner.querySelectorAll('.option').forEach((option: HTMLDivElement) => {
|
||||
var checkbox: HTMLInputElement = option.querySelector('input');
|
||||
var optionID = option.getAttribute('optionID');
|
||||
|
||||
if (checkbox.checked) values.push(optionID);
|
||||
});
|
||||
return values;
|
||||
};
|
||||
|
||||
fields.forEach((field) => {
|
||||
var fieldcontainer: HTMLDivElement = this.container.querySelector(`.field_${field.id}`);
|
||||
if (fieldcontainer == null) {
|
||||
fieldcontainer = <HTMLDivElement>ce('div', [
|
||||
'field',
|
||||
`field_${field.id}`
|
||||
]);
|
||||
this.container.appendChild(fieldcontainer);
|
||||
}
|
||||
|
||||
var label: HTMLDivElement = fieldcontainer.querySelector('.fieldlabel');
|
||||
if (label == null) {
|
||||
label = <HTMLDivElement>ce('div', 'fieldlabel', null, `${field.name}${field.required ? ' *' : ''}`);
|
||||
fieldcontainer.appendChild(label);
|
||||
} else label.innerText = `${field.name}${field.required ? ' *' : ''}`;
|
||||
|
||||
var input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = fieldcontainer.querySelector(
|
||||
'.input'
|
||||
);
|
||||
|
||||
if (input == null) {
|
||||
switch (field.type) {
|
||||
case 'number':
|
||||
case 'text':
|
||||
case 'color':
|
||||
input = <HTMLInputElement>ce('input', 'input', { type: field.type, value: field.value });
|
||||
fieldcontainer.appendChild(input);
|
||||
break;
|
||||
case 'select':
|
||||
case 'connection':
|
||||
if (field.multi != undefined && field.multi == true) {
|
||||
var multiSelect = ce('div', [
|
||||
'multiselect'
|
||||
]);
|
||||
input = <HTMLInputElement>ce('select', [
|
||||
'input',
|
||||
'multiinput'
|
||||
]);
|
||||
|
||||
input.onmouseup = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
dropdown.style.display = 'block';
|
||||
input.classList.add('open');
|
||||
|
||||
var handleWindowClick = (e: MouseEvent) => {
|
||||
if (e.target) {
|
||||
var target = <HTMLElement>e.target;
|
||||
|
||||
if (
|
||||
!target.classList.contains('msdp') &&
|
||||
!target.classList.contains('multiinput')
|
||||
) {
|
||||
window.removeEventListener('click', handleWindowClick);
|
||||
dropdown.style.display = 'none';
|
||||
input.classList.remove('open');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('click', handleWindowClick);
|
||||
};
|
||||
|
||||
input.onmousedown = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
multiSelect.appendChild(input);
|
||||
|
||||
var dropdown = ce('div', [
|
||||
'dropdown',
|
||||
'msdp'
|
||||
]);
|
||||
var dropdowninner = <HTMLDivElement>ce('div', [
|
||||
'inner',
|
||||
'msdp'
|
||||
]);
|
||||
|
||||
dropdown.appendChild(dropdowninner);
|
||||
multiSelect.appendChild(dropdown);
|
||||
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
((value) => {
|
||||
valueNameMap[value.id] = field.values[i].text;
|
||||
|
||||
var item = ce(
|
||||
'div',
|
||||
[
|
||||
'option',
|
||||
'msdp'
|
||||
],
|
||||
{ optionID: value.id }
|
||||
);
|
||||
var checkbox = <HTMLInputElement>ce('input', 'msdp', {
|
||||
type: 'checkbox'
|
||||
});
|
||||
|
||||
checkbox.checked = field.value != undefined && field.value.includes(value.id);
|
||||
|
||||
item.onclick = () => {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
updateMulti(getMultiValues(dropdowninner), input);
|
||||
this.emit('fields', this.export());
|
||||
};
|
||||
|
||||
item.appendChild(checkbox);
|
||||
item.appendChild(
|
||||
ce(
|
||||
'div',
|
||||
[
|
||||
'text',
|
||||
'msdp'
|
||||
],
|
||||
null,
|
||||
value.text
|
||||
)
|
||||
);
|
||||
dropdowninner.appendChild(item);
|
||||
})(field.values[i]);
|
||||
}
|
||||
|
||||
updateMulti(field.value, input);
|
||||
|
||||
fieldcontainer.appendChild(multiSelect);
|
||||
} else {
|
||||
input = <HTMLInputElement>ce('select', 'input');
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
var option = ce(
|
||||
'option',
|
||||
null,
|
||||
{ value: field.values[i].id },
|
||||
field.values[i].text
|
||||
);
|
||||
if (field.value == field.values[i].id) option.setAttribute('selected', '');
|
||||
input.appendChild(option);
|
||||
}
|
||||
|
||||
fieldcontainer.appendChild(input);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (input) input.oninput = () => this.emit('fields', this.export());
|
||||
} else {
|
||||
input.value = field.value;
|
||||
|
||||
if (field.type == 'select' || field.type == 'connection') {
|
||||
var parsedOptionValues = [];
|
||||
if (field.multi != undefined && field.multi == true) {
|
||||
var dropdown_inner = <HTMLDivElement>input.parentElement.querySelector('.inner');
|
||||
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
var selectValue = field.values[i];
|
||||
var existingOption: HTMLDivElement = dropdown_inner.querySelector(
|
||||
`.option[optionid="${selectValue.id}"]`
|
||||
);
|
||||
|
||||
parsedOptionValues.push(selectValue.id);
|
||||
|
||||
if (existingOption) {
|
||||
var text: HTMLDivElement = existingOption.querySelector('.text');
|
||||
text.innerText = selectValue.text;
|
||||
} else {
|
||||
((value) => {
|
||||
valueNameMap[value.id] = field.values[i].text;
|
||||
|
||||
var item = ce(
|
||||
'div',
|
||||
[
|
||||
'option',
|
||||
'msdp'
|
||||
],
|
||||
{ optionID: value.id }
|
||||
);
|
||||
var checkbox = <HTMLInputElement>ce('input', 'msdp', {
|
||||
type: 'checkbox'
|
||||
});
|
||||
|
||||
checkbox.checked = field.value != undefined && field.value.includes(value.id);
|
||||
|
||||
item.onclick = () => {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
updateMulti(getMultiValues(dropdown_inner), input);
|
||||
this.emit('fields', this.export());
|
||||
};
|
||||
|
||||
item.appendChild(checkbox);
|
||||
item.appendChild(
|
||||
ce(
|
||||
'div',
|
||||
[
|
||||
'text',
|
||||
'msdp'
|
||||
],
|
||||
null,
|
||||
value.text
|
||||
)
|
||||
);
|
||||
dropdown_inner.appendChild(item);
|
||||
})(selectValue);
|
||||
}
|
||||
}
|
||||
else dropdown_inner.innerHTML = '';
|
||||
|
||||
dropdown_inner.querySelectorAll('.option').forEach((option: HTMLOptionElement) => {
|
||||
var optionValue = option.getAttribute('optionID');
|
||||
var checkbox: HTMLInputElement = option.querySelector('input');
|
||||
|
||||
if (!parsedOptionValues.includes(optionValue)) option.parentElement.removeChild(option);
|
||||
else checkbox.checked = field.value.includes(optionValue);
|
||||
});
|
||||
} else {
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
var selectValue = field.values[i];
|
||||
var existing: HTMLOptionElement = input.querySelector(
|
||||
`option[value="${selectValue.id}"]`
|
||||
);
|
||||
|
||||
if (existing) existing.innerText = selectValue.text;
|
||||
else {
|
||||
existing = <HTMLOptionElement>ce(
|
||||
'option',
|
||||
null,
|
||||
{ value: selectValue.id },
|
||||
selectValue.text
|
||||
);
|
||||
input.appendChild(existing);
|
||||
}
|
||||
parsedOptionValues.push(selectValue.id);
|
||||
}
|
||||
else input.innerHTML = '';
|
||||
|
||||
input.querySelectorAll('option').forEach((option: HTMLOptionElement) => {
|
||||
var optionValue = option.getAttribute('value');
|
||||
option.removeAttribute('selected');
|
||||
|
||||
if (!parsedOptionValues.includes(optionValue)) option.parentElement.removeChild(option);
|
||||
else if (field.value == option) option.setAttribute('selected', '');
|
||||
});
|
||||
}
|
||||
|
||||
// if (field.value != undefined && input.value.length > 0) input.value = field.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.lastFields = fields;
|
||||
}
|
||||
|
||||
export(): EditorAPI_Field[] {
|
||||
var copyOfLast = JSON.parse(JSON.stringify(this.lastFields));
|
||||
for (let i = 0; i < copyOfLast.length; i++) {
|
||||
var field = copyOfLast[i];
|
||||
var fieldcontainer: HTMLDivElement = this.container.querySelector(`.field_${field.id}`);
|
||||
if (fieldcontainer) {
|
||||
var input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = fieldcontainer.querySelector(
|
||||
'.input'
|
||||
);
|
||||
if (input) {
|
||||
if (!input.classList.contains('multiinput')) copyOfLast[i].value = input.value;
|
||||
else copyOfLast[i].value = input.value.split(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copyOfLast;
|
||||
}
|
||||
};
|
||||
|
||||
interface Action_Settings {
|
||||
integrationID: string;
|
||||
actionID: string;
|
||||
actionInstanceID: string;
|
||||
pageID: string;
|
||||
actionType: 'up' | 'down';
|
||||
keyX: number | string;
|
||||
keyY: number | string;
|
||||
}
|
||||
|
||||
interface ActionEditor {
|
||||
elements: {
|
||||
up: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
down: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
latch: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
unlatch: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
};
|
||||
|
||||
openEditors: {
|
||||
up: { [actionInstanceID: string]: typeof Action };
|
||||
down: { [actionInstanceID: string]: typeof Action };
|
||||
latch: { [actionInstanceID: string]: typeof Action };
|
||||
unlatch: { [actionInstanceID: string]: typeof Action };
|
||||
};
|
||||
|
||||
listeners: () => void;
|
||||
|
||||
open: (actions: Page_Key_ActionTypes, x: string, y: string, key: Page_Key, pageID: string) => void;
|
||||
openAction: (actionInstance: Page_Key_Action, type: string) => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
interface Page_Key_ActionTypes {
|
||||
up: Page_Key_Actions;
|
||||
down: Page_Key_Actions;
|
||||
}
|
||||
interface Page_Key_Actions {
|
||||
[actionInstanceID: string]: Page_Key_Action;
|
||||
}
|
||||
|
||||
interface Page_Key_Action {
|
||||
integrationID: string;
|
||||
actionID: string;
|
||||
actionInstanceID: string;
|
||||
properties: { [property: string]: any };
|
||||
logs: { timestamp: number; type: 'error' | 'info' | 'warning'; text: string }[];
|
||||
}
|
||||
|
||||
interface EditorAPI_Field {
|
||||
id: string;
|
||||
name: string;
|
||||
type: 'text' | 'number' | 'select' | 'connection' | 'color';
|
||||
value: any;
|
||||
values?: { id: string; text: string }[];
|
||||
required?: boolean;
|
||||
multi?: boolean;
|
||||
}
|
||||
110
Frontend/pages/home/ts/ActionSelector.ts
Normal file
110
Frontend/pages/home/ts/ActionSelector.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
var ActionSelector: ActionSelector = {
|
||||
elements: {
|
||||
actiondialog: document.querySelector('.actiondialog')
|
||||
},
|
||||
|
||||
fadeTimeout: null,
|
||||
|
||||
maps: {
|
||||
integrationNames: {},
|
||||
actionNames: {}
|
||||
},
|
||||
|
||||
init() {
|
||||
ActionSelector.elements.actiondialog.querySelectorAll('.item').forEach((item: HTMLDivElement) => {
|
||||
var integrationID = item.getAttribute('integrationID');
|
||||
var actionID = item.getAttribute('actionID');
|
||||
var integrationName = (<HTMLDivElement>item.querySelector('.integration')).innerText.trim();
|
||||
var actionName = (<HTMLDivElement>item.querySelector('.action')).innerText.trim();
|
||||
|
||||
if (ActionSelector.maps.integrationNames[integrationID] == undefined)
|
||||
ActionSelector.maps.integrationNames[integrationID] = integrationName;
|
||||
if (ActionSelector.maps.actionNames[actionID] == undefined)
|
||||
ActionSelector.maps.actionNames[actionID] = actionName;
|
||||
});
|
||||
},
|
||||
|
||||
register(input: HTMLInputElement, callback: (integrationID: string, actionID: string) => void) {
|
||||
input.onfocus = () => ActionSelector.show(input, callback);
|
||||
input.onblur = () => setTimeout(ActionSelector.hide, 200);
|
||||
input.oninput = () => ActionSelector.search(input.value);
|
||||
},
|
||||
|
||||
show(input: HTMLInputElement, callback: (integrationID: string, actionID: string) => void) {
|
||||
clearTimeout(ActionSelector.fadeTimeout);
|
||||
|
||||
ActionSelector.search('');
|
||||
|
||||
var boundingbox = input.getBoundingClientRect();
|
||||
|
||||
ActionSelector.elements.actiondialog.style.top = `${boundingbox.top - 31}px`;
|
||||
ActionSelector.elements.actiondialog.style.left = `${boundingbox.left - 10}px`;
|
||||
ActionSelector.elements.actiondialog.style.width = `${boundingbox.width - 4}px`;
|
||||
ActionSelector.elements.actiondialog.style.maxHeight = `${Math.min(
|
||||
window.innerHeight - boundingbox.top,
|
||||
200
|
||||
)}px`;
|
||||
|
||||
ActionSelector.elements.actiondialog.style.display = 'flex';
|
||||
ActionSelector.elements.actiondialog.style.transitionDuration = '.3s';
|
||||
ActionSelector.elements.actiondialog.style.opacity = '1';
|
||||
ActionSelector.elements.actiondialog.style.pointerEvents = 'auto';
|
||||
|
||||
ActionSelector.elements.actiondialog.querySelectorAll('.item').forEach((item: HTMLDivElement) => {
|
||||
item.onclick = () => {
|
||||
var integrationID = item.getAttribute('integrationID');
|
||||
var actionID = item.getAttribute('actionID');
|
||||
callback(integrationID, actionID);
|
||||
|
||||
input.value = '';
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
hide() {
|
||||
ActionSelector.elements.actiondialog.style.transitionDuration = '.3s';
|
||||
ActionSelector.elements.actiondialog.style.opacity = '0';
|
||||
ActionSelector.elements.actiondialog.style.pointerEvents = 'none';
|
||||
|
||||
clearTimeout(ActionSelector.fadeTimeout);
|
||||
ActionSelector.fadeTimeout = setTimeout(() => {
|
||||
ActionSelector.elements.actiondialog.style.display = 'none';
|
||||
}, 300);
|
||||
},
|
||||
|
||||
search(query: string) {
|
||||
ActionSelector.elements.actiondialog.querySelectorAll('.item').forEach((item: HTMLDivElement) => {
|
||||
var integration: HTMLDivElement = item.querySelector('.integration');
|
||||
var action: HTMLDivElement = item.querySelector('.action');
|
||||
|
||||
var interactionQuery = integration.innerText.toLowerCase();
|
||||
var actionQuery = action.innerText.toLowerCase();
|
||||
|
||||
query = query.toLowerCase();
|
||||
|
||||
if (interactionQuery.includes(query) || actionQuery.includes(query) || query.length == 0)
|
||||
item.classList.remove('hidden');
|
||||
else item.classList.add('hidden');
|
||||
});
|
||||
}
|
||||
};
|
||||
ActionSelector.init();
|
||||
|
||||
interface ActionSelector {
|
||||
elements: {
|
||||
actiondialog: HTMLDivElement;
|
||||
};
|
||||
|
||||
fadeTimeout: any;
|
||||
|
||||
maps: {
|
||||
integrationNames: { [integrationID: string]: string };
|
||||
actionNames: { [actionID: string]: string };
|
||||
};
|
||||
|
||||
init: () => void;
|
||||
register: (input: HTMLInputElement, callback: (integrationID: string, actionID: string) => void) => void;
|
||||
show: (input: HTMLInputElement, callback: (integrationID: string, actionID: string) => void) => void;
|
||||
hide: () => void;
|
||||
search: (query: string) => void;
|
||||
}
|
||||
72
Frontend/pages/home/ts/Clipboard.ts
Normal file
72
Frontend/pages/home/ts/Clipboard.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
var UndeckedClipboard = new class UndeckedClipboard {
|
||||
constructor() {}
|
||||
|
||||
hasKeyInClipboard() {
|
||||
return localStorage.getItem('clipboard') != undefined && localStorage.getItem('clipboard').length > 0;
|
||||
}
|
||||
|
||||
copyKey(originKeyX: number, originKeyY: number) {
|
||||
localStorage.setItem('clipboard', `key_copy_${PageHandler.currentPageID}.${originKeyX}.${originKeyY}`);
|
||||
UndeckedNotification('Key has been copied to clipboard');
|
||||
}
|
||||
|
||||
copyGhostKey(originKeyX: number, originKeyY: number) {
|
||||
localStorage.setItem('clipboard', `key_ghost_${PageHandler.currentPageID}.${originKeyX}.${originKeyY}`);
|
||||
UndeckedNotification('Key has been copied to clipboard as a ghost');
|
||||
}
|
||||
|
||||
cutKey(originKeyX: number, originKeyY: number) {
|
||||
//TODO: Implement something in the front end to show that the item is being cut right now
|
||||
localStorage.setItem('clipboard', `key_cut_${PageHandler.currentPageID}.${originKeyX}.${originKeyY}`);
|
||||
UndeckedNotification('Key has been cut to clipboard');
|
||||
}
|
||||
|
||||
pasteKey(destinationKeyX: number, destinationKeyY: number) {
|
||||
if (this.hasKeyInClipboard()) {
|
||||
var clipboard = this.decodeClipboard();
|
||||
|
||||
if (clipboard.elementType == 'key') {
|
||||
socket.emit(
|
||||
'page',
|
||||
'operation',
|
||||
clipboard.operationType,
|
||||
clipboard.id,
|
||||
clipboard.x,
|
||||
clipboard.y,
|
||||
PageHandler.currentPageID,
|
||||
destinationKeyX,
|
||||
destinationKeyY
|
||||
);
|
||||
|
||||
if (clipboard.operationType == 'cut') localStorage.setItem('clipboard', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decodeClipboard(): {
|
||||
elementType: 'key';
|
||||
operationType: 'cut' | 'copy' | 'ghost';
|
||||
id: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
} {
|
||||
var clipboard = {
|
||||
elementType: null,
|
||||
operationType: null,
|
||||
id: null,
|
||||
x: null,
|
||||
y: null
|
||||
};
|
||||
|
||||
if (this.hasKeyInClipboard()) {
|
||||
var raw = localStorage.getItem('clipboard').split('_');
|
||||
clipboard.elementType = raw[0];
|
||||
clipboard.operationType = raw[1];
|
||||
var args = raw[2].split('.');
|
||||
clipboard.id = args[0];
|
||||
clipboard.x = args[1];
|
||||
clipboard.y = args[2];
|
||||
return clipboard;
|
||||
} else return clipboard;
|
||||
}
|
||||
}();
|
||||
75
Frontend/pages/home/ts/Communication.ts
Normal file
75
Frontend/pages/home/ts/Communication.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
declare var ce: (
|
||||
type: string,
|
||||
classList?: string | string[],
|
||||
attributes?: { [key: string]: string },
|
||||
innerText?: string,
|
||||
innerHTML?: string
|
||||
) => HTMLElement;
|
||||
declare var UndeckedNotification: (message: string, type?: 'info' | 'error', time?: number) => void;
|
||||
declare var io: any;
|
||||
|
||||
var responseToken = Math.random().toString(16).substr(2, 8);
|
||||
|
||||
var socket = io('/');
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('Connected to server');
|
||||
|
||||
socket.emit('init', 'home');
|
||||
});
|
||||
|
||||
var fontSizeRatio: number = null;
|
||||
var renderQuality: number = null;
|
||||
socket.on('quality', (quality: number) => {
|
||||
document.querySelectorAll('canvas.ready').forEach((canvas: HTMLCanvasElement) => {
|
||||
canvas.width = quality;
|
||||
canvas.height = quality;
|
||||
|
||||
var context = canvas.getContext('2d');
|
||||
context.textBaseline = 'middle';
|
||||
context.textAlign = 'center';
|
||||
});
|
||||
|
||||
renderQuality = quality;
|
||||
fontSizeRatio = quality / 100;
|
||||
});
|
||||
|
||||
socket.on('pagelist', (pagelist: PageListItem[]) => {
|
||||
(function render() {
|
||||
if (fontSizeRatio != null) PageList.render(pagelist);
|
||||
else setTimeout(render, 100);
|
||||
})();
|
||||
});
|
||||
|
||||
socket.on('connectedlist', (connected: ConnectedList[]) => Connections.renderConnected(connected));
|
||||
|
||||
socket.on('page', (query: string, ...args: any[]) => {
|
||||
switch (query) {
|
||||
case 'updatename':
|
||||
var pageID: string = args[0];
|
||||
var newName: string = args[1];
|
||||
|
||||
PageList.updateName(pageID, newName);
|
||||
break;
|
||||
|
||||
case 'updatekey':
|
||||
var pageID: string = args[0];
|
||||
var x: string = args[1];
|
||||
var y: string = args[2];
|
||||
var key: Page_Key = args[3];
|
||||
var returnResponseToken: string = args[4];
|
||||
|
||||
if (PageHandler.currentPageID == pageID) {
|
||||
if (PageHandler.currentPage.keys[y] != undefined && PageHandler.currentPage.keys[y][x] != undefined)
|
||||
PageHandler.currentPage.keys[y][x] = key;
|
||||
|
||||
if (responseToken != returnResponseToken) {
|
||||
KeyHandler.render(x, y, key);
|
||||
|
||||
if (Editor.currentKey != undefined && Editor.currentKey.id == key.id) Editor.open(x, y, key);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
206
Frontend/pages/home/ts/Connections.ts
Normal file
206
Frontend/pages/home/ts/Connections.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
var Connections: Connections = {
|
||||
elements: {
|
||||
connectionbrowser: document.querySelector('.connectionbrowser'),
|
||||
|
||||
table: document.querySelector('.connectedtable'),
|
||||
|
||||
dialog: {
|
||||
container: document.querySelector('.connectiondialog'),
|
||||
dialog: document.querySelector('.connectiondialog').querySelector('.dialog'),
|
||||
fields: document.querySelector('.connectiondialog').querySelector('.fields'),
|
||||
message: document.querySelector('.connectiondialog').querySelector('.message'),
|
||||
link: document.querySelector('.connectiondialog').querySelector('.link'),
|
||||
cancel: document.querySelector('.connectiondialog').querySelector('.cn'),
|
||||
connect: document.querySelector('.connectiondialog').querySelector('.co')
|
||||
}
|
||||
},
|
||||
|
||||
init() {
|
||||
Connections.elements.connectionbrowser.querySelectorAll('.available').forEach((item: HTMLDivElement) => {
|
||||
var integrationID = item.getAttribute('integrationID');
|
||||
var connectionType = item.getAttribute('connectionType');
|
||||
var button: HTMLDivElement = item.querySelector('.button');
|
||||
button.onclick = () => Connections.requestNewDevice(integrationID, connectionType);
|
||||
});
|
||||
},
|
||||
|
||||
requestNewDevice(integrationID: string, connectionType: string) {
|
||||
socket.emit(
|
||||
'connections',
|
||||
'request',
|
||||
integrationID,
|
||||
connectionType,
|
||||
(connectionRequestData: ConnectionRequestData) => {
|
||||
if (connectionRequestData.fields && connectionRequestData.fields.length > 0)
|
||||
Connections.openDialog(integrationID, connectionType, connectionRequestData);
|
||||
else UndeckedNotification('Unable to add a new device of this type.', 'error', 5000);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
openDialog(integrationID, connectionType, connectionRequestData: ConnectionRequestData) {
|
||||
Connections.elements.dialog.fields.innerHTML = '';
|
||||
|
||||
var nameField: Connection_Field = {
|
||||
id: '_internal_name',
|
||||
name: 'Connection Name',
|
||||
type: 'text'
|
||||
};
|
||||
connectionRequestData.fields = [
|
||||
nameField,
|
||||
...connectionRequestData.fields
|
||||
];
|
||||
|
||||
if (connectionRequestData.message != undefined) {
|
||||
Connections.elements.dialog.message.style.display = 'block';
|
||||
Connections.elements.dialog.message.innerText = connectionRequestData.message;
|
||||
} else Connections.elements.dialog.message.style.display = 'none';
|
||||
|
||||
if (connectionRequestData.link != undefined) {
|
||||
Connections.elements.dialog.link.style.display = 'inline-block';
|
||||
Connections.elements.dialog.link.innerText = connectionRequestData.link.title;
|
||||
Connections.elements.dialog.link.href = connectionRequestData.link.address;
|
||||
} else Connections.elements.dialog.link.style.display = 'none';
|
||||
|
||||
connectionRequestData.fields.forEach((field) => {
|
||||
var fieldcontainer = ce('div', [
|
||||
'field',
|
||||
`field_${field.id}`
|
||||
]);
|
||||
Connections.elements.dialog.fields.appendChild(fieldcontainer);
|
||||
|
||||
var label = ce('div', 'fieldlabel', null, `${field.name}`);
|
||||
fieldcontainer.appendChild(label);
|
||||
|
||||
var input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = null;
|
||||
|
||||
switch (field.type) {
|
||||
case 'number':
|
||||
case 'text':
|
||||
input = <HTMLInputElement>ce('input', 'input', { type: field.type, fieldID: field.id });
|
||||
if (field.value != undefined) input.value = field.value;
|
||||
fieldcontainer.appendChild(input);
|
||||
break;
|
||||
case 'select':
|
||||
input = <HTMLInputElement>ce('select', 'input', { fieldID: field.id });
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
var option = ce('option', null, { value: field.values[i].id }, field.values[i].text);
|
||||
if (field.value != undefined && field.values[i].id == field.value)
|
||||
option.setAttribute('selected', '');
|
||||
input.appendChild(option);
|
||||
}
|
||||
|
||||
fieldcontainer.appendChild(input);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
Connections.elements.dialog.cancel.onclick = Connections.closeDialog;
|
||||
Connections.elements.dialog.connect.onclick = () => {
|
||||
var properties = {};
|
||||
var inputs: NodeListOf<HTMLInputElement> = Connections.elements.dialog.fields.querySelectorAll('.input');
|
||||
for (let i = 0; i < inputs.length; i++)
|
||||
if (inputs[i].hasAttribute('fieldID')) properties[inputs[i].getAttribute('fieldID')] = inputs[i].value;
|
||||
|
||||
Connections.elements.dialog.dialog.style.display = 'none';
|
||||
socket.emit(
|
||||
'connections',
|
||||
'create',
|
||||
integrationID,
|
||||
connectionType,
|
||||
properties,
|
||||
(succeed: boolean, errormessage?: string) => {
|
||||
if (succeed == true) {
|
||||
Connections.closeDialog();
|
||||
} else {
|
||||
Connections.elements.dialog.dialog.style.display = 'block';
|
||||
UndeckedNotification(
|
||||
errormessage != undefined ? errormessage : 'Unable to validate connection',
|
||||
'error',
|
||||
5000
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Connections.elements.dialog.dialog.style.display = 'block';
|
||||
Connections.elements.dialog.container.style.display = 'flex';
|
||||
},
|
||||
|
||||
closeDialog() {
|
||||
Connections.elements.dialog.container.style.display = 'none';
|
||||
},
|
||||
|
||||
renderConnected(connectList: ConnectedList[]) {
|
||||
var table = Connections.elements.table;
|
||||
|
||||
for (let i = 0; i < connectList.length; i++) {
|
||||
var connected = connectList[i];
|
||||
if (table.querySelector(`tr[connectionid="${connected.connectionID}"]`) == null) {
|
||||
var row = ce('tr', null, { connectionID: connected.connectionID });
|
||||
var status = ce('td', 'status');
|
||||
var statuscontainer = ce('div', 'statuscontainer');
|
||||
statuscontainer.appendChild(
|
||||
ce('div', [
|
||||
'value',
|
||||
'online'
|
||||
])
|
||||
);
|
||||
status.appendChild(statuscontainer);
|
||||
row.appendChild(status);
|
||||
row.appendChild(ce('td', 'name', null, connected.name));
|
||||
row.appendChild(ce('td', 'integration', null, connected.integrationName));
|
||||
row.appendChild(ce('td', 'type', null, connected.connectionType));
|
||||
table.appendChild(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Connections.init();
|
||||
|
||||
interface Connections {
|
||||
elements: {
|
||||
connectionbrowser: HTMLDivElement;
|
||||
table: HTMLTableElement;
|
||||
|
||||
dialog: {
|
||||
container: HTMLDivElement;
|
||||
dialog: HTMLDivElement;
|
||||
message: HTMLDivElement;
|
||||
link: HTMLAnchorElement;
|
||||
fields: HTMLDivElement;
|
||||
cancel: HTMLDivElement;
|
||||
connect: HTMLDivElement;
|
||||
};
|
||||
};
|
||||
|
||||
init: () => void;
|
||||
requestNewDevice: (integrationID: string, connectionType: string) => void;
|
||||
openDialog: (integrationID, connectionID, connectionRequestData: ConnectionRequestData) => void;
|
||||
closeDialog: () => void;
|
||||
renderConnected: (connectList: ConnectedList[]) => void;
|
||||
}
|
||||
|
||||
interface Connection_Field {
|
||||
id: string;
|
||||
name: string;
|
||||
type: 'text' | 'number' | 'select';
|
||||
values?: { id: string; text: string }[];
|
||||
value?: string;
|
||||
}
|
||||
|
||||
interface ConnectedList {
|
||||
connectionID: string;
|
||||
integrationName: string;
|
||||
connectionType: string;
|
||||
name: string;
|
||||
online: boolean;
|
||||
}
|
||||
|
||||
interface ConnectionRequestData {
|
||||
fields: Connection_Field[];
|
||||
message?: string;
|
||||
link?: { address: string; title: string };
|
||||
}
|
||||
137
Frontend/pages/home/ts/ContextMenu.ts
Normal file
137
Frontend/pages/home/ts/ContextMenu.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
var ContextMenu = new class ContextMenu {
|
||||
elements: {
|
||||
menu: HTMLDivElement;
|
||||
|
||||
items: {
|
||||
copy: HTMLDivElement;
|
||||
paste: HTMLDivElement;
|
||||
cut: HTMLDivElement;
|
||||
ghost: HTMLDivElement;
|
||||
osc: HTMLDivElement;
|
||||
http: HTMLDivElement;
|
||||
};
|
||||
};
|
||||
|
||||
open: boolean;
|
||||
contextHolder: HTMLElement;
|
||||
|
||||
constructor() {
|
||||
this.elements = {
|
||||
menu: document.querySelector('.contextmenu'),
|
||||
items: {
|
||||
copy: document.querySelector('.contextmenu').querySelector('.copy'),
|
||||
paste: document.querySelector('.contextmenu').querySelector('.paste'),
|
||||
cut: document.querySelector('.contextmenu').querySelector('.cut'),
|
||||
ghost: document.querySelector('.contextmenu').querySelector('.ghost'),
|
||||
osc: document.querySelector('.contextmenu').querySelector('.osc'),
|
||||
http: document.querySelector('.contextmenu').querySelector('.http')
|
||||
}
|
||||
};
|
||||
|
||||
this.open = false;
|
||||
this.contextHolder = null;
|
||||
|
||||
window.addEventListener('contextmenu', (ev: MouseEvent) => this.handle(this.elements.menu, ev));
|
||||
window.addEventListener('mousedown', (ev: MouseEvent) => {
|
||||
if (this.open == true)
|
||||
if (ev.target) {
|
||||
var target = ev.target as HTMLElement;
|
||||
|
||||
if (
|
||||
target.classList.contains('contextmenu') ||
|
||||
(target.parentElement != undefined && target.parentElement.classList.contains('contextmenu'))
|
||||
) {
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
} else this.close();
|
||||
});
|
||||
}
|
||||
|
||||
setItems(items: DropdownItemTypes[]) {
|
||||
for (var type in this.elements.items) {
|
||||
if (items.includes(<DropdownItemTypes>type)) this.elements.items[type].style.display = 'block';
|
||||
else this.elements.items[type].style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.elements.menu.style.display = 'none';
|
||||
if (this.contextHolder != null) {
|
||||
this.contextHolder.classList.remove('context');
|
||||
this.contextHolder = null;
|
||||
}
|
||||
this.open = false;
|
||||
}
|
||||
|
||||
handle(menu: HTMLDivElement, ev: MouseEvent) {
|
||||
if (ev.target != undefined) {
|
||||
var target = ev.target as HTMLElement;
|
||||
|
||||
var types = {
|
||||
key: (element: HTMLElement) => {
|
||||
if (element.hasAttribute('x') && element.hasAttribute('y')) {
|
||||
var keyX = element.getAttribute('x');
|
||||
var keyY = element.getAttribute('y');
|
||||
|
||||
var items: DropdownItemTypes[] = [
|
||||
'osc',
|
||||
'http'
|
||||
];
|
||||
if (PageHandler.currentPage.keys[keyY][keyX].state.type != 'empty') items.push('copy', 'cut');
|
||||
if (PageHandler.currentPage.keys[keyY][keyX].state.type == 'custom') items.push('ghost');
|
||||
if (UndeckedClipboard.hasKeyInClipboard()) items.push('paste');
|
||||
|
||||
this.setItems(items);
|
||||
|
||||
this.elements.items.copy.onclick = () => {
|
||||
UndeckedClipboard.copyKey(parseInt(keyX), parseInt(keyY));
|
||||
this.close();
|
||||
};
|
||||
|
||||
this.elements.items.cut.onclick = () => {
|
||||
UndeckedClipboard.cutKey(parseInt(keyX), parseInt(keyY));
|
||||
this.close();
|
||||
};
|
||||
|
||||
this.elements.items.paste.onclick = () => {
|
||||
UndeckedClipboard.pasteKey(parseInt(keyX), parseInt(keyY));
|
||||
this.close();
|
||||
};
|
||||
|
||||
this.elements.items.ghost.onclick = () => {
|
||||
UndeckedClipboard.copyGhostKey(parseInt(keyX), parseInt(keyY));
|
||||
this.close();
|
||||
};
|
||||
|
||||
return items.length > 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (var type in types)
|
||||
if (target.classList.contains(type)) {
|
||||
ev.preventDefault();
|
||||
|
||||
var valid = types[type](target);
|
||||
|
||||
if (valid) {
|
||||
this.contextHolder = target;
|
||||
target.classList.add('context');
|
||||
|
||||
setTimeout(() => {
|
||||
this.open = true;
|
||||
}, 100);
|
||||
|
||||
menu.style.left = `${ev.pageX}px`;
|
||||
menu.style.top = `${ev.pageY - 50}px`;
|
||||
menu.style.display = 'block';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
type DropdownItemTypes = 'copy' | 'paste' | 'cut' | 'ghost' | 'osc' | 'http';
|
||||
468
Frontend/pages/home/ts/Editor.ts
Normal file
468
Frontend/pages/home/ts/Editor.ts
Normal file
@@ -0,0 +1,468 @@
|
||||
var Editor: Editor = {
|
||||
elements: {
|
||||
container: document.querySelector('.edit'),
|
||||
appearence: {
|
||||
text: {
|
||||
content: document.querySelector('.ap_text_value'),
|
||||
size: document.querySelector('.ap_text_size'),
|
||||
color: document.querySelector('.ap_text_color'),
|
||||
offsetX: document.querySelector('.ap_text_offsetx').querySelector('input[type="range"]'),
|
||||
offsetY: document.querySelector('.ap_text_offsety').querySelector('input[type="range"]')
|
||||
},
|
||||
background: {
|
||||
color: document.querySelector('.ap_background_color')
|
||||
},
|
||||
image: {
|
||||
size: document.querySelector('.ap_image_size').querySelector('input[type="range"]'),
|
||||
offsetX: document.querySelector('.ap_image_offsetx').querySelector('input[type="range"]'),
|
||||
offsetY: document.querySelector('.ap_image_offsety').querySelector('input[type="range"]'),
|
||||
rotation: document.querySelector('.ap_image_rotation').querySelector('input[type="range"]')
|
||||
}
|
||||
},
|
||||
imageui: {
|
||||
imagetypes: document.querySelector('.imagetype'),
|
||||
imageinfopanels: document.querySelector('.imagemenu').querySelector('.info'),
|
||||
imagepanels: document.querySelector('.row.image').querySelector('.panels'),
|
||||
advanced: document.querySelector('.row.image').querySelector('.advanced')
|
||||
},
|
||||
buttonui: {
|
||||
buttontypes: document.querySelector('.buttontype'),
|
||||
visual: document.querySelector('.vis'),
|
||||
actioninner: document.querySelector('.actioninner'),
|
||||
containerTitles: document.querySelector('.editcontainer').querySelectorAll('.containertitle')
|
||||
},
|
||||
checks: {
|
||||
toggle: {
|
||||
container: document.querySelector('.checks').querySelector('.check.toggle'),
|
||||
checkbox: document.querySelector('.checks').querySelector('.check.toggle').querySelector('input')
|
||||
},
|
||||
confirm: {
|
||||
container: document.querySelector('.checks').querySelector('.check.confirm'),
|
||||
checkbox: document.querySelector('.checks').querySelector('.check.confirm').querySelector('input')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
currentX: null,
|
||||
currentY: null,
|
||||
currentKey: null,
|
||||
currentImageType: null,
|
||||
currentButtonType: null,
|
||||
isToggle: null,
|
||||
isConfirm: null,
|
||||
|
||||
isOpen: false,
|
||||
|
||||
registerChange() {
|
||||
if (Editor.currentKey != null) {
|
||||
var editorExport = Editor.export();
|
||||
KeyHandler.render(Editor.currentX, Editor.currentY, editorExport);
|
||||
|
||||
socket.emit(
|
||||
'page',
|
||||
'setkey',
|
||||
PageHandler.currentPageID,
|
||||
Editor.currentX,
|
||||
Editor.currentY,
|
||||
editorExport,
|
||||
responseToken
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
listeners() {
|
||||
for (var inputCategory in Editor.elements.appearence) {
|
||||
for (var inputName in Editor.elements.appearence[inputCategory]) {
|
||||
var inputElement: HTMLInputElement = Editor.elements.appearence[inputCategory][inputName];
|
||||
|
||||
inputElement.oninput = Editor.registerChange;
|
||||
|
||||
if (inputElement.type == 'range')
|
||||
((range: HTMLInputElement, number: HTMLInputElement) => {
|
||||
range.addEventListener('input', () => {
|
||||
number.value = range.value;
|
||||
});
|
||||
number.addEventListener('input', () => {
|
||||
range.value = number.value;
|
||||
Editor.registerChange();
|
||||
});
|
||||
})(inputElement, inputElement.parentElement.querySelector('input[type="number"]'));
|
||||
}
|
||||
}
|
||||
|
||||
Editor.elements.imageui.imagetypes.querySelectorAll('.selectoritem').forEach((item: HTMLDivElement) => {
|
||||
item.onclick = () => {
|
||||
var panelType: 'none' | 'icon' | 'upload' = <any>item.getAttribute('panel');
|
||||
|
||||
Editor.selectImageTab(panelType);
|
||||
};
|
||||
});
|
||||
|
||||
Editor.elements.buttonui.buttontypes.querySelectorAll('.buttonitem').forEach((item: HTMLDivElement) => {
|
||||
item.onclick = () => {
|
||||
var buttonType: 'empty' | 'custom' | 'pageup' | 'pagedown' = <any>item.getAttribute('type');
|
||||
|
||||
Editor.selectButtonType(buttonType);
|
||||
};
|
||||
});
|
||||
|
||||
Editor.elements.checks.toggle.container.onclick = () =>
|
||||
Editor.setActionOptions(!Editor.elements.checks.toggle.checkbox.checked, Editor.isConfirm);
|
||||
|
||||
Editor.elements.checks.confirm.container.onclick = () =>
|
||||
Editor.setActionOptions(Editor.isToggle, !Editor.elements.checks.confirm.checkbox.checked);
|
||||
},
|
||||
|
||||
export(): Page_Key {
|
||||
if (Editor.isOpen) {
|
||||
return {
|
||||
id: Editor.currentKey.id,
|
||||
actions: Editor.currentKey.actions,
|
||||
state: {
|
||||
type: Editor.currentButtonType,
|
||||
confirm: Editor.isConfirm,
|
||||
toggle: Editor.isToggle
|
||||
},
|
||||
appearence: {
|
||||
text: Editor.getElementCategoryValues('text'),
|
||||
background: Editor.getElementCategoryValues('background'),
|
||||
image: Editor.getElementCategoryValues('image')
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
open(x: string, y: string, key: Page_Key) {
|
||||
if (Editor.isOpen) Editor.close();
|
||||
Editor.currentKey = key;
|
||||
Editor.currentX = x;
|
||||
Editor.currentY = y;
|
||||
Editor.isOpen = true;
|
||||
|
||||
document.querySelectorAll('.actionselector').forEach((selector: HTMLInputElement) => {
|
||||
selector.value = '';
|
||||
});
|
||||
|
||||
// --------- TEXT ---------
|
||||
{
|
||||
var textElems = Editor.elements.appearence.text;
|
||||
var textData: Page_Key_Text = Editor.getAppearenceCategory('text', key);
|
||||
textElems.content.value = textData.value != undefined ? textData.value : '';
|
||||
textElems.size.value = textData.size != undefined ? String(textData.size) : '10';
|
||||
textElems.color.value = textData.color != undefined ? textData.color : '#000000';
|
||||
textElems.offsetX.value = textData.offsetX != undefined ? String(textData.offsetX) : '0';
|
||||
var offsetXNumber: HTMLInputElement = textElems.offsetX.parentElement.querySelector('input[type="number"]');
|
||||
offsetXNumber.value = textData.offsetX != undefined ? String(textData.offsetX) : '0';
|
||||
textElems.offsetY.value = textData.offsetY != undefined ? String(textData.offsetY) : '0';
|
||||
var offsetYNumber: HTMLInputElement = textElems.offsetY.parentElement.querySelector('input[type="number"]');
|
||||
offsetYNumber.value = textData.offsetY != undefined ? String(textData.offsetY) : '0';
|
||||
}
|
||||
|
||||
// --------- BACKGROUND ---------
|
||||
{
|
||||
var backgroundElems = Editor.elements.appearence.background;
|
||||
var backgroundData: Page_Key_Background = Editor.getAppearenceCategory('background', key);
|
||||
backgroundElems.color.value = backgroundData.color != undefined ? backgroundData.color : '#000000';
|
||||
}
|
||||
|
||||
// --------- IMAGE ---------
|
||||
{
|
||||
var imageElems = Editor.elements.appearence.image;
|
||||
var imageData: Page_Key_Image = Editor.getAppearenceCategory('image', key);
|
||||
imageElems.size.value = imageData.size != undefined ? String(imageData.size) : '100';
|
||||
var sizeNumber: HTMLInputElement = imageElems.size.parentElement.querySelector('input[type="number"]');
|
||||
sizeNumber.value = imageData.size != undefined ? String(imageData.size) : '100';
|
||||
imageElems.rotation.value = imageData.rotation != undefined ? String(imageData.rotation) : '0';
|
||||
var rotationNumber: HTMLInputElement = imageElems.rotation.parentElement.querySelector(
|
||||
'input[type="number"]'
|
||||
);
|
||||
rotationNumber.value = imageData.rotation != undefined ? String(imageData.rotation) : '0';
|
||||
imageElems.offsetX.value = imageData.offsetX != undefined ? String(imageData.offsetX) : '0';
|
||||
var offsetXNumber: HTMLInputElement = imageElems.offsetX.parentElement.querySelector(
|
||||
'input[type="number"]'
|
||||
);
|
||||
offsetXNumber.value = imageData.offsetX != undefined ? String(imageData.offsetX) : '0';
|
||||
imageElems.offsetY.value = imageData.offsetY != undefined ? String(imageData.offsetY) : '0';
|
||||
var offsetYNumber: HTMLInputElement = imageElems.offsetY.parentElement.querySelector(
|
||||
'input[type="number"]'
|
||||
);
|
||||
offsetYNumber.value = imageData.offsetY != undefined ? String(imageData.offsetY) : '0';
|
||||
}
|
||||
|
||||
if (imageData.address != undefined) {
|
||||
Editor.selectImageTab('upload', true);
|
||||
} else if (imageData.iconid != undefined) {
|
||||
Editor.selectImageTab('icon', true);
|
||||
Icons.select(imageData.iconid);
|
||||
}
|
||||
|
||||
Editor.selectButtonType(key.state.type, true);
|
||||
Editor.setActionOptions(key.state.toggle, key.state.confirm, true);
|
||||
|
||||
var actions = key.actions != undefined ? key.actions : { up: {}, down: {} };
|
||||
ActionEditor.open(actions, x, y, key, PageHandler.currentPageID);
|
||||
|
||||
Editor.elements.container.classList.remove('disabled');
|
||||
},
|
||||
|
||||
close() {
|
||||
Editor.isOpen = false;
|
||||
Editor.currentKey = null;
|
||||
Editor.currentX = null;
|
||||
Editor.currentY = null;
|
||||
|
||||
document.querySelectorAll('.actionselector').forEach((selector: HTMLInputElement) => {
|
||||
selector.value = '';
|
||||
});
|
||||
|
||||
var text = Editor.elements.appearence.text;
|
||||
text.content.value = 'Button';
|
||||
text.size.value = '20';
|
||||
text.color.value = '#ffffff';
|
||||
|
||||
var background = Editor.elements.appearence.background;
|
||||
background.color.value = '#000000';
|
||||
|
||||
Editor.elements.container.classList.add('disabled');
|
||||
|
||||
Editor.selectImageTab('none', true);
|
||||
|
||||
ActionEditor.close();
|
||||
|
||||
Icons.deselect();
|
||||
},
|
||||
|
||||
selectImageTab(panelType: 'none' | 'icon' | 'upload', ignoreUpdate = false) {
|
||||
Editor.elements.imageui.imagetypes.querySelectorAll('.selectoritem').forEach((selectoritem: HTMLDivElement) => {
|
||||
if (panelType == selectoritem.getAttribute('panel')) selectoritem.classList.add('selected');
|
||||
else selectoritem.classList.remove('selected');
|
||||
});
|
||||
Editor.elements.imageui.imagepanels.querySelectorAll('.panel').forEach((panel: HTMLDivElement) => {
|
||||
if (panelType == panel.getAttribute('panel')) panel.classList.add('selected');
|
||||
else panel.classList.remove('selected');
|
||||
});
|
||||
Editor.elements.imageui.imageinfopanels.querySelectorAll('.infopanel').forEach((infopanel: HTMLDivElement) => {
|
||||
if (panelType == infopanel.getAttribute('panel')) infopanel.classList.add('selected');
|
||||
else infopanel.classList.remove('selected');
|
||||
});
|
||||
|
||||
Editor.currentImageType = panelType != 'none' ? panelType : null;
|
||||
|
||||
if (panelType == 'none') Editor.elements.imageui.advanced.style.display = 'none';
|
||||
else Editor.elements.imageui.advanced.style.display = 'flex';
|
||||
|
||||
if (panelType == 'icon') Icons.loadOnScreen();
|
||||
if (ignoreUpdate == false) Editor.registerChange();
|
||||
},
|
||||
|
||||
selectButtonType(buttonType: KeyTypes, ignoreUpdate = false) {
|
||||
if (buttonType == 'ghost') buttonType = 'custom';
|
||||
|
||||
Editor.elements.buttonui.buttontypes.querySelectorAll('.buttonitem').forEach((selectoritem: HTMLDivElement) => {
|
||||
if (buttonType == selectoritem.getAttribute('type')) selectoritem.classList.add('selected');
|
||||
else selectoritem.classList.remove('selected');
|
||||
});
|
||||
|
||||
Editor.currentButtonType = buttonType;
|
||||
|
||||
switch (buttonType) {
|
||||
case 'empty':
|
||||
case 'pageup':
|
||||
case 'pagedown':
|
||||
case 'currentpage':
|
||||
Editor.elements.buttonui.visual.style.display = 'none';
|
||||
Editor.elements.buttonui.actioninner.style.display = 'none';
|
||||
|
||||
Editor.elements.buttonui.containerTitles.forEach((title) => (title.style.display = 'none'));
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
Editor.elements.buttonui.visual.style.display = 'block';
|
||||
Editor.elements.buttonui.actioninner.style.display = 'block';
|
||||
Editor.elements.buttonui.containerTitles.forEach((title) => (title.style.display = 'flex'));
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignoreUpdate == false) Editor.registerChange();
|
||||
},
|
||||
|
||||
setActionOptions(toggle: boolean, confirm: boolean, ignoreUpdate = false) {
|
||||
if (toggle) {
|
||||
ActionEditor.elements.up.container.style.display = 'none';
|
||||
ActionEditor.elements.latch.container.style.display = 'block';
|
||||
} else {
|
||||
ActionEditor.elements.up.container.style.display = 'block';
|
||||
ActionEditor.elements.latch.container.style.display = 'none';
|
||||
}
|
||||
if (confirm) {
|
||||
ActionEditor.elements.down.container.style.display = 'none';
|
||||
} else {
|
||||
ActionEditor.elements.down.container.style.display = 'block';
|
||||
}
|
||||
|
||||
Editor.isToggle = toggle;
|
||||
Editor.isConfirm = confirm;
|
||||
|
||||
Editor.elements.checks.toggle.checkbox.checked = toggle;
|
||||
Editor.elements.checks.confirm.checkbox.checked = confirm;
|
||||
|
||||
if (ignoreUpdate == false) Editor.registerChange();
|
||||
},
|
||||
|
||||
getAppearenceCategory(category: Editor_Categories, key: Page_Key = Editor.currentKey) {
|
||||
switch (category) {
|
||||
case 'text':
|
||||
var textData: Page_Key_Text =
|
||||
key.appearence != undefined && key.appearence.text != undefined
|
||||
? key.appearence.text
|
||||
: { value: '', size: 10, color: '#000000', offsetX: 0, offsetY: 0 };
|
||||
return textData;
|
||||
case 'background':
|
||||
var backgroundData: Page_Key_Background =
|
||||
key.appearence != undefined && key.appearence.background != undefined
|
||||
? key.appearence.background
|
||||
: { color: '#000000' };
|
||||
return backgroundData;
|
||||
case 'image':
|
||||
var imageData: Page_Key_Image =
|
||||
key.appearence != undefined && key.appearence.image != undefined
|
||||
? key.appearence.image
|
||||
: { offsetX: 0, offsetY: 0, size: 100, rotation: 0 };
|
||||
return imageData;
|
||||
}
|
||||
},
|
||||
|
||||
getElementCategoryValues(category: Editor_Categories) {
|
||||
switch (category) {
|
||||
case 'text':
|
||||
var textData: Page_Key_Text = {
|
||||
value: Editor.currentButtonType != 'empty' ? Editor.elements.appearence.text.content.value : '',
|
||||
color:
|
||||
Editor.currentButtonType != 'empty' ? Editor.elements.appearence.text.color.value : '#ffffff',
|
||||
size:
|
||||
Editor.currentButtonType != 'empty' ? parseInt(Editor.elements.appearence.text.size.value) : 20,
|
||||
offsetX:
|
||||
Editor.currentButtonType != 'empty'
|
||||
? parseFloat(Editor.elements.appearence.text.offsetX.value)
|
||||
: 0,
|
||||
offsetY:
|
||||
Editor.currentButtonType != 'empty'
|
||||
? parseFloat(Editor.elements.appearence.text.offsetY.value)
|
||||
: 0
|
||||
};
|
||||
return textData;
|
||||
case 'background':
|
||||
var backgroundData: Page_Key_Background = {
|
||||
color:
|
||||
Editor.currentButtonType != 'empty'
|
||||
? Editor.elements.appearence.background.color.value
|
||||
: '#000000'
|
||||
};
|
||||
return backgroundData;
|
||||
case 'image':
|
||||
var imageData: Page_Key_Image = {
|
||||
size:
|
||||
Editor.currentButtonType != 'empty'
|
||||
? parseFloat(Editor.elements.appearence.image.size.value)
|
||||
: 100,
|
||||
rotation:
|
||||
Editor.currentButtonType != 'empty'
|
||||
? parseFloat(Editor.elements.appearence.image.rotation.value)
|
||||
: 0,
|
||||
offsetX:
|
||||
Editor.currentButtonType != 'empty'
|
||||
? parseFloat(Editor.elements.appearence.image.offsetX.value)
|
||||
: 0,
|
||||
offsetY:
|
||||
Editor.currentButtonType != 'empty'
|
||||
? parseFloat(Editor.elements.appearence.image.offsetY.value)
|
||||
: 0
|
||||
};
|
||||
|
||||
if (Editor.currentImageType != null && Editor.currentButtonType != 'empty') {
|
||||
switch (Editor.currentImageType) {
|
||||
case 'icon':
|
||||
imageData.iconid = Icons.currentSelected;
|
||||
imageData.iconstyle = 'white';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return imageData;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Editor.listeners();
|
||||
|
||||
interface Editor {
|
||||
elements: {
|
||||
container: HTMLDivElement;
|
||||
appearence: {
|
||||
text: {
|
||||
content: HTMLInputElement;
|
||||
size: HTMLSelectElement;
|
||||
color: HTMLInputElement;
|
||||
offsetX: HTMLInputElement;
|
||||
offsetY: HTMLInputElement;
|
||||
};
|
||||
background: {
|
||||
color: HTMLInputElement;
|
||||
};
|
||||
image: {
|
||||
size: HTMLInputElement;
|
||||
offsetX: HTMLInputElement;
|
||||
offsetY: HTMLInputElement;
|
||||
rotation: HTMLInputElement;
|
||||
};
|
||||
};
|
||||
imageui: {
|
||||
imagetypes: HTMLDivElement;
|
||||
imageinfopanels: HTMLDivElement;
|
||||
imagepanels: HTMLDivElement;
|
||||
advanced: HTMLDivElement;
|
||||
};
|
||||
|
||||
buttonui: {
|
||||
buttontypes: HTMLDivElement;
|
||||
visual: HTMLDivElement;
|
||||
actioninner: HTMLDivElement;
|
||||
containerTitles: NodeListOf<HTMLDivElement>;
|
||||
};
|
||||
|
||||
checks: {
|
||||
toggle: {
|
||||
container: HTMLDivElement;
|
||||
checkbox: HTMLInputElement;
|
||||
};
|
||||
confirm: {
|
||||
container: HTMLDivElement;
|
||||
checkbox: HTMLInputElement;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
isOpen: boolean;
|
||||
currentKey: Page_Key;
|
||||
currentX: string;
|
||||
currentY: string;
|
||||
currentImageType: 'icon' | 'upload';
|
||||
currentButtonType: KeyTypes;
|
||||
isToggle: boolean;
|
||||
isConfirm: boolean;
|
||||
|
||||
listeners: () => void;
|
||||
export: () => Page_Key;
|
||||
open: (x: string, y: string, key: Page_Key) => void;
|
||||
close: () => void;
|
||||
registerChange: () => void;
|
||||
selectImageTab: (panelType: string, ignoreUpdate?: boolean) => void;
|
||||
selectButtonType: (buttonType: KeyTypes, ignoreUpdate?: boolean) => void;
|
||||
setActionOptions: (toggle: boolean, confirm: boolean, ignoreUpdate?: boolean) => void;
|
||||
|
||||
getAppearenceCategory: (category: Editor_Categories, key?: Page_Key) => any;
|
||||
getElementCategoryValues: (category: Editor_Categories) => any;
|
||||
}
|
||||
|
||||
type Editor_Categories = 'text' | 'background' | 'image';
|
||||
85
Frontend/pages/home/ts/Icons.ts
Normal file
85
Frontend/pages/home/ts/Icons.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
var Icons: Icons = {
|
||||
elements: {
|
||||
container: document.querySelector('.panel.icons').querySelector('.list')
|
||||
},
|
||||
|
||||
resizeTimeout: null,
|
||||
|
||||
currentSelected: null,
|
||||
|
||||
loadOnScreen() {
|
||||
Icons.elements.container.querySelectorAll('.icon').forEach((icon: HTMLDivElement) => {
|
||||
var position = icon.getBoundingClientRect();
|
||||
|
||||
// checking for partial visibility
|
||||
if (position.top < window.innerHeight && position.bottom >= 0) {
|
||||
if (icon.hasAttribute('notloaded')) {
|
||||
icon.removeAttribute('notloaded');
|
||||
var iconID = icon.getAttribute('iconID');
|
||||
|
||||
var whiteImg: HTMLImageElement = icon.querySelector('img.white');
|
||||
var blackImg: HTMLImageElement = icon.querySelector('img.black');
|
||||
|
||||
whiteImg.src = `/stc/materialicons/white/${iconID}.png`;
|
||||
blackImg.src = `/stc/materialicons/black/${iconID}.png`;
|
||||
whiteImg.style.opacity = '1';
|
||||
blackImg.style.opacity = '0';
|
||||
}
|
||||
}
|
||||
|
||||
icon.onclick = () => Icons.select(icon.getAttribute('iconID'));
|
||||
});
|
||||
},
|
||||
|
||||
listeners() {
|
||||
var counter = 0;
|
||||
Icons.elements.container.onscroll = () => {
|
||||
counter++;
|
||||
if (counter > 5) {
|
||||
Icons.loadOnScreen();
|
||||
counter = 0;
|
||||
}
|
||||
clearTimeout(Icons.resizeTimeout);
|
||||
Icons.resizeTimeout = setTimeout(() => {
|
||||
Icons.loadOnScreen();
|
||||
}, 100);
|
||||
};
|
||||
// Icons.loadOnScreen();
|
||||
},
|
||||
|
||||
select(iconID: string, ignoreUpdate: boolean = false) {
|
||||
var icons = Icons.elements.container.querySelectorAll('.icon');
|
||||
for (let i = 0; i < icons.length; i++) {
|
||||
var testIconID = icons[i].getAttribute('iconID');
|
||||
if (iconID != testIconID) icons[i].classList.remove('selected');
|
||||
else icons[i].classList.add('selected');
|
||||
}
|
||||
Icons.currentSelected = iconID;
|
||||
|
||||
if (ignoreUpdate == false) Editor.registerChange();
|
||||
},
|
||||
|
||||
deselect() {
|
||||
var icons = Icons.elements.container.querySelectorAll('.icon');
|
||||
for (let i = 0; i < icons.length; i++) icons[i].classList.remove('selected');
|
||||
|
||||
Icons.currentSelected = null;
|
||||
}
|
||||
};
|
||||
|
||||
Icons.listeners();
|
||||
|
||||
interface Icons {
|
||||
elements: {
|
||||
container: HTMLDivElement;
|
||||
};
|
||||
|
||||
resizeTimeout: any;
|
||||
|
||||
currentSelected: string;
|
||||
|
||||
loadOnScreen: () => void;
|
||||
listeners: () => void;
|
||||
select: (iconID: string, ignoreUpdate?: boolean) => void;
|
||||
deselect: () => void;
|
||||
}
|
||||
57
Frontend/pages/home/ts/KeyBoardHandler.ts
Normal file
57
Frontend/pages/home/ts/KeyBoardHandler.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
window.addEventListener('keydown', (ev: KeyboardEvent) => {
|
||||
if (document.activeElement && document.activeElement.nodeName != 'INPUT') {
|
||||
if ((isWin() && ev.ctrlKey) || (isMac() && ev.metaKey)) {
|
||||
switch (ev.key) {
|
||||
case 'c':
|
||||
ev.preventDefault();
|
||||
if (PageHandler.currentPageID && KeyHandler.selected.length == 1) {
|
||||
UndeckedClipboard.copyKey(
|
||||
parseInt(KeyHandler.selected[0].split(',')[0]),
|
||||
parseInt(KeyHandler.selected[0].split(',')[1])
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
ev.preventDefault();
|
||||
if (PageHandler.currentPageID && KeyHandler.selected.length == 1) {
|
||||
UndeckedClipboard.cutKey(
|
||||
parseInt(KeyHandler.selected[0].split(',')[0]),
|
||||
parseInt(KeyHandler.selected[0].split(',')[1])
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
ev.preventDefault();
|
||||
if (PageHandler.currentPageID && KeyHandler.selected.length == 1) {
|
||||
UndeckedClipboard.pasteKey(
|
||||
parseInt(KeyHandler.selected[0].split(',')[0]),
|
||||
parseInt(KeyHandler.selected[0].split(',')[1])
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
ev.preventDefault();
|
||||
if (PageHandler.currentPageID && KeyHandler.selected.length == 1) {
|
||||
UndeckedClipboard.copyGhostKey(
|
||||
parseInt(KeyHandler.selected[0].split(',')[0]),
|
||||
parseInt(KeyHandler.selected[0].split(',')[1])
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (ev.key) {
|
||||
case 'Delete':
|
||||
var x = parseInt(KeyHandler.selected[0].split(',')[0]);
|
||||
var y = parseInt(KeyHandler.selected[0].split(',')[1]);
|
||||
socket.emit('page', 'operation', 'delete', PageHandler.currentPageID, x, y);
|
||||
Editor.close();
|
||||
KeyHandler.select(String(x), String(y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
372
Frontend/pages/home/ts/KeyHandler.ts
Normal file
372
Frontend/pages/home/ts/KeyHandler.ts
Normal file
@@ -0,0 +1,372 @@
|
||||
var KeyHandler: KeyHandler = {
|
||||
elements: {
|
||||
keys: {}
|
||||
},
|
||||
|
||||
selected: [],
|
||||
imagecache: {},
|
||||
ghostImage: null,
|
||||
|
||||
init() {
|
||||
KeyHandler.ghostImage = new Image();
|
||||
KeyHandler.ghostImage.src = '/stc/icon/ghost.png';
|
||||
KeyHandler.ghostImage.onload = () => {
|
||||
for (var y = 0; y < 4; y++) {
|
||||
if (KeyHandler.elements.keys[y] == undefined) KeyHandler.elements.keys[y] = {};
|
||||
for (var x = 0; x < 8; x++) {
|
||||
var keyCheck: HTMLDivElement = document.querySelector(`.key[y="${y}"][x="${x}"]`);
|
||||
if (keyCheck) {
|
||||
((keyX: number, keyY: number, key: HTMLDivElement) => {
|
||||
var canvas = key.querySelector('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
KeyHandler.elements.keys[keyY][keyX] = { key, canvas, context };
|
||||
|
||||
context.textBaseline = 'middle';
|
||||
context.textAlign = 'center';
|
||||
|
||||
key.onclick = () => {
|
||||
if (altDown == false) KeyHandler.select(String(keyX), String(keyY));
|
||||
else {
|
||||
socket.emit('page', 'executekey', PageHandler.currentPageID, keyX, keyY);
|
||||
}
|
||||
};
|
||||
})(x, y, keyCheck);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
clear() {
|
||||
for (var y in KeyHandler.elements.keys) {
|
||||
for (var x in KeyHandler.elements.keys[y]) {
|
||||
var key = KeyHandler.elements.keys[y][x];
|
||||
|
||||
key.context.clearRect(0, 0, 100, 100);
|
||||
key.key.classList.remove('selected');
|
||||
}
|
||||
}
|
||||
KeyHandler.selected = [];
|
||||
Editor.close();
|
||||
},
|
||||
|
||||
render(x: string, y: string, data: Page_Key) {
|
||||
if (KeyHandler.elements.keys[String(y)]) {
|
||||
if (KeyHandler.elements.keys[String(y)][String(x)]) {
|
||||
var context = KeyHandler.elements.keys[String(y)][String(x)].context;
|
||||
|
||||
context.clearRect(0, 0, renderQuality, renderQuality);
|
||||
|
||||
if (data.state.type == 'custom') {
|
||||
var appearence = data.appearence;
|
||||
|
||||
render(appearence);
|
||||
} else if (data.state.type == 'ghost') {
|
||||
var appearence = data.appearence;
|
||||
|
||||
if (appearence == undefined) appearence = {};
|
||||
|
||||
appearence.system = { ghost: true };
|
||||
|
||||
render(appearence);
|
||||
} else if (data.state.type == 'pageup') {
|
||||
render({
|
||||
text: { value: 'Up', color: '#ffffff', size: 18, offsetX: 0, offsetY: 25 },
|
||||
background: { color: '#4676b7' },
|
||||
image: {
|
||||
size: 100,
|
||||
rotation: 0,
|
||||
offsetX: 0,
|
||||
offsetY: -15,
|
||||
iconid: 'keyboard_arrow_up',
|
||||
iconstyle: 'white'
|
||||
},
|
||||
system: {
|
||||
border: {
|
||||
color: '#253e5e',
|
||||
thickness: 8
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (data.state.type == 'pagedown') {
|
||||
render({
|
||||
text: { value: 'Down', color: '#ffffff', size: 18, offsetX: 0, offsetY: -25 },
|
||||
background: { color: '#4676b7' },
|
||||
image: {
|
||||
size: 100,
|
||||
rotation: 0,
|
||||
offsetX: 0,
|
||||
offsetY: 15,
|
||||
iconid: 'keyboard_arrow_down',
|
||||
iconstyle: 'white'
|
||||
},
|
||||
system: {
|
||||
border: {
|
||||
color: '#253e5e',
|
||||
thickness: 8
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (data.state.type == 'currentpage') {
|
||||
render({
|
||||
text: {
|
||||
value: `Page\\n\\n${PageHandler.currentIndex + 1}`,
|
||||
color: '#ffffff',
|
||||
size: 22,
|
||||
offsetX: 0,
|
||||
offsetY: 0
|
||||
},
|
||||
background: { color: '#4676b7' },
|
||||
system: {
|
||||
border: {
|
||||
color: '#253e5e',
|
||||
thickness: 8
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else console.error(`Invalid x '${x}'`);
|
||||
} else console.error(`Invalid y '${y}'`);
|
||||
|
||||
function render(appearence: Page_Key_Appearence) {
|
||||
if (appearence.background != undefined) {
|
||||
context.fillStyle = appearence.background.color;
|
||||
context.fillRect(0, 0, renderQuality, renderQuality);
|
||||
context.fill();
|
||||
}
|
||||
|
||||
if (appearence.text != undefined) {
|
||||
context.fillStyle = appearence.text.color;
|
||||
context.font = `800 ${appearence.text.size * fontSizeRatio}px "Montserrat"`;
|
||||
|
||||
var text = appearence.text.value;
|
||||
var lineHeight = appearence.text.size * fontSizeRatio;
|
||||
|
||||
var centerX = renderQuality / 2 + appearence.text.offsetX / 100 * (renderQuality * 2);
|
||||
var centerY = renderQuality / 2 + appearence.text.offsetY / 100 * renderQuality;
|
||||
var canvasYCounter = centerY;
|
||||
|
||||
var words = text.replace(/\\n/g, ' \\n ').split(' ');
|
||||
var line = '';
|
||||
|
||||
var totalLineHeight = 0;
|
||||
for (var n = 0; n < words.length; n++) {
|
||||
if (words[n].length == 0) continue;
|
||||
|
||||
var testLine = line + words[n] + ' ';
|
||||
var metrics = context.measureText(testLine);
|
||||
var testWidth = metrics.width;
|
||||
if (words[n] != '\\n')
|
||||
if (testWidth > renderQuality && n > 0) {
|
||||
line = words[n] + ' ';
|
||||
totalLineHeight += lineHeight;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
else {
|
||||
totalLineHeight += lineHeight;
|
||||
line = '';
|
||||
}
|
||||
}
|
||||
|
||||
line = '';
|
||||
canvasYCounter = canvasYCounter - totalLineHeight / 2;
|
||||
|
||||
var firstSkip = false;
|
||||
for (var n = 0; n < words.length; n++) {
|
||||
if (words[n].length == 0) continue;
|
||||
var testLine = line + words[n] + ' ';
|
||||
var metrics = context.measureText(testLine);
|
||||
var testWidth = metrics.width;
|
||||
if (words[n] != '\\n')
|
||||
if (testWidth > renderQuality && n > 0) {
|
||||
context.fillText(line, centerX, canvasYCounter);
|
||||
line = words[n] + ' ';
|
||||
canvasYCounter += lineHeight;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
else {
|
||||
context.fillText(line, centerX, canvasYCounter);
|
||||
line = '';
|
||||
|
||||
canvasYCounter += firstSkip ? lineHeight * 2 : lineHeight;
|
||||
if (firstSkip) firstSkip = false;
|
||||
}
|
||||
}
|
||||
context.fillText(line, centerX, canvasYCounter);
|
||||
}
|
||||
|
||||
if (appearence.image != undefined) {
|
||||
var imageAddress =
|
||||
appearence.image.address != undefined
|
||||
? appearence.image.address
|
||||
: appearence.image.iconid != undefined
|
||||
? `/stc/materialicons/white/${appearence.image.iconid}.png`
|
||||
: null;
|
||||
|
||||
var imageSize =
|
||||
appearence.image.size != undefined ? appearence.image.size / 100 * renderQuality : renderQuality;
|
||||
if (imageAddress) {
|
||||
var centerX = renderQuality / 2 + appearence.image.offsetX / 100 * renderQuality;
|
||||
var centerY = renderQuality / 2 + appearence.image.offsetY / 100 * renderQuality;
|
||||
|
||||
if (KeyHandler.imagecache[imageAddress] != undefined)
|
||||
renderImage(KeyHandler.imagecache[imageAddress]);
|
||||
else {
|
||||
KeyHandler.imagecache[imageAddress] = new Image();
|
||||
KeyHandler.imagecache[imageAddress].src = imageAddress;
|
||||
KeyHandler.imagecache[imageAddress].onload = () =>
|
||||
renderImage(KeyHandler.imagecache[imageAddress]);
|
||||
}
|
||||
|
||||
function renderImage(image) {
|
||||
context.save();
|
||||
context.translate(centerX, centerY);
|
||||
context.rotate(appearence.image.rotation * Math.PI / 180);
|
||||
context.drawImage(
|
||||
image,
|
||||
imageSize / 2 - imageSize,
|
||||
imageSize / 2 - imageSize,
|
||||
imageSize,
|
||||
imageSize
|
||||
);
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (appearence.system != undefined) {
|
||||
if (appearence.system.border != undefined) {
|
||||
var relativeThickness = appearence.system.border.thickness / 100 * renderQuality;
|
||||
context.fillStyle = appearence.system.border.color;
|
||||
context.fillRect(0, 0, renderQuality, relativeThickness);
|
||||
context.rect(0, renderQuality - relativeThickness, renderQuality, relativeThickness);
|
||||
context.rect(0, 0, relativeThickness, renderQuality);
|
||||
context.rect(renderQuality - relativeThickness, 0, relativeThickness, renderQuality);
|
||||
context.fill();
|
||||
}
|
||||
|
||||
if (appearence.system.ghost == true) {
|
||||
var size = 50 / 100 * renderQuality;
|
||||
context.save();
|
||||
context.globalAlpha = 0.7;
|
||||
context.translate(renderQuality / 2, renderQuality / 2);
|
||||
context.drawImage(KeyHandler.ghostImage, size / 2 - size, size / 2 - size, size, size);
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
select(x: string, y: string, multi: boolean = false) {
|
||||
if (PageHandler.currentPage != null) {
|
||||
if (PageHandler.currentPage.keys[y] != undefined && PageHandler.currentPage.keys[y][x] != undefined) {
|
||||
// var keyConfig = PageHandler.currentPage.keys[y][x];
|
||||
|
||||
var query = `${x},${y}`;
|
||||
if (multi) {
|
||||
if (!KeyHandler.selected.includes(query)) KeyHandler.selected.push(query);
|
||||
} else
|
||||
KeyHandler.selected = [
|
||||
query
|
||||
];
|
||||
|
||||
document.querySelectorAll('.key').forEach((key: HTMLDivElement) => {
|
||||
var checkX = key.getAttribute('x');
|
||||
var checkY = key.getAttribute('y');
|
||||
var checkQuery = `${checkX},${checkY}`;
|
||||
if (KeyHandler.selected.includes(checkQuery)) key.classList.add('selected');
|
||||
else key.classList.remove('selected');
|
||||
});
|
||||
|
||||
if (KeyHandler.selected.length > 1) Editor.close();
|
||||
else {
|
||||
socket.emit('page', 'getkey', PageHandler.currentPageID, x, y, (key: Page_Key) => {
|
||||
Editor.open(x, y, key);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
KeyHandler.init();
|
||||
|
||||
var altDown = false;
|
||||
|
||||
window.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (e.key == 'alt' || e.altKey == true) altDown = true;
|
||||
});
|
||||
window.addEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (e.key == 'alt' || e.altKey == false) altDown = false;
|
||||
});
|
||||
|
||||
interface KeyHandler {
|
||||
elements: {
|
||||
keys: {
|
||||
[y: string]: {
|
||||
[x: string]: {
|
||||
key: HTMLDivElement;
|
||||
canvas: HTMLCanvasElement;
|
||||
context: CanvasRenderingContext2D;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
selected: string[];
|
||||
imagecache: { [iconID: string]: HTMLImageElement };
|
||||
ghostImage: any;
|
||||
|
||||
init: () => void;
|
||||
clear: () => void;
|
||||
render: (x: string, y: string, data: Page_Key) => void;
|
||||
select: (x: string, y: string, multi?: boolean) => void;
|
||||
}
|
||||
|
||||
interface Page_Key {
|
||||
id: string;
|
||||
state: {
|
||||
type: KeyTypes;
|
||||
toggle: boolean;
|
||||
confirm: boolean;
|
||||
};
|
||||
actions: Page_Key_ActionTypes;
|
||||
appearence: Page_Key_Appearence;
|
||||
}
|
||||
interface Page_Key_Appearence {
|
||||
text?: Page_Key_Text;
|
||||
image?: Page_Key_Image;
|
||||
background?: Page_Key_Background;
|
||||
system?: {
|
||||
border?: {
|
||||
color: string;
|
||||
thickness: number;
|
||||
};
|
||||
ghost?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface Page_Key_Text {
|
||||
value: string;
|
||||
size: number;
|
||||
color: string;
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
}
|
||||
|
||||
interface Page_Key_Image {
|
||||
address?: string;
|
||||
iconid?: string;
|
||||
iconstyle?: 'black' | 'white';
|
||||
size: number;
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
rotation: number;
|
||||
}
|
||||
|
||||
interface Page_Key_Background {
|
||||
color: string;
|
||||
}
|
||||
|
||||
type KeyTypes = 'empty' | 'custom' | 'pageup' | 'pagedown' | 'currentpage' | 'ghost';
|
||||
17
Frontend/pages/home/ts/MetaData.ts
Normal file
17
Frontend/pages/home/ts/MetaData.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
function isMac() {
|
||||
return navigator.appVersion.indexOf('Mac') != -1;
|
||||
}
|
||||
function isWin() {
|
||||
return navigator.appVersion.indexOf('Win') != -1;
|
||||
}
|
||||
function isLinux() {
|
||||
return navigator.appVersion.indexOf('Linux') != -1;
|
||||
}
|
||||
function isUnix() {
|
||||
return navigator.appVersion.indexOf('X11') != -1;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.shortcutKey').forEach((command: HTMLDivElement) => {
|
||||
if (isMac()) command.innerText = 'Command';
|
||||
else command.innerText = 'Ctrl';
|
||||
});
|
||||
17
Frontend/pages/home/ts/OverviewScaler.ts
Normal file
17
Frontend/pages/home/ts/OverviewScaler.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
function scaleOverview() {
|
||||
var overview: HTMLDivElement = document.querySelector('.overview');
|
||||
var deck: HTMLDivElement = overview.querySelector('.deck');
|
||||
|
||||
if (overview.clientWidth > 0) {
|
||||
var margin = 10;
|
||||
|
||||
var widthScale = overview.clientWidth / (deck.clientWidth + margin * 2);
|
||||
var heightScale = overview.clientHeight / (deck.clientHeight + margin * 2);
|
||||
|
||||
if (deck.clientHeight * widthScale > overview.clientHeight) deck.style.transform = `scale(${heightScale})`;
|
||||
else deck.style.transform = `scale(${widthScale})`;
|
||||
} else setTimeout(scaleOverview, 100);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', scaleOverview);
|
||||
scaleOverview();
|
||||
141
Frontend/pages/home/ts/PageHandler.ts
Normal file
141
Frontend/pages/home/ts/PageHandler.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
var PageHandler: PageHandler = {
|
||||
elements: {
|
||||
pagename: document.querySelector('.pagename'),
|
||||
left: document.querySelector('.pageselector').querySelector('.box.left'),
|
||||
right: document.querySelector('.pageselector').querySelector('.box.right'),
|
||||
add: document.querySelector('.pageselector').querySelector('.box.add')
|
||||
},
|
||||
|
||||
currentPageID: null,
|
||||
currentPage: null,
|
||||
currentIndex: null,
|
||||
|
||||
request(pageID: string, index: number) {
|
||||
socket.emit('page', 'request', pageID, (err?: string, page?: Page_Config) => {
|
||||
if (err) UndeckedNotification(`Error whilst getting page ${pageID}: ${err}`, 'error', 5000);
|
||||
else {
|
||||
PageHandler.currentIndex = index;
|
||||
PageHandler.render(page);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render(page: Page_Config) {
|
||||
KeyHandler.clear();
|
||||
|
||||
PageHandler.currentPageID = page.pageID;
|
||||
PageHandler.currentPage = page;
|
||||
|
||||
PageList.select(page.pageID);
|
||||
|
||||
PageHandler.elements.pagename.value = page.name;
|
||||
PageHandler.elements.pagename.removeAttribute('disabled');
|
||||
|
||||
for (var y in page.keys) {
|
||||
for (var x in page.keys[y]) {
|
||||
KeyHandler.render(x, y, page.keys[y][x]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
listeners() {
|
||||
PageHandler.elements.pagename.oninput = () => {
|
||||
if (PageHandler.currentPageID != null && PageHandler.elements.pagename.value.length > 0) {
|
||||
socket.emit('page', 'setname', PageHandler.currentPageID, PageHandler.elements.pagename.value);
|
||||
}
|
||||
};
|
||||
|
||||
PageHandler.elements.add.onclick = () => {
|
||||
var pageName = prompt('New page name', 'Untitled page');
|
||||
if (pageName && pageName.length > 0) {
|
||||
socket.emit('page', 'create', pageName);
|
||||
}
|
||||
};
|
||||
|
||||
PageHandler.elements.left.onclick = () => {
|
||||
var selected = document.querySelector('.pageitem.selected');
|
||||
if (selected && selected.previousElementSibling) (<HTMLElement>selected.previousElementSibling).click();
|
||||
};
|
||||
|
||||
PageHandler.elements.right.onclick = () => {
|
||||
var selected = document.querySelector('.pageitem.selected');
|
||||
if (selected && selected.nextElementSibling) (<HTMLElement>selected.nextElementSibling).click();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
PageHandler.listeners();
|
||||
|
||||
interface PageHandler {
|
||||
elements: {
|
||||
pagename: HTMLInputElement;
|
||||
|
||||
left: HTMLDivElement;
|
||||
right: HTMLDivElement;
|
||||
add: HTMLDivElement;
|
||||
};
|
||||
|
||||
currentPageID: string;
|
||||
currentPage: Page_Config;
|
||||
currentIndex: number;
|
||||
|
||||
request: (pageID: string, index: number) => void;
|
||||
render: (page: Page_Config) => void;
|
||||
listeners: () => void;
|
||||
}
|
||||
|
||||
interface Page_Config {
|
||||
pageID: string;
|
||||
name?: string;
|
||||
|
||||
keys?: Page_Config_Keys;
|
||||
}
|
||||
|
||||
interface Page_Config_Keys {
|
||||
'0'?: {
|
||||
'0'?: Page_Key;
|
||||
'1'?: Page_Key;
|
||||
'2'?: Page_Key;
|
||||
'3'?: Page_Key;
|
||||
'4'?: Page_Key;
|
||||
'5'?: Page_Key;
|
||||
'6'?: Page_Key;
|
||||
'7'?: Page_Key;
|
||||
'8'?: Page_Key;
|
||||
};
|
||||
'1'?: {
|
||||
'0'?: Page_Key;
|
||||
'1'?: Page_Key;
|
||||
'2'?: Page_Key;
|
||||
'3'?: Page_Key;
|
||||
'4'?: Page_Key;
|
||||
'5'?: Page_Key;
|
||||
'6'?: Page_Key;
|
||||
'7'?: Page_Key;
|
||||
'8'?: Page_Key;
|
||||
};
|
||||
'2'?: {
|
||||
'0'?: Page_Key;
|
||||
'1'?: Page_Key;
|
||||
'2'?: Page_Key;
|
||||
'3'?: Page_Key;
|
||||
'4'?: Page_Key;
|
||||
'5'?: Page_Key;
|
||||
'6'?: Page_Key;
|
||||
'7'?: Page_Key;
|
||||
'8'?: Page_Key;
|
||||
};
|
||||
'3'?: {
|
||||
'0'?: Page_Key;
|
||||
'1'?: Page_Key;
|
||||
'2'?: Page_Key;
|
||||
'3'?: Page_Key;
|
||||
'4'?: Page_Key;
|
||||
'5'?: Page_Key;
|
||||
'6'?: Page_Key;
|
||||
'7'?: Page_Key;
|
||||
'8'?: Page_Key;
|
||||
};
|
||||
}
|
||||
|
||||
type Icon = '';
|
||||
115
Frontend/pages/home/ts/PageList.ts
Normal file
115
Frontend/pages/home/ts/PageList.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
var PageList: PageList = {
|
||||
elements: {
|
||||
container: document.querySelector('.pageselector')
|
||||
},
|
||||
|
||||
order: null,
|
||||
|
||||
firstRender: true,
|
||||
|
||||
render(pagelist: PageListItem[]) {
|
||||
var list = ce('div', 'list');
|
||||
|
||||
PageList.order = pagelist.map((page) => {
|
||||
return page.pageID;
|
||||
});
|
||||
|
||||
pagelist.forEach((pagelistitem, index) => {
|
||||
var pageitem = ce('div', 'pageitem', { pageID: pagelistitem.pageID });
|
||||
var name = ce('div', 'name', null, pagelistitem.name);
|
||||
pageitem.appendChild(name);
|
||||
var move = ce('div', 'move');
|
||||
|
||||
var up = ce('div', [
|
||||
'moveitem',
|
||||
'up'
|
||||
]);
|
||||
up.appendChild(ce('img', 'normal', { src: '/stc/icon/up.png' }));
|
||||
up.appendChild(ce('img', 'selected', { src: '/stc/icon/up_gray.png' }));
|
||||
var down = ce('div', [
|
||||
'moveitem',
|
||||
'down'
|
||||
]);
|
||||
down.appendChild(ce('img', 'normal', { src: '/stc/icon/down.png' }));
|
||||
down.appendChild(ce('img', 'selected', { src: '/stc/icon/down_gray.png' }));
|
||||
up.onclick = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
var current = PageList.order.indexOf(pagelistitem.pageID);
|
||||
var newIndex = Math.max(current - 1, 0);
|
||||
|
||||
PageList.order.splice(current, 1);
|
||||
PageList.order.splice(newIndex, 0, pagelistitem.pageID);
|
||||
|
||||
socket.emit('page', 'setorder', PageList.order);
|
||||
};
|
||||
down.onclick = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
var current = PageList.order.indexOf(pagelistitem.pageID);
|
||||
var newIndex = Math.max(current + 1, 0);
|
||||
|
||||
PageList.order.splice(current, 1);
|
||||
PageList.order.splice(newIndex, 0, pagelistitem.pageID);
|
||||
|
||||
socket.emit('page', 'setorder', PageList.order);
|
||||
};
|
||||
move.appendChild(up);
|
||||
move.appendChild(down);
|
||||
pageitem.appendChild(move);
|
||||
pageitem.onclick = () => {
|
||||
PageHandler.request(pagelistitem.pageID, index);
|
||||
};
|
||||
list.appendChild(pageitem);
|
||||
|
||||
if (PageHandler.currentPageID == pagelistitem.pageID) {
|
||||
PageHandler.request(pagelistitem.pageID, index);
|
||||
}
|
||||
});
|
||||
|
||||
var existing = PageList.elements.container.querySelector('.list');
|
||||
if (existing) existing.parentElement.removeChild(existing);
|
||||
PageList.elements.container.appendChild(list);
|
||||
},
|
||||
|
||||
select(pageID: string): boolean {
|
||||
var selectedFound = false;
|
||||
PageList.elements.container.querySelectorAll('.pageitem').forEach((item: HTMLDivElement) => {
|
||||
var itemPageID = item.getAttribute('pageID');
|
||||
if (itemPageID == pageID) {
|
||||
item.classList.add('selected');
|
||||
selectedFound = true;
|
||||
} else item.classList.remove('selected');
|
||||
});
|
||||
|
||||
if (selectedFound) {
|
||||
history.pushState(null, null, `/pages/${pageID}`);
|
||||
TabController.setTitle(`Page ${pageID}`);
|
||||
}
|
||||
return selectedFound;
|
||||
},
|
||||
|
||||
updateName(pageID: string, name: string) {
|
||||
var item = PageList.elements.container.querySelector(`.pageitem[pageid="${pageID}"]`);
|
||||
if (item) {
|
||||
var nameElement: HTMLDivElement = item.querySelector('.name');
|
||||
nameElement.innerText = name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface PageList {
|
||||
elements: {
|
||||
container: HTMLDivElement;
|
||||
};
|
||||
firstRender: boolean;
|
||||
order: string[];
|
||||
|
||||
render: (pagelist: PageListItem[]) => void;
|
||||
select: (pageID: string) => boolean;
|
||||
updateName: (pageID: string, name: string) => void;
|
||||
}
|
||||
interface PageListItem {
|
||||
pageID: string;
|
||||
name: string;
|
||||
}
|
||||
95
Frontend/pages/home/ts/TabControllers.ts
Normal file
95
Frontend/pages/home/ts/TabControllers.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
var TabController: TabController = {
|
||||
elements: {
|
||||
menu: document.querySelector('.tabmenu'),
|
||||
pages: document.querySelector('.tabpages')
|
||||
},
|
||||
|
||||
setTitle(title: string) {
|
||||
var titleElement = document.querySelector('title');
|
||||
titleElement.innerText = `Undecked - ${title}`;
|
||||
},
|
||||
|
||||
show(tabname: string) {
|
||||
TabController.elements.menu.querySelectorAll('.item').forEach((element: HTMLDivElement) => {
|
||||
var tab = element.getAttribute('tab');
|
||||
if (tab == tabname) element.classList.add('active');
|
||||
else element.classList.remove('active');
|
||||
});
|
||||
TabController.elements.pages.querySelectorAll('.item').forEach((element: HTMLDivElement) => {
|
||||
var tab = element.getAttribute('tab');
|
||||
if (tab == tabname) element.classList.add('active');
|
||||
else element.classList.remove('active');
|
||||
});
|
||||
|
||||
if (tabname == 'pages') {
|
||||
var pageID = PageHandler.currentPageID;
|
||||
|
||||
history.pushState(null, null, `/pages/pageID`);
|
||||
TabController.setTitle(`Page ${pageID}`);
|
||||
} else {
|
||||
history.pushState(null, null, `/${tabname}`);
|
||||
|
||||
TabController.setTitle(tabname.charAt(0).toUpperCase() + tabname.slice(1));
|
||||
}
|
||||
},
|
||||
|
||||
registerListeners() {
|
||||
TabController.elements.menu.querySelectorAll('.item').forEach((element: HTMLDivElement) => {
|
||||
var tab = element.getAttribute('tab');
|
||||
element.onclick = () => {
|
||||
TabController.show(tab);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
init() {
|
||||
var args = window.location.pathname.split('/');
|
||||
if (args.length > 0) args.splice(0, 1);
|
||||
|
||||
var tab = args.length > 0 ? args[0] : null;
|
||||
var subTab = args.length > 1 ? args[1] : null;
|
||||
|
||||
if (PageList.order != null) {
|
||||
if (tab != undefined && tab.length > 0) {
|
||||
if (tab == 'pages') {
|
||||
var valid = PageList.select(subTab);
|
||||
|
||||
if (valid) {
|
||||
var index = PageList.order.indexOf(subTab);
|
||||
PageHandler.request(PageList.order[index], index);
|
||||
TabController.show('pages');
|
||||
} else {
|
||||
PageHandler.request(PageList.order[0], 0);
|
||||
TabController.show('pages');
|
||||
}
|
||||
} else {
|
||||
PageHandler.request(PageList.order[0], 0);
|
||||
TabController.show(tab);
|
||||
}
|
||||
} else {
|
||||
PageList.firstRender = false;
|
||||
PageHandler.request(PageList.order[0], 0);
|
||||
TabController.show('pages');
|
||||
}
|
||||
} else
|
||||
setTimeout(() => {
|
||||
TabController.init();
|
||||
}, 200);
|
||||
}
|
||||
};
|
||||
|
||||
TabController.init();
|
||||
TabController.registerListeners();
|
||||
|
||||
interface TabController {
|
||||
elements: {
|
||||
menu: HTMLDivElement;
|
||||
pages: HTMLDivElement;
|
||||
};
|
||||
|
||||
setTitle: (title: string) => void;
|
||||
show: (tabname: string) => void;
|
||||
registerListeners: () => void;
|
||||
|
||||
init: () => void;
|
||||
}
|
||||
7
Frontend/pages/home/tsconfig.json
Normal file
7
Frontend/pages/home/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"watch": true,
|
||||
"inlineSourceMap": true,
|
||||
"noImplicitUseStrict": true
|
||||
}
|
||||
}
|
||||
41
Frontend/pages/layouts/main/index.handlebars
Normal file
41
Frontend/pages/layouts/main/index.handlebars
Normal file
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/ld/main/style">
|
||||
<script src="/ld/main/script"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="headercontainer">
|
||||
<img src="/stc/logo/512.png">
|
||||
<div class="title">
|
||||
<div class="main">Undecked</div>
|
||||
<a class="sub" target="_blank" href="http://morphix.productions">by Morphix</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bodycontainer">
|
||||
{{{body}}}
|
||||
</div>
|
||||
|
||||
<div class="feedback">
|
||||
<div class="notificationcontainer">
|
||||
<div class="notification">
|
||||
Key has been saved to the clipboard.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
8
Frontend/pages/layouts/main/sass/Anchors.scss
Normal file
8
Frontend/pages/layouts/main/sass/Anchors.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
a {
|
||||
color: var(--main-color);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--main-hover-color);
|
||||
}
|
||||
}
|
||||
21
Frontend/pages/layouts/main/sass/Button.scss
Normal file
21
Frontend/pages/layouts/main/sass/Button.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
.button {
|
||||
padding: 4px 15px;
|
||||
background: var(--main-color);
|
||||
border-radius: var(--border-radius);
|
||||
text-decoration: none;
|
||||
transition-duration: .2s;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
|
||||
&.secondary {
|
||||
background: #565656;
|
||||
|
||||
&:hover {
|
||||
background: #303030;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--main-hover-color);
|
||||
}
|
||||
}
|
||||
38
Frontend/pages/layouts/main/sass/Feedback.scss
Normal file
38
Frontend/pages/layouts/main/sass/Feedback.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
.feedback {
|
||||
position: absolute;
|
||||
inset: 0px;
|
||||
z-index: 2;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
|
||||
|
||||
.notificationcontainer {
|
||||
position: absolute;
|
||||
bottom: -50px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
|
||||
.notification {
|
||||
padding: 10px 100px;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
|
||||
&.info {
|
||||
|
||||
background: var(--main-hover-color);
|
||||
box-shadow: 0px 0px 4px #243d5e;
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: var(--color-red-hover);
|
||||
box-shadow: 0px 0px 4px #431616;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Frontend/pages/layouts/main/sass/Input.scss
Normal file
37
Frontend/pages/layouts/main/sass/Input.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
border: 2px solid var(--main-hover-color);
|
||||
transition-duration: .2s;
|
||||
border-radius: var(--border-radius);
|
||||
padding: 5px 10px;
|
||||
background: var(--background);
|
||||
color: white;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-size: 12px;
|
||||
|
||||
|
||||
&:hover {
|
||||
border: 2px solid var(--main-color);
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
border: 2px solid var(--main-color);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="color"] {
|
||||
overflow: hidden;
|
||||
|
||||
&::-webkit-color-swatch-wrapper {
|
||||
padding: 0px;
|
||||
width: calc(100% + 22px);
|
||||
height: calc(100% + 12px);
|
||||
margin-top: -6px;
|
||||
margin-left: -11px;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
67
Frontend/pages/layouts/main/sass/Layout.scss
Normal file
67
Frontend/pages/layouts/main/sass/Layout.scss
Normal file
@@ -0,0 +1,67 @@
|
||||
:root {
|
||||
--background: #323232;
|
||||
--main-color: #4676b7;
|
||||
--main-hover-color: #305383;
|
||||
--main-secondary-color: #253e5e;
|
||||
--border-radius: 10px;
|
||||
--small-border-radius: 10px;
|
||||
--panel-color: #222;
|
||||
--subpanel-color: #2c2c2c;
|
||||
--color-red: #af2a20;
|
||||
--color-red-hover: #76201a;
|
||||
--color-green: #195e19;
|
||||
--color-green-hover: #124412;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: white;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
.headercontainer {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
margin-left: 10px;
|
||||
|
||||
.main {
|
||||
font-size: 24px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
color: lightgrey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bodycontainer {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
|
||||
|
||||
}
|
||||
1
Frontend/pages/layouts/main/script.js
Normal file
1
Frontend/pages/layouts/main/script.js
Normal file
@@ -0,0 +1 @@
|
||||
function ce(e,n,t,i,o,s){var c=document.createElement(e);if(n&&("string"==typeof n?c.classList.add(n):(e=c.classList).add.apply(e,n)),t)for(var r in t)c.setAttribute(r,t[r]);if(i&&(c.innerText=i),o&&(c.innerHTML=o),s)for(var r in s)c.style[r]=s[r];return c}var notificationTimeout,UndeckedNotification,notificationOpen=!1;window.addEventListener("DOMContentLoaded",function(){var o={isOpen:!(UndeckedNotification=function(e,n,t){o.open(e,n=void 0===n?"info":n,t=void 0===t?2e3:t)}),timeout:null,transitionDuration:200,elements:{container:document.querySelector(".feedback").querySelector(".notificationcontainer"),box:document.querySelector(".feedback").querySelector(".notification")},open:function(e,n,t){void 0===n&&(n="info");function i(){o.elements.box.classList.remove("info","error"),o.elements.box.classList.add(n),o.elements.box.innerText=e,o.elements.container.style.transitionTimingFunction="ease-out",o.elements.container.style.transitionDuration="".concat(o.transitionDuration,"ms"),o.elements.container.style.bottom="50px",o.isOpen=!0,o.timeout=setTimeout(o.close,t),o.elements.box.onclick=function(){return o.close()}}1==o.isOpen?o.close(i):i()},close:function(e){clearTimeout(o.timeout),o.elements.container.style.transitionTimingFunction="ease-in",o.elements.container.style.bottom="-50px",setTimeout(function(){e&&e(),o.isOpen=!1},o.transitionDuration+100)}}});
|
||||
5
Frontend/pages/layouts/main/style.css
Normal file
5
Frontend/pages/layouts/main/style.css
Normal file
@@ -0,0 +1,5 @@
|
||||
a{color:var(--main-color);text-decoration:none}a:hover{color:var(--main-hover-color)}
|
||||
.button{padding:4px 15px;background:var(--main-color);border-radius:var(--border-radius);text-decoration:none;transition-duration:.2s;cursor:pointer;font-weight:500}.button.secondary{background:#565656}.button.secondary:hover{background:#303030}.button:hover{background:var(--main-hover-color)}
|
||||
.feedback{position:absolute;inset:0;z-index:2;overflow:hidden;pointer-events:none}.feedback .notificationcontainer{position:absolute;bottom:-50px;left:20px;right:20px;display:flex;justify-content:center;align-items:flex-end}.feedback .notificationcontainer .notification{padding:10px 100px;border-radius:var(--border-radius);font-size:14px;font-weight:600;pointer-events:auto;cursor:pointer}.feedback .notificationcontainer .notification.info{background:var(--main-hover-color);box-shadow:0 0 4px #243d5e}.feedback .notificationcontainer .notification.error{background:var(--color-red-hover);box-shadow:0 0 4px #431616}
|
||||
input,select,textarea{border:2px solid var(--main-hover-color);transition-duration:.2s;border-radius:var(--border-radius);padding:5px 10px;background:var(--background);color:#fff;font-family:Montserrat,sans-serif;font-size:12px}input:hover,select:hover,textarea:hover{border:2px solid var(--main-color)}input:active,input:focus,select:active,select:focus,textarea:active,textarea:focus{border:2px solid var(--main-color);outline:0}input[type=color]{overflow:hidden}input[type=color]::-webkit-color-swatch-wrapper{padding:0;width:calc(100% + 22px);height:calc(100% + 12px);margin-top:-6px;margin-left:-11px;outline:0;border:none}
|
||||
:root{--background:#323232;--main-color:#4676b7;--main-hover-color:#305383;--main-secondary-color:#253e5e;--border-radius:10px;--small-border-radius:10px;--panel-color:#222;--subpanel-color:#2c2c2c;--color-red:#af2a20;--color-red-hover:#76201a;--color-green:#195e19;--color-green-hover:#124412}body{background:var(--background);color:#fff;padding:0;margin:0;font-family:Montserrat,sans-serif}.headercontainer{position:absolute;top:10px;left:10px;right:10px;height:40px;display:flex;justify-content:flex-start;align-items:center}.headercontainer img{height:100%}.headercontainer .title{display:flex;flex-direction:column;align-items:flex-start;margin-left:10px}.headercontainer .title .main{font-size:24px;font-weight:300}.headercontainer .title .sub{font-size:12px;font-weight:300;color:#d3d3d3}.bodycontainer{position:absolute;top:60px;bottom:10px;left:10px;right:10px}
|
||||
20
Frontend/pages/layouts/main/ts/CE.ts
Normal file
20
Frontend/pages/layouts/main/ts/CE.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
function ce(
|
||||
type: string,
|
||||
classList?: string | string[],
|
||||
attributes?: { [key: string]: string },
|
||||
innerText?: string,
|
||||
innerHTML?: string,
|
||||
styling?: { [key: string]: string }
|
||||
) {
|
||||
var element = document.createElement(type);
|
||||
if (classList)
|
||||
if (typeof classList == 'string') element.classList.add(classList);
|
||||
else element.classList.add(...classList);
|
||||
if (attributes) for (var key in attributes) element.setAttribute(key, attributes[key]);
|
||||
if (innerText) element.innerText = innerText;
|
||||
if (innerHTML) element.innerHTML = innerHTML;
|
||||
if (styling) for (var key in styling) element.style[key] = styling[key];
|
||||
return element;
|
||||
}
|
||||
|
||||
// declare var ce:(type:string, classList?:string|string[], attributes?:{[key:string]:string}, innerText?:string, innerHTML?:string) => HTMLDivElement;
|
||||
54
Frontend/pages/layouts/main/ts/Notification.ts
Normal file
54
Frontend/pages/layouts/main/ts/Notification.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
var notificationOpen = false;
|
||||
var notificationTimeout: NodeJS.Timeout;
|
||||
|
||||
var UndeckedNotification: (message: string, type?: 'info' | 'error', time?: number) => void;
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
UndeckedNotification = (message: string, type: 'info' | 'error' = 'info', time: number = 2000) => {
|
||||
_UndeckedNotification.open(message, type, time);
|
||||
};
|
||||
|
||||
var _UndeckedNotification = {
|
||||
isOpen: false,
|
||||
timeout: null as NodeJS.Timeout,
|
||||
transitionDuration: 200,
|
||||
|
||||
elements: {
|
||||
container: document.querySelector('.feedback').querySelector('.notificationcontainer') as HTMLDivElement,
|
||||
box: document.querySelector('.feedback').querySelector('.notification') as HTMLDivElement
|
||||
},
|
||||
|
||||
open: (message: string, type: 'info' | 'error' = 'info', time: number) => {
|
||||
var open = () => {
|
||||
_UndeckedNotification.elements.box.classList.remove('info', 'error');
|
||||
_UndeckedNotification.elements.box.classList.add(type);
|
||||
_UndeckedNotification.elements.box.innerText = message;
|
||||
_UndeckedNotification.elements.container.style.transitionTimingFunction = 'ease-out';
|
||||
_UndeckedNotification.elements.container.style.transitionDuration = `${_UndeckedNotification.transitionDuration}ms`;
|
||||
_UndeckedNotification.elements.container.style.bottom = '50px';
|
||||
|
||||
_UndeckedNotification.isOpen = true;
|
||||
|
||||
_UndeckedNotification.timeout = setTimeout(_UndeckedNotification.close, time);
|
||||
|
||||
_UndeckedNotification.elements.box.onclick = () => _UndeckedNotification.close();
|
||||
};
|
||||
|
||||
if (_UndeckedNotification.isOpen == true) {
|
||||
_UndeckedNotification.close(open);
|
||||
} else open();
|
||||
},
|
||||
|
||||
close: (callback?: Function) => {
|
||||
clearTimeout(_UndeckedNotification.timeout);
|
||||
_UndeckedNotification.elements.container.style.transitionTimingFunction = 'ease-in';
|
||||
_UndeckedNotification.elements.container.style.bottom = '-50px';
|
||||
setTimeout(() => {
|
||||
if (callback) callback();
|
||||
_UndeckedNotification.isOpen = false;
|
||||
}, _UndeckedNotification.transitionDuration + 100);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// declare var UndeckedNotification: (message:string, type?:'info'|"error", time?:number) => void;
|
||||
6
Frontend/pages/layouts/main/tsconfig.json
Normal file
6
Frontend/pages/layouts/main/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"inlineSourceMap": true,
|
||||
"noImplicitUseStrict": true
|
||||
}
|
||||
}
|
||||
265
Frontend/pages/settings/package-lock.json
generated
Normal file
265
Frontend/pages/settings/package-lock.json
generated
Normal file
@@ -0,0 +1,265 @@
|
||||
{
|
||||
"name": "settings",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "settings",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"socket.io-client": "^4.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
|
||||
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
|
||||
},
|
||||
"node_modules/backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
|
||||
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.0",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~8.2.3",
|
||||
"xmlhttprequest-ssl": "~2.0.0",
|
||||
"yeast": "0.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
|
||||
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
|
||||
"dependencies": {
|
||||
"@socket.io/base64-arraybuffer": "~1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/parseqs": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||
},
|
||||
"node_modules/parseuri": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
|
||||
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"backo2": "~1.0.2",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.1.1",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
|
||||
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
|
||||
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@socket.io/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ=="
|
||||
},
|
||||
"@socket.io/component-emitter": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
|
||||
"integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q=="
|
||||
},
|
||||
"backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz",
|
||||
"integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.0",
|
||||
"has-cors": "1.1.0",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~8.2.3",
|
||||
"xmlhttprequest-ssl": "~2.0.0",
|
||||
"yeast": "0.1.2"
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz",
|
||||
"integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==",
|
||||
"requires": {
|
||||
"@socket.io/base64-arraybuffer": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"parseqs": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||
},
|
||||
"parseuri": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz",
|
||||
"integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"backo2": "~1.0.2",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.1.1",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~4.1.1"
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
|
||||
"integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.0.0",
|
||||
"debug": "~4.3.1"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
|
||||
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
|
||||
"requires": {}
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
|
||||
},
|
||||
"yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Frontend/pages/settings/package.json
Normal file
14
Frontend/pages/settings/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "settings",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"socket.io-client": "^4.4.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user