mirror of
https://github.com/lovasoa/whitebophir
synced 2024-11-10 06:24:17 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
05d0ea2d2e
46 changed files with 3721 additions and 3246 deletions
18
.github/workflows/CI.yml
vendored
18
.github/workflows/CI.yml
vendored
|
@ -5,13 +5,12 @@ name: Node.js CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -29,3 +28,16 @@ jobs:
|
||||||
- run: npm test
|
- run: npm test
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
|
|
||||||
|
format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Run Prettier check
|
||||||
|
run: npx prettier --check .
|
||||||
|
|
5
.prettierignore
Normal file
5
.prettierignore
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package.json
|
||||||
|
package-lock.json
|
||||||
|
node_modules/
|
||||||
|
.DS_Store
|
||||||
|
*.html
|
12
.prettierrc
Normal file
12
.prettierrc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"arrowParens": "always",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"embeddedLanguageFormatting": "auto",
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"insertPragma": false,
|
||||||
|
"proseWrap": "preserve",
|
||||||
|
"requirePragma": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"printWidth": 80
|
||||||
|
}
|
28
README.md
28
README.md
|
@ -45,6 +45,7 @@ You can then access WBO at `http://localhost:5001`.
|
||||||
Alternatively, you can run the code with [node.js](https://nodejs.org/) directly, without docker.
|
Alternatively, you can run the code with [node.js](https://nodejs.org/) directly, without docker.
|
||||||
|
|
||||||
First, download the sources:
|
First, download the sources:
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/lovasoa/whitebophir.git
|
git clone https://github.com/lovasoa/whitebophir.git
|
||||||
cd whitebophir
|
cd whitebophir
|
||||||
|
@ -58,14 +59,17 @@ npm install --production
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, you can start the server:
|
Finally, you can start the server:
|
||||||
|
|
||||||
```
|
```
|
||||||
PORT=5001 npm start
|
PORT=5001 npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
This will run WBO directly on your machine, on port 5001, without any isolation from the other services. You can also use an invokation like
|
This will run WBO directly on your machine, on port 5001, without any isolation from the other services. You can also use an invokation like
|
||||||
|
|
||||||
```
|
```
|
||||||
PORT=5001 HOST=127.0.0.1 npm start
|
PORT=5001 HOST=127.0.0.1 npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
to make whitebophir only listen on the loopback device. This is useful if you want to put whitebophir behind a reverse proxy.
|
to make whitebophir only listen on the loopback device. This is useful if you want to put whitebophir behind a reverse proxy.
|
||||||
|
|
||||||
### Running WBO on a subfolder
|
### Running WBO on a subfolder
|
||||||
|
@ -88,8 +92,8 @@ The `AUTH_SECRET_KEY` variable in [`configuration.js`](./server/configuration.js
|
||||||
Within the payload, you can declare the user's roles as an array.
|
Within the payload, you can declare the user's roles as an array.
|
||||||
Currently the only accepted roles are `moderator` and `editor`.
|
Currently the only accepted roles are `moderator` and `editor`.
|
||||||
|
|
||||||
- `moderator` will give the user an additional tool to wipe all data from the board. To declare this role, see the example below.
|
- `moderator` will give the user an additional tool to wipe all data from the board. To declare this role, see the example below.
|
||||||
- `editor` will give the user the ability to edit the board. This is the default role for all users.
|
- `editor` will give the user the ability to edit the board. This is the default role for all users.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -98,6 +102,7 @@ Currently the only accepted roles are `moderator` and `editor`.
|
||||||
"roles": ["moderator"]
|
"roles": ["moderator"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Moderators have access to the Clear tool, which will wipe all content from the board.
|
Moderators have access to the Clear tool, which will wipe all content from the board.
|
||||||
|
|
||||||
## Board name verification in the JWT
|
## Board name verification in the JWT
|
||||||
|
@ -108,9 +113,15 @@ To check for a valid board name just add the board name to the role with a ":".
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"roles": ["moderator:<boardName1>","moderator:<boardName2>","editor:<boardName3>","editor:<boardName4>"]
|
"roles": [
|
||||||
|
"moderator:<boardName1>",
|
||||||
|
"moderator:<boardName2>",
|
||||||
|
"editor:<boardName3>",
|
||||||
|
"editor:<boardName4>"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
eg, `http://myboard.com/boards/mySecretBoardName?token={token}`
|
eg, `http://myboard.com/boards/mySecretBoardName?token={token}`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -128,9 +139,10 @@ You can now be sure that only users who have the correct token have access to th
|
||||||
When you start a WBO server, it loads its configuration from several environment variables.
|
When you start a WBO server, it loads its configuration from several environment variables.
|
||||||
You can see a list of these variables in [`configuration.js`](./server/configuration.js).
|
You can see a list of these variables in [`configuration.js`](./server/configuration.js).
|
||||||
Some important environment variables are :
|
Some important environment variables are :
|
||||||
- `WBO_HISTORY_DIR` : configures the directory where the boards are saved. Defaults to `./server-data/`.
|
|
||||||
- `WBO_MAX_EMIT_COUNT` : the maximum number of messages that a client can send per unit of time. Increase this value if you want smoother drawings, at the expense of being susceptible to denial of service attacks if your server does not have enough processing power. By default, the units of this quantity are messages per 4 seconds, and the default value is `192`.
|
- `WBO_HISTORY_DIR` : configures the directory where the boards are saved. Defaults to `./server-data/`.
|
||||||
- `AUTH_SECRET_KEY` : If you would like to authenticate your boards using jwt, this declares the secret key.
|
- `WBO_MAX_EMIT_COUNT` : the maximum number of messages that a client can send per unit of time. Increase this value if you want smoother drawings, at the expense of being susceptible to denial of service attacks if your server does not have enough processing power. By default, the units of this quantity are messages per 4 seconds, and the default value is `192`.
|
||||||
|
- `AUTH_SECRET_KEY` : If you would like to authenticate your boards using jwt, this declares the secret key.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
@ -146,5 +158,5 @@ metrics collection agent.
|
||||||
|
|
||||||
Example: `docker run -e STATSD_URL=udp://127.0.0.1:8125 lovasoa/wbo`.
|
Example: `docker run -e STATSD_URL=udp://127.0.0.1:8125 lovasoa/wbo`.
|
||||||
|
|
||||||
- If you use **prometheus**, you can collect the metrics with [statsd-exporter](https://hub.docker.com/r/prom/statsd-exporter).
|
- If you use **prometheus**, you can collect the metrics with [statsd-exporter](https://hub.docker.com/r/prom/statsd-exporter).
|
||||||
- If you use **datadog**, you can collect the metrics with [dogstatsd](https://docs.datadoghq.com/developers/dogstatsd).
|
- If you use **datadog**, you can collect the metrics with [dogstatsd](https://docs.datadoghq.com/developers/dogstatsd).
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
html, body, svg {
|
html,
|
||||||
padding:0;
|
body,
|
||||||
margin:0;
|
svg {
|
||||||
font-family: Liberation sans, sans-serif;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-family:
|
||||||
|
Liberation sans,
|
||||||
|
sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
#canvas {
|
#canvas {
|
||||||
|
@ -16,7 +20,7 @@ html, body, svg {
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
position:fixed;
|
position: fixed;
|
||||||
top: 40%;
|
top: 40%;
|
||||||
left: 30%;
|
left: 30%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@ -57,29 +61,30 @@ html, body, svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu.closed {
|
#menu.closed {
|
||||||
border-radius:3px;
|
border-radius: 3px;
|
||||||
left:10px;
|
left: 10px;
|
||||||
top:10px;
|
top: 10px;
|
||||||
background-color:rgba(100,200,255,0.7);
|
background-color: rgba(100, 200, 255, 0.7);
|
||||||
width:6vw;
|
width: 6vw;
|
||||||
height:2em;
|
height: 2em;
|
||||||
transition-duration:1s;
|
transition-duration: 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu h2{ /*Menu title ("Menu")*/
|
#menu h2 {
|
||||||
|
/*Menu title ("Menu")*/
|
||||||
display: none;
|
display: none;
|
||||||
font-size:4vh;
|
font-size: 4vh;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
letter-spacing:.5vw;
|
letter-spacing: 0.5vw;
|
||||||
text-shadow: 0px 0px 5px white;
|
text-shadow: 0px 0px 5px white;
|
||||||
color:black;
|
color: black;
|
||||||
padding:0;
|
padding: 0;
|
||||||
margin:0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tools {
|
#menu .tools {
|
||||||
list-style-type:none;
|
list-style-type: none;
|
||||||
padding:0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings {
|
#settings {
|
||||||
|
@ -97,20 +102,20 @@ html, body, svg {
|
||||||
supported by Chrome, Opera and Firefox */
|
supported by Chrome, Opera and Firefox */
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
list-style-position:inside;
|
list-style-position: inside;
|
||||||
border:1px solid #eeeeee;
|
border: 1px solid #eeeeee;
|
||||||
text-decoration:none;
|
text-decoration: none;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
max-width: 40px;
|
max-width: 40px;
|
||||||
transition-duration: .2s;
|
transition-duration: 0.2s;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
box-shadow: inset 0 0 3px #8FA2BC;
|
box-shadow: inset 0 0 3px #8fa2bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tool:hover {
|
#menu .tool:hover {
|
||||||
|
@ -133,27 +138,26 @@ html, body, svg {
|
||||||
#menu:focus-within {
|
#menu:focus-within {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .oneTouch:active {
|
#menu .oneTouch:active {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color:#eeeeff;
|
background-color: #eeeeff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tool:active {
|
#menu .tool:active {
|
||||||
box-shadow: inset 0 0 1px #ddeeff;
|
box-shadow: inset 0 0 1px #ddeeff;
|
||||||
background-color:#eeeeff;
|
background-color: #eeeeff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tool.curTool {
|
#menu .tool.curTool {
|
||||||
box-shadow: 0 0 5px #0074D9;
|
box-shadow: 0 0 5px #0074d9;
|
||||||
background: linear-gradient(#96E1FF, #36A2FF);
|
background: linear-gradient(#96e1ff, #36a2ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tool-icon {
|
#menu .tool-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align:center;
|
text-align: center;
|
||||||
width: 35px;
|
width: 35px;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
margin: 2.5px;
|
margin: 2.5px;
|
||||||
|
@ -183,23 +187,22 @@ html, body, svg {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
font-size: .9em;
|
font-size: 0.9em;
|
||||||
line-height: 15px;
|
line-height: 15px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tool.hasSecondary .tool-icon{
|
#menu .tool.hasSecondary .tool-icon {
|
||||||
margin-top:0px;
|
margin-top: 0px;
|
||||||
margin-left:0px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tool .tool-icon.secondaryIcon{
|
#menu .tool .tool-icon.secondaryIcon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .tool.hasSecondary .tool-icon.secondaryIcon{
|
#menu .tool.hasSecondary .tool-icon.secondaryIcon {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
|
@ -209,15 +212,15 @@ html, body, svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
font-size:16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chooseColor {
|
#chooseColor {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height:100%;
|
height: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
color:black;
|
color: black;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -261,14 +264,14 @@ path {
|
||||||
}
|
}
|
||||||
|
|
||||||
text {
|
text {
|
||||||
font-family:"Arial", "Helvetica", sans-serif;
|
font-family: "Arial", "Helvetica", sans-serif;
|
||||||
user-select:none;
|
user-select: none;
|
||||||
-moz-user-select:none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
circle.opcursor {
|
circle.opcursor {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: .1s;
|
transition: 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cursor-me {
|
#cursor-me {
|
||||||
|
@ -280,7 +283,7 @@ circle.opcursor {
|
||||||
#chooseColor {
|
#chooseColor {
|
||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
label.tool-name[for=chooseColor] {
|
label.tool-name[for="chooseColor"] {
|
||||||
line-height: 10px;
|
line-height: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,7 +234,10 @@ footer a:hover {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
box-shadow: -150px 0 150px 150px black;
|
box-shadow: -150px 0 150px 150px black;
|
||||||
background: black;
|
background: black;
|
||||||
transition: width 0.5s, box-shadow 0.3s ease 0.2s, background-color 0.5s;
|
transition:
|
||||||
|
width 0.5s,
|
||||||
|
box-shadow 0.3s ease 0.2s,
|
||||||
|
background-color 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang-selector ul {
|
.lang-selector ul {
|
||||||
|
@ -253,7 +256,10 @@ footer a:hover {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
transition: box-shadow 0.3s, width 0.5s, background-color 0.5s ease 0.3s;
|
transition:
|
||||||
|
box-shadow 0.3s,
|
||||||
|
width 0.5s,
|
||||||
|
background-color 0.5s ease 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang-selector li a {
|
.lang-selector li a {
|
||||||
|
|
|
@ -29,10 +29,10 @@ var Tools = {};
|
||||||
Tools.i18n = (function i18n() {
|
Tools.i18n = (function i18n() {
|
||||||
var translations = JSON.parse(document.getElementById("translations").text);
|
var translations = JSON.parse(document.getElementById("translations").text);
|
||||||
return {
|
return {
|
||||||
"t": function translate(s) {
|
t: function translate(s) {
|
||||||
var key = s.toLowerCase().replace(/ /g, '_');
|
var key = s.toLowerCase().replace(/ /g, "_");
|
||||||
return translations[key] || s;
|
return translations[key] || s;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -66,16 +66,16 @@ Tools.connect = function () {
|
||||||
var params = new URLSearchParams(url.search);
|
var params = new URLSearchParams(url.search);
|
||||||
|
|
||||||
var socket_params = {
|
var socket_params = {
|
||||||
"path": window.location.pathname.split("/boards/")[0] + "/socket.io",
|
path: window.location.pathname.split("/boards/")[0] + "/socket.io",
|
||||||
"reconnection": true,
|
reconnection: true,
|
||||||
"reconnectionDelay": 100, //Make the xhr connections as fast as possible
|
reconnectionDelay: 100, //Make the xhr connections as fast as possible
|
||||||
"timeout": 1000 * 60 * 20 // Timeout after 20 minutes
|
timeout: 1000 * 60 * 20, // Timeout after 20 minutes
|
||||||
}
|
};
|
||||||
if(params.has("token")) {
|
if (params.has("token")) {
|
||||||
socket_params.query = "token=" + params.get("token");
|
socket_params.query = "token=" + params.get("token");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.socket = io.connect('', socket_params);
|
this.socket = io.connect("", socket_params);
|
||||||
|
|
||||||
//Receive draw instructions from the server
|
//Receive draw instructions from the server
|
||||||
this.socket.on("broadcast", function (msg) {
|
this.socket.on("broadcast", function (msg) {
|
||||||
|
@ -86,7 +86,7 @@ Tools.connect = function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on("reconnect", function onReconnection() {
|
this.socket.on("reconnect", function onReconnection() {
|
||||||
Tools.socket.emit('joinboard', Tools.boardName);
|
Tools.socket.emit("joinboard", Tools.boardName);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ Tools.boardName = (function () {
|
||||||
return decodeURIComponent(path[path.length - 1]);
|
return decodeURIComponent(path[path.length - 1]);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
Tools.token = (function() {
|
Tools.token = (function () {
|
||||||
var url = new URL(window.location);
|
var url = new URL(window.location);
|
||||||
var params = new URLSearchParams(url.search);
|
var params = new URLSearchParams(url.search);
|
||||||
return params.get("token");
|
return params.get("token");
|
||||||
|
@ -108,12 +108,13 @@ Tools.socket.emit("getboard", Tools.boardName);
|
||||||
|
|
||||||
function saveBoardNametoLocalStorage() {
|
function saveBoardNametoLocalStorage() {
|
||||||
var boardName = Tools.boardName;
|
var boardName = Tools.boardName;
|
||||||
if (boardName.toLowerCase() === 'anonymous') return;
|
if (boardName.toLowerCase() === "anonymous") return;
|
||||||
var recentBoards, key = "recent-boards";
|
var recentBoards,
|
||||||
|
key = "recent-boards";
|
||||||
try {
|
try {
|
||||||
recentBoards = JSON.parse(localStorage.getItem(key));
|
recentBoards = JSON.parse(localStorage.getItem(key));
|
||||||
if (!Array.isArray(recentBoards)) throw new Error("Invalid type");
|
if (!Array.isArray(recentBoards)) throw new Error("Invalid type");
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
// On localstorage or json error, reset board list
|
// On localstorage or json error, reset board list
|
||||||
recentBoards = [];
|
recentBoards = [];
|
||||||
console.log("Board history loading error", e);
|
console.log("Board history loading error", e);
|
||||||
|
@ -148,19 +149,25 @@ Tools.HTML = {
|
||||||
return this.template.add(function (elem) {
|
return this.template.add(function (elem) {
|
||||||
elem.addEventListener("click", callback);
|
elem.addEventListener("click", callback);
|
||||||
elem.id = "toolID-" + toolName;
|
elem.id = "toolID-" + toolName;
|
||||||
elem.getElementsByClassName("tool-name")[0].textContent = Tools.i18n.t(toolName);
|
elem.getElementsByClassName("tool-name")[0].textContent =
|
||||||
|
Tools.i18n.t(toolName);
|
||||||
var toolIconElem = elem.getElementsByClassName("tool-icon")[0];
|
var toolIconElem = elem.getElementsByClassName("tool-icon")[0];
|
||||||
toolIconElem.src = toolIcon;
|
toolIconElem.src = toolIcon;
|
||||||
toolIconElem.alt = toolIcon;
|
toolIconElem.alt = toolIcon;
|
||||||
if (oneTouch) elem.classList.add("oneTouch");
|
if (oneTouch) elem.classList.add("oneTouch");
|
||||||
elem.title =
|
elem.title =
|
||||||
Tools.i18n.t(toolName) + " (" +
|
Tools.i18n.t(toolName) +
|
||||||
Tools.i18n.t("keyboard shortcut") + ": " +
|
" (" +
|
||||||
toolShortcut + ")" +
|
Tools.i18n.t("keyboard shortcut") +
|
||||||
(Tools.list[toolName].secondary ? " [" + Tools.i18n.t("click_to_toggle") + "]" : "");
|
": " +
|
||||||
|
toolShortcut +
|
||||||
|
")" +
|
||||||
|
(Tools.list[toolName].secondary
|
||||||
|
? " [" + Tools.i18n.t("click_to_toggle") + "]"
|
||||||
|
: "");
|
||||||
if (Tools.list[toolName].secondary) {
|
if (Tools.list[toolName].secondary) {
|
||||||
elem.classList.add('hasSecondary');
|
elem.classList.add("hasSecondary");
|
||||||
var secondaryIcon = elem.getElementsByClassName('secondaryIcon')[0];
|
var secondaryIcon = elem.getElementsByClassName("secondaryIcon")[0];
|
||||||
secondaryIcon.src = Tools.list[toolName].secondary.icon;
|
secondaryIcon.src = Tools.list[toolName].secondary.icon;
|
||||||
toolIconElem.classList.add("primaryIcon");
|
toolIconElem.classList.add("primaryIcon");
|
||||||
}
|
}
|
||||||
|
@ -185,7 +192,8 @@ Tools.HTML = {
|
||||||
|
|
||||||
// Change primary icon
|
// Change primary icon
|
||||||
elem.getElementsByClassName("tool-icon")[0].src = icon;
|
elem.getElementsByClassName("tool-icon")[0].src = icon;
|
||||||
elem.getElementsByClassName("tool-name")[0].textContent = Tools.i18n.t(name);
|
elem.getElementsByClassName("tool-name")[0].textContent =
|
||||||
|
Tools.i18n.t(name);
|
||||||
},
|
},
|
||||||
addStylesheet: function (href) {
|
addStylesheet: function (href) {
|
||||||
//Adds a css stylesheet to the html or svg document
|
//Adds a css stylesheet to the html or svg document
|
||||||
|
@ -201,19 +209,20 @@ Tools.HTML = {
|
||||||
if (button.key) this.addShortcut(button.key, setColor);
|
if (button.key) this.addShortcut(button.key, setColor);
|
||||||
return this.colorPresetTemplate.add(function (elem) {
|
return this.colorPresetTemplate.add(function (elem) {
|
||||||
elem.addEventListener("click", setColor);
|
elem.addEventListener("click", setColor);
|
||||||
elem.id = "color_" + button.color.replace(/^#/, '');
|
elem.id = "color_" + button.color.replace(/^#/, "");
|
||||||
elem.style.backgroundColor = button.color;
|
elem.style.backgroundColor = button.color;
|
||||||
if (button.key) {
|
if (button.key) {
|
||||||
elem.title = Tools.i18n.t("keyboard shortcut") + ": " + button.key;
|
elem.title = Tools.i18n.t("keyboard shortcut") + ": " + button.key;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Tools.list = {}; // An array of all known tools. {"toolName" : {toolObject}}
|
Tools.list = {}; // An array of all known tools. {"toolName" : {toolObject}}
|
||||||
|
|
||||||
Tools.isBlocked = function toolIsBanned(tool) {
|
Tools.isBlocked = function toolIsBanned(tool) {
|
||||||
if (tool.name.includes(",")) throw new Error("Tool Names must not contain a comma");
|
if (tool.name.includes(","))
|
||||||
|
throw new Error("Tool Names must not contain a comma");
|
||||||
return Tools.server_config.BLOCKED_TOOLS.includes(tool.name);
|
return Tools.server_config.BLOCKED_TOOLS.includes(tool.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -224,8 +233,12 @@ Tools.register = function registerTool(newTool) {
|
||||||
if (Tools.isBlocked(newTool)) return;
|
if (Tools.isBlocked(newTool)) return;
|
||||||
|
|
||||||
if (newTool.name in Tools.list) {
|
if (newTool.name in Tools.list) {
|
||||||
console.log("Tools.add: The tool '" + newTool.name + "' is already" +
|
console.log(
|
||||||
"in the list. Updating it...");
|
"Tools.add: The tool '" +
|
||||||
|
newTool.name +
|
||||||
|
"' is already" +
|
||||||
|
"in the list. Updating it...",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Format the new tool correctly
|
//Format the new tool correctly
|
||||||
|
@ -242,7 +255,7 @@ Tools.register = function registerTool(newTool) {
|
||||||
if (pending) {
|
if (pending) {
|
||||||
console.log("Drawing pending messages for '%s'.", newTool.name);
|
console.log("Drawing pending messages for '%s'.", newTool.name);
|
||||||
var msg;
|
var msg;
|
||||||
while (msg = pending.shift()) {
|
while ((msg = pending.shift())) {
|
||||||
//Transmit the message to the tool (precising that it comes from the network)
|
//Transmit the message to the tool (precising that it comes from the network)
|
||||||
newTool.draw(msg, false);
|
newTool.draw(msg, false);
|
||||||
}
|
}
|
||||||
|
@ -262,13 +275,20 @@ Tools.add = function (newTool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add the tool to the GUI
|
//Add the tool to the GUI
|
||||||
Tools.HTML.addTool(newTool.name, newTool.icon, newTool.iconHTML, newTool.shortcut, newTool.oneTouch);
|
Tools.HTML.addTool(
|
||||||
|
newTool.name,
|
||||||
|
newTool.icon,
|
||||||
|
newTool.iconHTML,
|
||||||
|
newTool.shortcut,
|
||||||
|
newTool.oneTouch,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Tools.change = function (toolName) {
|
Tools.change = function (toolName) {
|
||||||
var newTool = Tools.list[toolName];
|
var newTool = Tools.list[toolName];
|
||||||
var oldTool = Tools.curTool;
|
var oldTool = Tools.curTool;
|
||||||
if (!newTool) throw new Error("Trying to select a tool that has never been added!");
|
if (!newTool)
|
||||||
|
throw new Error("Trying to select a tool that has never been added!");
|
||||||
if (newTool === oldTool) {
|
if (newTool === oldTool) {
|
||||||
if (newTool.secondary) {
|
if (newTool.secondary) {
|
||||||
newTool.secondary.active = !newTool.secondary.active;
|
newTool.secondary.active = !newTool.secondary.active;
|
||||||
|
@ -280,7 +300,7 @@ Tools.change = function (toolName) {
|
||||||
}
|
}
|
||||||
if (!newTool.oneTouch) {
|
if (!newTool.oneTouch) {
|
||||||
//Update the GUI
|
//Update the GUI
|
||||||
var curToolName = (Tools.curTool) ? Tools.curTool.name : "";
|
var curToolName = Tools.curTool ? Tools.curTool.name : "";
|
||||||
try {
|
try {
|
||||||
Tools.HTML.changeTool(curToolName, toolName);
|
Tools.HTML.changeTool(curToolName, toolName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -314,7 +334,7 @@ Tools.addToolListeners = function addToolListeners(tool) {
|
||||||
for (var event in tool.compiledListeners) {
|
for (var event in tool.compiledListeners) {
|
||||||
var listener = tool.compiledListeners[event];
|
var listener = tool.compiledListeners[event];
|
||||||
var target = listener.target || Tools.board;
|
var target = listener.target || Tools.board;
|
||||||
target.addEventListener(event, listener, { 'passive': false });
|
target.addEventListener(event, listener, { passive: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -331,7 +351,11 @@ Tools.removeToolListeners = function removeToolListeners(tool) {
|
||||||
(function () {
|
(function () {
|
||||||
// Handle secondary tool switch with shift (key code 16)
|
// Handle secondary tool switch with shift (key code 16)
|
||||||
function handleShift(active, evt) {
|
function handleShift(active, evt) {
|
||||||
if (evt.keyCode === 16 && Tools.curTool.secondary && Tools.curTool.secondary.active !== active) {
|
if (
|
||||||
|
evt.keyCode === 16 &&
|
||||||
|
Tools.curTool.secondary &&
|
||||||
|
Tools.curTool.secondary.active !== active
|
||||||
|
) {
|
||||||
Tools.change(Tools.curTool.name);
|
Tools.change(Tools.curTool.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,10 +369,10 @@ Tools.send = function (data, toolName) {
|
||||||
d.tool = toolName;
|
d.tool = toolName;
|
||||||
Tools.applyHooks(Tools.messageHooks, d);
|
Tools.applyHooks(Tools.messageHooks, d);
|
||||||
var message = {
|
var message = {
|
||||||
"board": Tools.boardName,
|
board: Tools.boardName,
|
||||||
"data": d
|
data: d,
|
||||||
};
|
};
|
||||||
Tools.socket.emit('broadcast', message);
|
Tools.socket.emit("broadcast", message);
|
||||||
};
|
};
|
||||||
|
|
||||||
Tools.drawAndSend = function (data, tool) {
|
Tools.drawAndSend = function (data, tool) {
|
||||||
|
@ -376,9 +400,14 @@ function messageForTool(message) {
|
||||||
else Tools.pendingMessages[name].push(message);
|
else Tools.pendingMessages[name].push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.tool !== 'Hand' && message.transform != null) {
|
if (message.tool !== "Hand" && message.transform != null) {
|
||||||
//this message has special info for the mover
|
//this message has special info for the mover
|
||||||
messageForTool({ tool: 'Hand', type: 'update', transform: message.transform, id: message.id});
|
messageForTool({
|
||||||
|
tool: "Hand",
|
||||||
|
type: "update",
|
||||||
|
transform: message.transform,
|
||||||
|
id: message.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +422,8 @@ function batchCall(fn, args) {
|
||||||
return Promise.all(batch.map(fn))
|
return Promise.all(batch.map(fn))
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return new Promise(requestAnimationFrame);
|
return new Promise(requestAnimationFrame);
|
||||||
}).then(batchCall.bind(null, fn, rest));
|
})
|
||||||
|
.then(batchCall.bind(null, fn, rest));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,14 +451,15 @@ window.addEventListener("focus", function () {
|
||||||
|
|
||||||
function updateDocumentTitle() {
|
function updateDocumentTitle() {
|
||||||
document.title =
|
document.title =
|
||||||
(Tools.unreadMessagesCount ? '(' + Tools.unreadMessagesCount + ') ' : '') +
|
(Tools.unreadMessagesCount ? "(" + Tools.unreadMessagesCount + ") " : "") +
|
||||||
Tools.boardName +
|
Tools.boardName +
|
||||||
" | WBO";
|
" | WBO";
|
||||||
}
|
}
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
// Scroll and hash handling
|
// Scroll and hash handling
|
||||||
var scrollTimeout, lastStateUpdate = Date.now();
|
var scrollTimeout,
|
||||||
|
lastStateUpdate = Date.now();
|
||||||
|
|
||||||
window.addEventListener("scroll", function onScroll() {
|
window.addEventListener("scroll", function onScroll() {
|
||||||
var scale = Tools.getScale();
|
var scale = Tools.getScale();
|
||||||
|
@ -437,8 +468,12 @@ function updateDocumentTitle() {
|
||||||
|
|
||||||
clearTimeout(scrollTimeout);
|
clearTimeout(scrollTimeout);
|
||||||
scrollTimeout = setTimeout(function updateHistory() {
|
scrollTimeout = setTimeout(function updateHistory() {
|
||||||
var hash = '#' + (x | 0) + ',' + (y | 0) + ',' + Tools.getScale().toFixed(1);
|
var hash =
|
||||||
if (Date.now() - lastStateUpdate > 5000 && hash !== window.location.hash) {
|
"#" + (x | 0) + "," + (y | 0) + "," + Tools.getScale().toFixed(1);
|
||||||
|
if (
|
||||||
|
Date.now() - lastStateUpdate > 5000 &&
|
||||||
|
hash !== window.location.hash
|
||||||
|
) {
|
||||||
window.history.pushState({}, "", hash);
|
window.history.pushState({}, "", hash);
|
||||||
lastStateUpdate = Date.now();
|
lastStateUpdate = Date.now();
|
||||||
} else {
|
} else {
|
||||||
|
@ -448,7 +483,7 @@ function updateDocumentTitle() {
|
||||||
});
|
});
|
||||||
|
|
||||||
function setScrollFromHash() {
|
function setScrollFromHash() {
|
||||||
var coords = window.location.hash.slice(1).split(',');
|
var coords = window.location.hash.slice(1).split(",");
|
||||||
var x = coords[0] | 0;
|
var x = coords[0] | 0;
|
||||||
var y = coords[1] | 0;
|
var y = coords[1] | 0;
|
||||||
var scale = parseFloat(coords[2]);
|
var scale = parseFloat(coords[2]);
|
||||||
|
@ -464,7 +499,8 @@ function updateDocumentTitle() {
|
||||||
|
|
||||||
function resizeCanvas(m) {
|
function resizeCanvas(m) {
|
||||||
//Enlarge the canvas whenever something is drawn near its border
|
//Enlarge the canvas whenever something is drawn near its border
|
||||||
var x = m.x | 0, y = m.y | 0
|
var x = m.x | 0,
|
||||||
|
y = m.y | 0;
|
||||||
var MAX_BOARD_SIZE = Tools.server_config.MAX_BOARD_SIZE || 65536; // Maximum value for any x or y on the board
|
var MAX_BOARD_SIZE = Tools.server_config.MAX_BOARD_SIZE || 65536; // Maximum value for any x or y on the board
|
||||||
if (x > Tools.svg.width.baseVal.value - 2000) {
|
if (x > Tools.svg.width.baseVal.value - 2000) {
|
||||||
Tools.svg.width.baseVal.value = Math.min(x + 2000, MAX_BOARD_SIZE);
|
Tools.svg.width.baseVal.value = Math.min(x + 2000, MAX_BOARD_SIZE);
|
||||||
|
@ -486,36 +522,38 @@ Tools.messageHooks = [resizeCanvas, updateUnreadCount];
|
||||||
Tools.scale = 1.0;
|
Tools.scale = 1.0;
|
||||||
var scaleTimeout = null;
|
var scaleTimeout = null;
|
||||||
Tools.setScale = function setScale(scale) {
|
Tools.setScale = function setScale(scale) {
|
||||||
var fullScale = Math.max(window.innerWidth, window.innerHeight) / Tools.server_config.MAX_BOARD_SIZE;
|
var fullScale =
|
||||||
|
Math.max(window.innerWidth, window.innerHeight) /
|
||||||
|
Tools.server_config.MAX_BOARD_SIZE;
|
||||||
var minScale = Math.max(0.1, fullScale);
|
var minScale = Math.max(0.1, fullScale);
|
||||||
var maxScale = 10;
|
var maxScale = 10;
|
||||||
if (isNaN(scale)) scale = 1;
|
if (isNaN(scale)) scale = 1;
|
||||||
scale = Math.max(minScale, Math.min(maxScale, scale));
|
scale = Math.max(minScale, Math.min(maxScale, scale));
|
||||||
Tools.svg.style.willChange = 'transform';
|
Tools.svg.style.willChange = "transform";
|
||||||
Tools.svg.style.transform = 'scale(' + scale + ')';
|
Tools.svg.style.transform = "scale(" + scale + ")";
|
||||||
clearTimeout(scaleTimeout);
|
clearTimeout(scaleTimeout);
|
||||||
scaleTimeout = setTimeout(function () {
|
scaleTimeout = setTimeout(function () {
|
||||||
Tools.svg.style.willChange = 'auto';
|
Tools.svg.style.willChange = "auto";
|
||||||
}, 1000);
|
}, 1000);
|
||||||
Tools.scale = scale;
|
Tools.scale = scale;
|
||||||
return scale;
|
return scale;
|
||||||
}
|
};
|
||||||
Tools.getScale = function getScale() {
|
Tools.getScale = function getScale() {
|
||||||
return Tools.scale;
|
return Tools.scale;
|
||||||
}
|
};
|
||||||
|
|
||||||
//List of hook functions that will be applied to tools before adding them
|
//List of hook functions that will be applied to tools before adding them
|
||||||
Tools.toolHooks = [
|
Tools.toolHooks = [
|
||||||
function checkToolAttributes(tool) {
|
function checkToolAttributes(tool) {
|
||||||
if (typeof (tool.name) !== "string") throw "A tool must have a name";
|
if (typeof tool.name !== "string") throw "A tool must have a name";
|
||||||
if (typeof (tool.listeners) !== "object") {
|
if (typeof tool.listeners !== "object") {
|
||||||
tool.listeners = {};
|
tool.listeners = {};
|
||||||
}
|
}
|
||||||
if (typeof (tool.onstart) !== "function") {
|
if (typeof tool.onstart !== "function") {
|
||||||
tool.onstart = function () { };
|
tool.onstart = function () {};
|
||||||
}
|
}
|
||||||
if (typeof (tool.onquit) !== "function") {
|
if (typeof tool.onquit !== "function") {
|
||||||
tool.onquit = function () { };
|
tool.onquit = function () {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function compileListeners(tool) {
|
function compileListeners(tool) {
|
||||||
|
@ -526,16 +564,18 @@ Tools.toolHooks = [
|
||||||
var compiled = tool.compiledListeners || {};
|
var compiled = tool.compiledListeners || {};
|
||||||
tool.compiledListeners = compiled;
|
tool.compiledListeners = compiled;
|
||||||
|
|
||||||
function compile(listener) { //closure
|
function compile(listener) {
|
||||||
return (function listen(evt) {
|
//closure
|
||||||
|
return function listen(evt) {
|
||||||
var x = evt.pageX / Tools.getScale(),
|
var x = evt.pageX / Tools.getScale(),
|
||||||
y = evt.pageY / Tools.getScale();
|
y = evt.pageY / Tools.getScale();
|
||||||
return listener(x, y, evt, false);
|
return listener(x, y, evt, false);
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileTouch(listener) { //closure
|
function compileTouch(listener) {
|
||||||
return (function touchListen(evt) {
|
//closure
|
||||||
|
return function touchListen(evt) {
|
||||||
//Currently, we don't handle multitouch
|
//Currently, we don't handle multitouch
|
||||||
if (evt.changedTouches.length === 1) {
|
if (evt.changedTouches.length === 1) {
|
||||||
//evt.preventDefault();
|
//evt.preventDefault();
|
||||||
|
@ -545,19 +585,27 @@ Tools.toolHooks = [
|
||||||
return listener(x, y, evt, true);
|
return listener(x, y, evt, true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapUnsetHover(f, toolName) {
|
function wrapUnsetHover(f, toolName) {
|
||||||
return (function unsetHover(evt) {
|
return function unsetHover(evt) {
|
||||||
document.activeElement && document.activeElement.blur && document.activeElement.blur();
|
document.activeElement &&
|
||||||
|
document.activeElement.blur &&
|
||||||
|
document.activeElement.blur();
|
||||||
return f(evt);
|
return f(evt);
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listeners.press) {
|
if (listeners.press) {
|
||||||
compiled["mousedown"] = wrapUnsetHover(compile(listeners.press), tool.name);
|
compiled["mousedown"] = wrapUnsetHover(
|
||||||
compiled["touchstart"] = wrapUnsetHover(compileTouch(listeners.press), tool.name);
|
compile(listeners.press),
|
||||||
|
tool.name,
|
||||||
|
);
|
||||||
|
compiled["touchstart"] = wrapUnsetHover(
|
||||||
|
compileTouch(listeners.press),
|
||||||
|
tool.name,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (listeners.move) {
|
if (listeners.move) {
|
||||||
compiled["mousemove"] = compile(listeners.move);
|
compiled["mousemove"] = compile(listeners.move);
|
||||||
|
@ -572,7 +620,7 @@ Tools.toolHooks = [
|
||||||
compiled["touchend"] = releaseTouch;
|
compiled["touchend"] = releaseTouch;
|
||||||
compiled["touchcancel"] = releaseTouch;
|
compiled["touchcancel"] = releaseTouch;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
Tools.applyHooks = function (hooks, object) {
|
Tools.applyHooks = function (hooks, object) {
|
||||||
|
@ -582,12 +630,11 @@ Tools.applyHooks = function (hooks, object) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
|
||||||
Tools.generateUID = function (prefix, suffix) {
|
Tools.generateUID = function (prefix, suffix) {
|
||||||
var uid = Date.now().toString(36); //Create the uids in chronological order
|
var uid = Date.now().toString(36); //Create the uids in chronological order
|
||||||
uid += (Math.round(Math.random() * 36)).toString(36); //Add a random character at the end
|
uid += Math.round(Math.random() * 36).toString(36); //Add a random character at the end
|
||||||
if (prefix) uid = prefix + uid;
|
if (prefix) uid = prefix + uid;
|
||||||
if (suffix) uid = uid + suffix;
|
if (suffix) uid = uid + suffix;
|
||||||
return uid;
|
return uid;
|
||||||
|
@ -595,7 +642,7 @@ Tools.generateUID = function (prefix, suffix) {
|
||||||
|
|
||||||
Tools.createSVGElement = function createSVGElement(name, attrs) {
|
Tools.createSVGElement = function createSVGElement(name, attrs) {
|
||||||
var elem = document.createElementNS(Tools.svg.namespaceURI, name);
|
var elem = document.createElementNS(Tools.svg.namespaceURI, name);
|
||||||
if (typeof (attrs) !== "object") return elem;
|
if (typeof attrs !== "object") return elem;
|
||||||
Object.keys(attrs).forEach(function (key, i) {
|
Object.keys(attrs).forEach(function (key, i) {
|
||||||
elem.setAttributeNS(null, key, attrs[key]);
|
elem.setAttributeNS(null, key, attrs[key]);
|
||||||
});
|
});
|
||||||
|
@ -608,17 +655,17 @@ Tools.positionElement = function (elem, x, y) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Tools.colorPresets = [
|
Tools.colorPresets = [
|
||||||
{ color: "#001f3f", key: '1' },
|
{ color: "#001f3f", key: "1" },
|
||||||
{ color: "#FF4136", key: '2' },
|
{ color: "#FF4136", key: "2" },
|
||||||
{ color: "#0074D9", key: '3' },
|
{ color: "#0074D9", key: "3" },
|
||||||
{ color: "#FF851B", key: '4' },
|
{ color: "#FF851B", key: "4" },
|
||||||
{ color: "#FFDC00", key: '5' },
|
{ color: "#FFDC00", key: "5" },
|
||||||
{ color: "#3D9970", key: '6' },
|
{ color: "#3D9970", key: "6" },
|
||||||
{ color: "#91E99B", key: '7' },
|
{ color: "#91E99B", key: "7" },
|
||||||
{ color: "#90468b", key: '8' },
|
{ color: "#90468b", key: "8" },
|
||||||
{ color: "#7FDBFF", key: '9' },
|
{ color: "#7FDBFF", key: "9" },
|
||||||
{ color: "#AAAAAA", key: '0' },
|
{ color: "#AAAAAA", key: "0" },
|
||||||
{ color: "#E65194" }
|
{ color: "#E65194" },
|
||||||
];
|
];
|
||||||
|
|
||||||
Tools.color_chooser = document.getElementById("chooseColor");
|
Tools.color_chooser = document.getElementById("chooseColor");
|
||||||
|
@ -631,7 +678,9 @@ Tools.getColor = (function color() {
|
||||||
var color_index = (Math.random() * Tools.colorPresets.length) | 0;
|
var color_index = (Math.random() * Tools.colorPresets.length) | 0;
|
||||||
var initial_color = Tools.colorPresets[color_index].color;
|
var initial_color = Tools.colorPresets[color_index].color;
|
||||||
Tools.setColor(initial_color);
|
Tools.setColor(initial_color);
|
||||||
return function () { return Tools.color_chooser.value; };
|
return function () {
|
||||||
|
return Tools.color_chooser.value;
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
Tools.colorPresets.forEach(Tools.HTML.addColorButton.bind(Tools.HTML));
|
Tools.colorPresets.forEach(Tools.HTML.addColorButton.bind(Tools.HTML));
|
||||||
|
@ -651,12 +700,17 @@ Tools.setSize = (function size() {
|
||||||
|
|
||||||
chooser.onchange = chooser.oninput = update;
|
chooser.onchange = chooser.oninput = update;
|
||||||
return function (value) {
|
return function (value) {
|
||||||
if (value !== null && value !== undefined) { chooser.value = value; update(); }
|
if (value !== null && value !== undefined) {
|
||||||
|
chooser.value = value;
|
||||||
|
update();
|
||||||
|
}
|
||||||
return parseInt(chooser.value);
|
return parseInt(chooser.value);
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
Tools.getSize = (function () { return Tools.setSize() });
|
Tools.getSize = function () {
|
||||||
|
return Tools.setSize();
|
||||||
|
};
|
||||||
|
|
||||||
Tools.getOpacity = (function opacity() {
|
Tools.getOpacity = (function opacity() {
|
||||||
var chooser = document.getElementById("chooseOpacity");
|
var chooser = document.getElementById("chooseOpacity");
|
||||||
|
@ -673,7 +727,6 @@ Tools.getOpacity = (function opacity() {
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
//Scale the canvas on load
|
//Scale the canvas on load
|
||||||
Tools.svg.width.baseVal.value = document.body.clientWidth;
|
Tools.svg.width.baseVal.value = document.body.clientWidth;
|
||||||
Tools.svg.height.baseVal.value = document.body.clientHeight;
|
Tools.svg.height.baseVal.value = document.body.clientHeight;
|
||||||
|
@ -696,15 +749,14 @@ Tools.svg.height.baseVal.value = document.body.clientHeight;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
var pos = {top: 0, scroll:0};
|
var pos = { top: 0, scroll: 0 };
|
||||||
var menu = document.getElementById("menu");
|
var menu = document.getElementById("menu");
|
||||||
function menu_mousedown(evt) {
|
function menu_mousedown(evt) {
|
||||||
pos = {
|
pos = {
|
||||||
top: menu.scrollTop,
|
top: menu.scrollTop,
|
||||||
scroll: evt.clientY
|
scroll: evt.clientY,
|
||||||
}
|
};
|
||||||
menu.addEventListener("mousemove", menu_mousemove);
|
menu.addEventListener("mousemove", menu_mousemove);
|
||||||
document.addEventListener("mouseup", menu_mouseup);
|
document.addEventListener("mouseup", menu_mouseup);
|
||||||
}
|
}
|
||||||
|
@ -717,4 +769,4 @@ Tools.svg.height.baseVal.value = document.body.clientHeight;
|
||||||
document.removeEventListener("mouseup", menu_mouseup);
|
document.removeEventListener("mouseup", menu_mouseup);
|
||||||
}
|
}
|
||||||
menu.addEventListener("mousedown", menu_mousedown);
|
menu.addEventListener("mousedown", menu_mousedown);
|
||||||
})()
|
})();
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*jshint bitwise:false*/
|
/*jshint bitwise:false*/
|
||||||
|
|
||||||
// ==ClosureCompiler==
|
// ==ClosureCompiler==
|
||||||
|
@ -35,12 +34,14 @@
|
||||||
// @use_types_for_optimization true
|
// @use_types_for_optimization true
|
||||||
// ==/ClosureCompiler==
|
// ==/ClosureCompiler==
|
||||||
|
|
||||||
var canvascolor = (function() {//Code Isolation
|
var canvascolor = (function () {
|
||||||
|
//Code Isolation
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function addCSS () {
|
(function addCSS() {
|
||||||
var styleTag = document.createElement("style");
|
var styleTag = document.createElement("style");
|
||||||
styleTag.innerHTML = [".canvascolor-container{",
|
styleTag.innerHTML = [
|
||||||
|
".canvascolor-container{",
|
||||||
"background-color:black;",
|
"background-color:black;",
|
||||||
"border-radius:5px;",
|
"border-radius:5px;",
|
||||||
"overflow:hidden;",
|
"overflow:hidden;",
|
||||||
|
@ -57,38 +58,45 @@ var canvascolor = (function() {//Code Isolation
|
||||||
".canvascolor-history > div{",
|
".canvascolor-history > div{",
|
||||||
"margin:2px;",
|
"margin:2px;",
|
||||||
"display:inline-block;",
|
"display:inline-block;",
|
||||||
"}"].join("");
|
"}",
|
||||||
|
].join("");
|
||||||
document.head.appendChild(styleTag);
|
document.head.appendChild(styleTag);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function hsv2rgb (h,s,v) {
|
function hsv2rgb(h, s, v) {
|
||||||
if( s === 0 ) return [v,v,v]; // achromatic (grey)
|
if (s === 0) return [v, v, v]; // achromatic (grey)
|
||||||
|
|
||||||
h /= (Math.PI/6); // sector 0 to 5
|
h /= Math.PI / 6; // sector 0 to 5
|
||||||
var i = h|0,
|
var i = h | 0,
|
||||||
f = h - i, // factorial part of h
|
f = h - i, // factorial part of h
|
||||||
p = v * ( 1 - s ),
|
p = v * (1 - s),
|
||||||
q = v * ( 1 - s * f ),
|
q = v * (1 - s * f),
|
||||||
t = v * ( 1 - s * ( 1 - f ) );
|
t = v * (1 - s * (1 - f));
|
||||||
switch( i%6 ) {
|
switch (i % 6) {
|
||||||
case 0: return [v,t,p];
|
case 0:
|
||||||
case 1: return [q,v,p];
|
return [v, t, p];
|
||||||
case 2: return [p,v,t];
|
case 1:
|
||||||
case 3: return [p,q,v];
|
return [q, v, p];
|
||||||
case 4: return [t,p,v];
|
case 2:
|
||||||
case 5:return [v,p,q];
|
return [p, v, t];
|
||||||
|
case 3:
|
||||||
|
return [p, q, v];
|
||||||
|
case 4:
|
||||||
|
return [t, p, v];
|
||||||
|
case 5:
|
||||||
|
return [v, p, q];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFixedPosition(elem) {
|
function isFixedPosition(elem) {
|
||||||
do {
|
do {
|
||||||
if (getComputedStyle(elem).position === "fixed") return true;
|
if (getComputedStyle(elem).position === "fixed") return true;
|
||||||
} while ( (elem = elem.parentElement) !== null );
|
} while ((elem = elem.parentElement) !== null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var containerTemplate;
|
var containerTemplate;
|
||||||
(function createContainer(){
|
(function createContainer() {
|
||||||
containerTemplate = document.createElement("div");
|
containerTemplate = document.createElement("div");
|
||||||
containerTemplate.className = "canvascolor-container";
|
containerTemplate.className = "canvascolor-container";
|
||||||
var canvas = document.createElement("canvas");
|
var canvas = document.createElement("canvas");
|
||||||
|
@ -101,18 +109,22 @@ var canvascolor = (function() {//Code Isolation
|
||||||
function canvascolor(elem) {
|
function canvascolor(elem) {
|
||||||
var curcolor = elem.value || "#000";
|
var curcolor = elem.value || "#000";
|
||||||
|
|
||||||
var w=200, h=w/2;
|
var w = 200,
|
||||||
|
h = w / 2;
|
||||||
|
|
||||||
var container = containerTemplate.cloneNode(true);
|
var container = containerTemplate.cloneNode(true);
|
||||||
container.style.width = w+"px";
|
container.style.width = w + "px";
|
||||||
container.style.position = isFixedPosition(elem) ? "fixed" : "absolute";
|
container.style.position = isFixedPosition(elem) ? "fixed" : "absolute";
|
||||||
var canvas = container.getElementsByTagName("canvas")[0];
|
var canvas = container.getElementsByTagName("canvas")[0];
|
||||||
var ctx = canvas.getContext("2d");
|
var ctx = canvas.getContext("2d");
|
||||||
canvas.width = w; canvas.height=h;
|
canvas.width = w;
|
||||||
|
canvas.height = h;
|
||||||
|
|
||||||
var prevcolorsDiv = container.getElementsByClassName("canvascolor-history")[0];
|
var prevcolorsDiv = container.getElementsByClassName(
|
||||||
prevcolorsDiv.style.width=w+"px";
|
"canvascolor-history",
|
||||||
prevcolorsDiv.style.maxHeight=h+"px";
|
)[0];
|
||||||
|
prevcolorsDiv.style.width = w + "px";
|
||||||
|
prevcolorsDiv.style.maxHeight = h + "px";
|
||||||
|
|
||||||
var previewdiv = createColorDiv(curcolor);
|
var previewdiv = createColorDiv(curcolor);
|
||||||
previewdiv.style.border = "1px solid white";
|
previewdiv.style.border = "1px solid white";
|
||||||
|
@ -120,59 +132,65 @@ var canvascolor = (function() {//Code Isolation
|
||||||
|
|
||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
|
|
||||||
function displayContainer(){
|
function displayContainer() {
|
||||||
var rect = elem.getBoundingClientRect();
|
var rect = elem.getBoundingClientRect();
|
||||||
var conttop=(rect.top+rect.height+3),
|
var conttop = rect.top + rect.height + 3,
|
||||||
contleft=rect.left;
|
contleft = rect.left;
|
||||||
if (container.style.position !== "fixed") {
|
if (container.style.position !== "fixed") {
|
||||||
conttop += document.documentElement.scrollTop;
|
conttop += document.documentElement.scrollTop;
|
||||||
contleft += document.documentElement.scrollLeft;
|
contleft += document.documentElement.scrollLeft;
|
||||||
}
|
}
|
||||||
container.style.top = conttop+"px";
|
container.style.top = conttop + "px";
|
||||||
container.style.left = contleft+"px";
|
container.style.left = contleft + "px";
|
||||||
container.style.display = "block";
|
container.style.display = "block";
|
||||||
}
|
}
|
||||||
function hideContainer(){
|
function hideContainer() {
|
||||||
container.style.display = "none";
|
container.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.addEventListener("mouseover", displayContainer, true);
|
elem.addEventListener("mouseover", displayContainer, true);
|
||||||
container.addEventListener("mouseleave", hideContainer, false);
|
container.addEventListener("mouseleave", hideContainer, false);
|
||||||
elem.addEventListener("keyup", function(){
|
elem.addEventListener(
|
||||||
|
"keyup",
|
||||||
|
function () {
|
||||||
changeColor(elem.value, true);
|
changeColor(elem.value, true);
|
||||||
}, true);
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
changeColor(elem.value, true);
|
changeColor(elem.value, true);
|
||||||
|
|
||||||
var idata = ctx.createImageData(w,h);
|
var idata = ctx.createImageData(w, h);
|
||||||
|
|
||||||
function rgb2hex (rgb) {
|
function rgb2hex(rgb) {
|
||||||
function num2hex (c) {return (c*15/255|0).toString(16);}
|
function num2hex(c) {
|
||||||
return "#"+num2hex(rgb[0])+num2hex(rgb[1])+num2hex(rgb[2]);
|
return (((c * 15) / 255) | 0).toString(16);
|
||||||
|
}
|
||||||
|
return "#" + num2hex(rgb[0]) + num2hex(rgb[1]) + num2hex(rgb[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function colorAt(coords) {
|
function colorAt(coords) {
|
||||||
var x=coords[0], y=coords[1];
|
var x = coords[0],
|
||||||
return hsv2rgb(x/w*Math.PI, 1, (1-y/h)*255);
|
y = coords[1];
|
||||||
|
return hsv2rgb((x / w) * Math.PI, 1, (1 - y / h) * 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
for (var x=0; x<w; x++) {
|
for (var x = 0; x < w; x++) {
|
||||||
for (var y=0;y<h; y++) {
|
for (var y = 0; y < h; y++) {
|
||||||
var i = 4*(x+y*w);
|
var i = 4 * (x + y * w);
|
||||||
var rgb = colorAt([x,y]);
|
var rgb = colorAt([x, y]);
|
||||||
idata.data[i] = rgb[0];//Red
|
idata.data[i] = rgb[0]; //Red
|
||||||
idata.data[i+1] = rgb[1];//Green
|
idata.data[i + 1] = rgb[1]; //Green
|
||||||
idata.data[i+2] = rgb[2];//Blue
|
idata.data[i + 2] = rgb[2]; //Blue
|
||||||
idata.data[i+3] = 255;
|
idata.data[i + 3] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.putImageData(idata,0,0);
|
ctx.putImageData(idata, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
render();
|
render();
|
||||||
|
|
||||||
|
|
||||||
/** Changes the current color (the value of the input field) and updates other variables accordingly
|
/** Changes the current color (the value of the input field) and updates other variables accordingly
|
||||||
* @param {string} color The new color. Must be a valid CSS color string if ensureValid is not specified
|
* @param {string} color The new color. Must be a valid CSS color string if ensureValid is not specified
|
||||||
* @param {boolean} [ensureValid=false] Do not make the change if color is not a valid CSS color
|
* @param {boolean} [ensureValid=false] Do not make the change if color is not a valid CSS color
|
||||||
|
@ -189,16 +207,20 @@ var canvascolor = (function() {//Code Isolation
|
||||||
elem.focus();
|
elem.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createColorDiv (color) {
|
function createColorDiv(color) {
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
div.style.width = (w/3-10)+"px";
|
div.style.width = w / 3 - 10 + "px";
|
||||||
div.style.height = (h/3-8)+"px";
|
div.style.height = h / 3 - 8 + "px";
|
||||||
div.style.backgroundColor = color;
|
div.style.backgroundColor = color;
|
||||||
div.addEventListener("click", function(){
|
div.addEventListener(
|
||||||
|
"click",
|
||||||
|
function () {
|
||||||
changeColor(color);
|
changeColor(color);
|
||||||
}, true);
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
if (prevcolorsDiv.childElementCount <= 1) prevcolorsDiv.appendChild(div);
|
if (prevcolorsDiv.childElementCount <= 1) prevcolorsDiv.appendChild(div);
|
||||||
else prevcolorsDiv.insertBefore(div,prevcolorsDiv.children[1]);
|
else prevcolorsDiv.insertBefore(div, prevcolorsDiv.children[1]);
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,36 +229,51 @@ var canvascolor = (function() {//Code Isolation
|
||||||
return [evt.clientX - canvasrect.left, evt.clientY - canvasrect.top];
|
return [evt.clientX - canvasrect.left, evt.clientY - canvasrect.top];
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.addEventListener("mousemove", function(evt){
|
canvas.addEventListener(
|
||||||
|
"mousemove",
|
||||||
|
function (evt) {
|
||||||
var coords = canvasPos(evt);
|
var coords = canvasPos(evt);
|
||||||
previewdiv.style.backgroundColor = rgb2hex(colorAt(coords));
|
previewdiv.style.backgroundColor = rgb2hex(colorAt(coords));
|
||||||
}, true);
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
canvas.addEventListener("click", function(evt){
|
canvas.addEventListener(
|
||||||
|
"click",
|
||||||
|
function (evt) {
|
||||||
var coords = canvasPos(evt);
|
var coords = canvasPos(evt);
|
||||||
var color = rgb2hex(colorAt(coords));
|
var color = rgb2hex(colorAt(coords));
|
||||||
createColorDiv(color);
|
createColorDiv(color);
|
||||||
changeColor(color);
|
changeColor(color);
|
||||||
}, true);
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
canvas.addEventListener("mouseleave", function(){
|
canvas.addEventListener(
|
||||||
|
"mouseleave",
|
||||||
|
function () {
|
||||||
previewdiv.style.backgroundColor = curcolor;
|
previewdiv.style.backgroundColor = curcolor;
|
||||||
}, true);
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Put a color picker on every input[type=color] if the browser doesn't support this input type
|
//Put a color picker on every input[type=color] if the browser doesn't support this input type
|
||||||
//and on every input with the class canvascolor
|
//and on every input with the class canvascolor
|
||||||
var pickers = document.querySelectorAll("input.canvascolor, input[type=color]");
|
var pickers = document.querySelectorAll(
|
||||||
for (var i=0;i <pickers.length; i++) {
|
"input.canvascolor, input[type=color]",
|
||||||
|
);
|
||||||
|
for (var i = 0; i < pickers.length; i++) {
|
||||||
var input = pickers.item(i);
|
var input = pickers.item(i);
|
||||||
//If the browser supports native color picker and the user didn't
|
//If the browser supports native color picker and the user didn't
|
||||||
//explicitly added canvascolor to the element, we do not add a custom color picker
|
//explicitly added canvascolor to the element, we do not add a custom color picker
|
||||||
if (input.type !== "color" ||
|
if (
|
||||||
input.className.split(" ").indexOf("canvascolor") !== -1) {
|
input.type !== "color" ||
|
||||||
|
input.className.split(" ").indexOf("canvascolor") !== -1
|
||||||
|
) {
|
||||||
canvascolor(input);
|
canvascolor(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return canvascolor;
|
return canvascolor;
|
||||||
}());
|
})();
|
||||||
|
|
|
@ -9,7 +9,7 @@ function showRecentBoards() {
|
||||||
|
|
||||||
var list = document.createElement("ul");
|
var list = document.createElement("ul");
|
||||||
|
|
||||||
recentBoards.forEach(function(name) {
|
recentBoards.forEach(function (name) {
|
||||||
var listItem = document.createElement("li");
|
var listItem = document.createElement("li");
|
||||||
var link = document.createElement("a");
|
var link = document.createElement("a");
|
||||||
link.setAttribute("href", `/boards/${encodeURIComponent(name)}`);
|
link.setAttribute("href", `/boards/${encodeURIComponent(name)}`);
|
||||||
|
|
|
@ -24,10 +24,11 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!SVGGraphicsElement.prototype.transformedBBox || !SVGGraphicsElement.prototype.transformedBBoxContains) {
|
if (
|
||||||
[pointInTransformedBBox,
|
!SVGGraphicsElement.prototype.transformedBBox ||
|
||||||
transformedBBoxIntersects] = (function () {
|
!SVGGraphicsElement.prototype.transformedBBoxContains
|
||||||
|
) {
|
||||||
|
[pointInTransformedBBox, transformedBBoxIntersects] = (function () {
|
||||||
var get_transform_matrix = function (elem) {
|
var get_transform_matrix = function (elem) {
|
||||||
// Returns the first translate or transform matrix or makes one
|
// Returns the first translate or transform matrix or makes one
|
||||||
var transform = null;
|
var transform = null;
|
||||||
|
@ -41,84 +42,82 @@ if (!SVGGraphicsElement.prototype.transformedBBox || !SVGGraphicsElement.prototy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transform == null) {
|
if (transform == null) {
|
||||||
transform = elem.transform.baseVal.createSVGTransformFromMatrix(Tools.svg.createSVGMatrix());
|
transform = elem.transform.baseVal.createSVGTransformFromMatrix(
|
||||||
|
Tools.svg.createSVGMatrix(),
|
||||||
|
);
|
||||||
elem.transform.baseVal.appendItem(transform);
|
elem.transform.baseVal.appendItem(transform);
|
||||||
}
|
}
|
||||||
return transform.matrix;
|
return transform.matrix;
|
||||||
}
|
};
|
||||||
|
|
||||||
var transformRelative = function (m,t) {
|
var transformRelative = function (m, t) {
|
||||||
return [
|
return [m.a * t[0] + m.c * t[1], m.b * t[0] + m.d * t[1]];
|
||||||
m.a*t[0]+m.c*t[1],
|
};
|
||||||
m.b*t[0]+m.d*t[1]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
var transformAbsolute = function (m,t) {
|
var transformAbsolute = function (m, t) {
|
||||||
return [
|
return [m.a * t[0] + m.c * t[1] + m.e, m.b * t[0] + m.d * t[1] + m.f];
|
||||||
m.a*t[0]+m.c*t[1]+m.e,
|
};
|
||||||
m.b*t[0]+m.d*t[1]+m.f
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
SVGGraphicsElement.prototype.transformedBBox = function (scale=1) {
|
SVGGraphicsElement.prototype.transformedBBox = function (scale = 1) {
|
||||||
bbox = this.getBBox();
|
bbox = this.getBBox();
|
||||||
tmatrix = get_transform_matrix(this);
|
tmatrix = get_transform_matrix(this);
|
||||||
tmatrix.e /= scale;
|
tmatrix.e /= scale;
|
||||||
tmatrix.f /= scale;
|
tmatrix.f /= scale;
|
||||||
return {
|
return {
|
||||||
r: transformAbsolute(tmatrix,[bbox.x/scale,bbox.y/scale]),
|
r: transformAbsolute(tmatrix, [bbox.x / scale, bbox.y / scale]),
|
||||||
a: transformRelative(tmatrix,[bbox.width/scale,0]),
|
a: transformRelative(tmatrix, [bbox.width / scale, 0]),
|
||||||
b: transformRelative(tmatrix,[0,bbox.height/scale])
|
b: transformRelative(tmatrix, [0, bbox.height / scale]),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
SVGSVGElement.prototype.transformedBBox = function (scale=1) {
|
SVGSVGElement.prototype.transformedBBox = function (scale = 1) {
|
||||||
bbox = {
|
bbox = {
|
||||||
x: this.x.baseVal.value,
|
x: this.x.baseVal.value,
|
||||||
y: this.y.baseVal.value,
|
y: this.y.baseVal.value,
|
||||||
width: this.width.baseVal.value,
|
width: this.width.baseVal.value,
|
||||||
height: this.height.baseVal.value
|
height: this.height.baseVal.value,
|
||||||
};
|
};
|
||||||
tmatrix = get_transform_matrix(this);
|
tmatrix = get_transform_matrix(this);
|
||||||
tmatrix.e /= scale;
|
tmatrix.e /= scale;
|
||||||
tmatrix.f /= scale;
|
tmatrix.f /= scale;
|
||||||
return {
|
return {
|
||||||
r: transformAbsolute(tmatrix,[bbox.x/scale,bbox.y/scale]),
|
r: transformAbsolute(tmatrix, [bbox.x / scale, bbox.y / scale]),
|
||||||
a: transformRelative(tmatrix,[bbox.width/scale,0]),
|
a: transformRelative(tmatrix, [bbox.width / scale, 0]),
|
||||||
b: transformRelative(tmatrix,[0,bbox.height/scale])
|
b: transformRelative(tmatrix, [0, bbox.height / scale]),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
var pointInTransformedBBox = function ([x,y],{r,a,b}) {
|
var pointInTransformedBBox = function ([x, y], { r, a, b }) {
|
||||||
var d = [x-r[0],y-r[1]];
|
var d = [x - r[0], y - r[1]];
|
||||||
var idet = (a[0]*b[1]-a[1]*b[0]);
|
var idet = a[0] * b[1] - a[1] * b[0];
|
||||||
var c1 = (d[0]*b[1]-d[1]*b[0]) / idet;
|
var c1 = (d[0] * b[1] - d[1] * b[0]) / idet;
|
||||||
var c2 = (d[1]*a[0]-d[0]*a[1]) / idet;
|
var c2 = (d[1] * a[0] - d[0] * a[1]) / idet;
|
||||||
return (c1>=0 && c1<=1 && c2>=0 && c2<=1)
|
return c1 >= 0 && c1 <= 1 && c2 >= 0 && c2 <= 1;
|
||||||
}
|
};
|
||||||
|
|
||||||
SVGGraphicsElement.prototype.transformedBBoxContains = function (x,y) {
|
SVGGraphicsElement.prototype.transformedBBoxContains = function (x, y) {
|
||||||
return pointInTransformedBBox([x, y], this.transformedBBox())
|
return pointInTransformedBBox([x, y], this.transformedBBox());
|
||||||
}
|
};
|
||||||
|
|
||||||
function transformedBBoxIntersects(bbox_a,bbox_b) {
|
function transformedBBoxIntersects(bbox_a, bbox_b) {
|
||||||
var corners = [
|
var corners = [
|
||||||
bbox_b.r,
|
bbox_b.r,
|
||||||
[bbox_b.r[0] + bbox_b.a[0], bbox_b.r[1] + bbox_b.a[1]],
|
[bbox_b.r[0] + bbox_b.a[0], bbox_b.r[1] + bbox_b.a[1]],
|
||||||
[bbox_b.r[0] + bbox_b.b[0], bbox_b.r[1] + bbox_b.b[1]],
|
[bbox_b.r[0] + bbox_b.b[0], bbox_b.r[1] + bbox_b.b[1]],
|
||||||
[bbox_b.r[0] + bbox_b.a[0] + bbox_b.b[0], bbox_b.r[1] + bbox_b.a[1] + bbox_b.b[1]]
|
[
|
||||||
]
|
bbox_b.r[0] + bbox_b.a[0] + bbox_b.b[0],
|
||||||
return corners.every(function(corner) {
|
bbox_b.r[1] + bbox_b.a[1] + bbox_b.b[1],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
return corners.every(function (corner) {
|
||||||
return pointInTransformedBBox(corner, bbox_a);
|
return pointInTransformedBBox(corner, bbox_a);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SVGGraphicsElement.prototype.transformedBBoxIntersects= function (bbox) {
|
SVGGraphicsElement.prototype.transformedBBoxIntersects = function (bbox) {
|
||||||
return transformedBBoxIntersects(this.transformedBBox(),bbox)
|
return transformedBBoxIntersects(this.transformedBBox(), bbox);
|
||||||
}
|
};
|
||||||
|
|
||||||
return [pointInTransformedBBox,
|
return [pointInTransformedBBox, transformedBBoxIntersects];
|
||||||
transformedBBoxIntersects]
|
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Minitpl = (function () {
|
Minitpl = (function () {
|
||||||
|
|
||||||
function Minitpl(elem, data) {
|
function Minitpl(elem, data) {
|
||||||
this.elem = (typeof (elem) === "string") ? document.querySelector(elem) : elem;
|
this.elem = typeof elem === "string" ? document.querySelector(elem) : elem;
|
||||||
if (!elem) {
|
if (!elem) {
|
||||||
throw "Invalid element!";
|
throw "Invalid element!";
|
||||||
}
|
}
|
||||||
|
@ -36,7 +35,7 @@ Minitpl = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function transform(element, transformer) {
|
function transform(element, transformer) {
|
||||||
if (typeof (transformer) === "function") {
|
if (typeof transformer === "function") {
|
||||||
transformer(element);
|
transformer(element);
|
||||||
} else {
|
} else {
|
||||||
element.textContent = transformer;
|
element.textContent = transformer;
|
||||||
|
@ -45,7 +44,7 @@ Minitpl = (function () {
|
||||||
|
|
||||||
Minitpl.prototype.add = function (data) {
|
Minitpl.prototype.add = function (data) {
|
||||||
var newElem = this.elem.cloneNode(true);
|
var newElem = this.elem.cloneNode(true);
|
||||||
if (typeof (data) === "object") {
|
if (typeof data === "object") {
|
||||||
for (var key in data) {
|
for (var key in data) {
|
||||||
var matches = newElem.querySelectorAll(key);
|
var matches = newElem.querySelectorAll(key);
|
||||||
for (var i = 0; i < matches.length; i++) {
|
for (var i = 0; i < matches.length; i++) {
|
||||||
|
@ -57,8 +56,7 @@ Minitpl = (function () {
|
||||||
}
|
}
|
||||||
this.parent.appendChild(newElem);
|
this.parent.appendChild(newElem);
|
||||||
return newElem;
|
return newElem;
|
||||||
}
|
};
|
||||||
|
|
||||||
return Minitpl;
|
return Minitpl;
|
||||||
}());
|
})();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
// @info
|
// @info
|
||||||
// Polyfill for SVG getPathData() and setPathData() methods. Based on:
|
// Polyfill for SVG getPathData() and setPathData() methods. Based on:
|
||||||
// - SVGPathSeg polyfill by Philip Rogers (MIT License)
|
// - SVGPathSeg polyfill by Philip Rogers (MIT License)
|
||||||
|
@ -11,11 +10,32 @@
|
||||||
// Jarosław Foksa
|
// Jarosław Foksa
|
||||||
// @license
|
// @license
|
||||||
// MIT License
|
// MIT License
|
||||||
if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathData) {
|
if (
|
||||||
|
!SVGPathElement.prototype.getPathData ||
|
||||||
|
!SVGPathElement.prototype.setPathData
|
||||||
|
) {
|
||||||
(function () {
|
(function () {
|
||||||
var commandsMap = {
|
var commandsMap = {
|
||||||
"Z": "Z", "M": "M", "L": "L", "C": "C", "Q": "Q", "A": "A", "H": "H", "V": "V", "S": "S", "T": "T",
|
Z: "Z",
|
||||||
"z": "Z", "m": "m", "l": "l", "c": "c", "q": "q", "a": "a", "h": "h", "v": "v", "s": "s", "t": "t"
|
M: "M",
|
||||||
|
L: "L",
|
||||||
|
C: "C",
|
||||||
|
Q: "Q",
|
||||||
|
A: "A",
|
||||||
|
H: "H",
|
||||||
|
V: "V",
|
||||||
|
S: "S",
|
||||||
|
T: "T",
|
||||||
|
z: "Z",
|
||||||
|
m: "m",
|
||||||
|
l: "l",
|
||||||
|
c: "c",
|
||||||
|
q: "q",
|
||||||
|
a: "a",
|
||||||
|
h: "h",
|
||||||
|
v: "v",
|
||||||
|
s: "s",
|
||||||
|
t: "t",
|
||||||
};
|
};
|
||||||
|
|
||||||
var Source = function (string) {
|
var Source = function (string) {
|
||||||
|
@ -41,27 +61,27 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
// Check for remaining coordinates in the current command.
|
// Check for remaining coordinates in the current command.
|
||||||
if (
|
if (
|
||||||
(char === "+" || char === "-" || char === "." || (char >= "0" && char <= "9")) && this._prevCommand !== "Z"
|
(char === "+" ||
|
||||||
|
char === "-" ||
|
||||||
|
char === "." ||
|
||||||
|
(char >= "0" && char <= "9")) &&
|
||||||
|
this._prevCommand !== "Z"
|
||||||
) {
|
) {
|
||||||
if (this._prevCommand === "M") {
|
if (this._prevCommand === "M") {
|
||||||
command = "L";
|
command = "L";
|
||||||
}
|
} else if (this._prevCommand === "m") {
|
||||||
else if (this._prevCommand === "m") {
|
|
||||||
command = "l";
|
command = "l";
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
command = this._prevCommand;
|
command = this._prevCommand;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
command = null;
|
command = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === null) {
|
if (command === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,24 +92,25 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
if (cmd === "H" || cmd === "V") {
|
if (cmd === "H" || cmd === "V") {
|
||||||
values = [this._parseNumber()];
|
values = [this._parseNumber()];
|
||||||
}
|
} else if (cmd === "M" || cmd === "L" || cmd === "T") {
|
||||||
else if (cmd === "M" || cmd === "L" || cmd === "T") {
|
|
||||||
values = [this._parseNumber(), this._parseNumber()];
|
values = [this._parseNumber(), this._parseNumber()];
|
||||||
}
|
} else if (cmd === "S" || cmd === "Q") {
|
||||||
else if (cmd === "S" || cmd === "Q") {
|
values = [
|
||||||
values = [this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseNumber()];
|
this._parseNumber(),
|
||||||
}
|
this._parseNumber(),
|
||||||
else if (cmd === "C") {
|
this._parseNumber(),
|
||||||
|
this._parseNumber(),
|
||||||
|
];
|
||||||
|
} else if (cmd === "C") {
|
||||||
values = [
|
values = [
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
this._parseNumber()
|
this._parseNumber(),
|
||||||
];
|
];
|
||||||
}
|
} else if (cmd === "A") {
|
||||||
else if (cmd === "A") {
|
|
||||||
values = [
|
values = [
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
|
@ -97,10 +118,9 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
this._parseArcFlag(),
|
this._parseArcFlag(),
|
||||||
this._parseArcFlag(),
|
this._parseArcFlag(),
|
||||||
this._parseNumber(),
|
this._parseNumber(),
|
||||||
this._parseNumber()
|
this._parseNumber(),
|
||||||
];
|
];
|
||||||
}
|
} else if (cmd === "Z") {
|
||||||
else if (cmd === "Z") {
|
|
||||||
this._skipOptionalSpaces();
|
this._skipOptionalSpaces();
|
||||||
values = [];
|
values = [];
|
||||||
}
|
}
|
||||||
|
@ -108,8 +128,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
if (values === null || values.indexOf(null) >= 0) {
|
if (values === null || values.indexOf(null) >= 0) {
|
||||||
// Unknown command or known command with invalid values
|
// Unknown command or known command with invalid values
|
||||||
return null;
|
return null;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return { type: command, values: values };
|
return { type: command, values: values };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -136,7 +155,14 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
_isCurrentSpace: function () {
|
_isCurrentSpace: function () {
|
||||||
var char = this._string[this._currentIndex];
|
var char = this._string[this._currentIndex];
|
||||||
return char <= " " && (char === " " || char === "\n" || char === "\t" || char === "\r" || char === "\f");
|
return (
|
||||||
|
char <= " " &&
|
||||||
|
(char === " " ||
|
||||||
|
char === "\n" ||
|
||||||
|
char === "\t" ||
|
||||||
|
char === "\r" ||
|
||||||
|
char === "\f")
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_skipOptionalSpaces: function () {
|
_skipOptionalSpaces: function () {
|
||||||
|
@ -157,7 +183,10 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._skipOptionalSpaces()) {
|
if (this._skipOptionalSpaces()) {
|
||||||
if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === ",") {
|
if (
|
||||||
|
this._currentIndex < this._endIndex &&
|
||||||
|
this._string[this._currentIndex] === ","
|
||||||
|
) {
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
this._skipOptionalSpaces();
|
this._skipOptionalSpaces();
|
||||||
}
|
}
|
||||||
|
@ -180,20 +209,24 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
this._skipOptionalSpaces();
|
this._skipOptionalSpaces();
|
||||||
|
|
||||||
// Read the sign.
|
// Read the sign.
|
||||||
if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === "+") {
|
if (
|
||||||
|
this._currentIndex < this._endIndex &&
|
||||||
|
this._string[this._currentIndex] === "+"
|
||||||
|
) {
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
}
|
} else if (
|
||||||
else if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === "-") {
|
this._currentIndex < this._endIndex &&
|
||||||
|
this._string[this._currentIndex] === "-"
|
||||||
|
) {
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
sign = -1;
|
sign = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this._currentIndex === this._endIndex ||
|
this._currentIndex === this._endIndex ||
|
||||||
(
|
((this._string[this._currentIndex] < "0" ||
|
||||||
(this._string[this._currentIndex] < "0" || this._string[this._currentIndex] > "9") &&
|
this._string[this._currentIndex] > "9") &&
|
||||||
this._string[this._currentIndex] !== "."
|
this._string[this._currentIndex] !== ".")
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
// The first character of a number must be one of [0-9+-.].
|
// The first character of a number must be one of [0-9+-.].
|
||||||
return null;
|
return null;
|
||||||
|
@ -222,7 +255,10 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the decimals.
|
// Read the decimals.
|
||||||
if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === ".") {
|
if (
|
||||||
|
this._currentIndex < this._endIndex &&
|
||||||
|
this._string[this._currentIndex] === "."
|
||||||
|
) {
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
|
|
||||||
// There must be a least one digit following the .
|
// There must be a least one digit following the .
|
||||||
|
@ -249,16 +285,17 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
if (
|
if (
|
||||||
this._currentIndex !== startIndex &&
|
this._currentIndex !== startIndex &&
|
||||||
this._currentIndex + 1 < this._endIndex &&
|
this._currentIndex + 1 < this._endIndex &&
|
||||||
(this._string[this._currentIndex] === "e" || this._string[this._currentIndex] === "E") &&
|
(this._string[this._currentIndex] === "e" ||
|
||||||
(this._string[this._currentIndex + 1] !== "x" && this._string[this._currentIndex + 1] !== "m")
|
this._string[this._currentIndex] === "E") &&
|
||||||
|
this._string[this._currentIndex + 1] !== "x" &&
|
||||||
|
this._string[this._currentIndex + 1] !== "m"
|
||||||
) {
|
) {
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
|
|
||||||
// Read the sign of the exponent.
|
// Read the sign of the exponent.
|
||||||
if (this._string[this._currentIndex] === "+") {
|
if (this._string[this._currentIndex] === "+") {
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
}
|
} else if (this._string[this._currentIndex] === "-") {
|
||||||
else if (this._string[this._currentIndex] === "-") {
|
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
expsign = -1;
|
expsign = -1;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +315,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
this._string[this._currentIndex] <= "9"
|
this._string[this._currentIndex] <= "9"
|
||||||
) {
|
) {
|
||||||
exponent *= 10;
|
exponent *= 10;
|
||||||
exponent += (this._string[this._currentIndex] - "0");
|
exponent += this._string[this._currentIndex] - "0";
|
||||||
this._currentIndex += 1;
|
this._currentIndex += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,17 +348,15 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
if (flagChar === "0") {
|
if (flagChar === "0") {
|
||||||
flag = 0;
|
flag = 0;
|
||||||
}
|
} else if (flagChar === "1") {
|
||||||
else if (flagChar === "1") {
|
|
||||||
flag = 1;
|
flag = 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._skipOptionalSpacesOrDelimiter();
|
this._skipOptionalSpacesOrDelimiter();
|
||||||
return flag;
|
return flag;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var parsePathDataString = function (string) {
|
var parsePathDataString = function (string) {
|
||||||
|
@ -336,25 +371,37 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
if (pathSeg === null) {
|
if (pathSeg === null) {
|
||||||
break;
|
break;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
pathData.push(pathSeg);
|
pathData.push(pathSeg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathData;
|
return pathData;
|
||||||
}
|
};
|
||||||
|
|
||||||
var setAttribute = SVGPathElement.prototype.setAttribute;
|
var setAttribute = SVGPathElement.prototype.setAttribute;
|
||||||
var removeAttribute = SVGPathElement.prototype.removeAttribute;
|
var removeAttribute = SVGPathElement.prototype.removeAttribute;
|
||||||
|
|
||||||
var $cachedPathData = window.Symbol ? Symbol() : "__cachedPathData";
|
var $cachedPathData = window.Symbol ? Symbol() : "__cachedPathData";
|
||||||
var $cachedNormalizedPathData = window.Symbol ? Symbol() : "__cachedNormalizedPathData";
|
var $cachedNormalizedPathData = window.Symbol
|
||||||
|
? Symbol()
|
||||||
|
: "__cachedNormalizedPathData";
|
||||||
|
|
||||||
// @info
|
// @info
|
||||||
// Get an array of corresponding cubic bezier curve parameters for given arc curve paramters.
|
// Get an array of corresponding cubic bezier curve parameters for given arc curve paramters.
|
||||||
var arcToCubicCurves = function (x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, _recursive) {
|
var arcToCubicCurves = function (
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
r1,
|
||||||
|
r2,
|
||||||
|
angle,
|
||||||
|
largeArcFlag,
|
||||||
|
sweepFlag,
|
||||||
|
_recursive,
|
||||||
|
) {
|
||||||
var degToRad = function (degrees) {
|
var degToRad = function (degrees) {
|
||||||
return (Math.PI * degrees) / 180;
|
return (Math.PI * degrees) / 180;
|
||||||
};
|
};
|
||||||
|
@ -374,8 +421,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
f2 = _recursive[1];
|
f2 = _recursive[1];
|
||||||
cx = _recursive[2];
|
cx = _recursive[2];
|
||||||
cy = _recursive[3];
|
cy = _recursive[3];
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var p1 = rotate(x1, y1, -angleRad);
|
var p1 = rotate(x1, y1, -angleRad);
|
||||||
x1 = p1.x;
|
x1 = p1.x;
|
||||||
y1 = p1.y;
|
y1 = p1.y;
|
||||||
|
@ -398,8 +444,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
if (largeArcFlag === sweepFlag) {
|
if (largeArcFlag === sweepFlag) {
|
||||||
sign = -1;
|
sign = -1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
sign = 1;
|
sign = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,8 +456,8 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
var k = sign * Math.sqrt(Math.abs(left / right));
|
var k = sign * Math.sqrt(Math.abs(left / right));
|
||||||
|
|
||||||
cx = k * r1 * y / r2 + (x1 + x2) / 2;
|
cx = (k * r1 * y) / r2 + (x1 + x2) / 2;
|
||||||
cy = k * -r2 * x / r1 + (y1 + y2) / 2;
|
cy = (k * -r2 * x) / r1 + (y1 + y2) / 2;
|
||||||
|
|
||||||
f1 = Math.asin(parseFloat(((y1 - cy) / r2).toFixed(9)));
|
f1 = Math.asin(parseFloat(((y1 - cy) / r2).toFixed(9)));
|
||||||
f2 = Math.asin(parseFloat(((y2 - cy) / r2).toFixed(9)));
|
f2 = Math.asin(parseFloat(((y2 - cy) / r2).toFixed(9)));
|
||||||
|
@ -441,21 +486,31 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
var df = f2 - f1;
|
var df = f2 - f1;
|
||||||
|
|
||||||
if (Math.abs(df) > (Math.PI * 120 / 180)) {
|
if (Math.abs(df) > (Math.PI * 120) / 180) {
|
||||||
var f2old = f2;
|
var f2old = f2;
|
||||||
var x2old = x2;
|
var x2old = x2;
|
||||||
var y2old = y2;
|
var y2old = y2;
|
||||||
|
|
||||||
if (sweepFlag && f2 > f1) {
|
if (sweepFlag && f2 > f1) {
|
||||||
f2 = f1 + (Math.PI * 120 / 180) * (1);
|
f2 = f1 + ((Math.PI * 120) / 180) * 1;
|
||||||
}
|
} else {
|
||||||
else {
|
f2 = f1 + ((Math.PI * 120) / 180) * -1;
|
||||||
f2 = f1 + (Math.PI * 120 / 180) * (-1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x2 = cx + r1 * Math.cos(f2);
|
x2 = cx + r1 * Math.cos(f2);
|
||||||
y2 = cy + r2 * Math.sin(f2);
|
y2 = cy + r2 * Math.sin(f2);
|
||||||
params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]);
|
params = arcToCubicCurves(
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
x2old,
|
||||||
|
y2old,
|
||||||
|
r1,
|
||||||
|
r2,
|
||||||
|
angle,
|
||||||
|
0,
|
||||||
|
sweepFlag,
|
||||||
|
[f2, f2old, cx, cy],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
df = f2 - f1;
|
df = f2 - f1;
|
||||||
|
@ -465,8 +520,8 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
var c2 = Math.cos(f2);
|
var c2 = Math.cos(f2);
|
||||||
var s2 = Math.sin(f2);
|
var s2 = Math.sin(f2);
|
||||||
var t = Math.tan(df / 4);
|
var t = Math.tan(df / 4);
|
||||||
var hx = 4 / 3 * r1 * t;
|
var hx = (4 / 3) * r1 * t;
|
||||||
var hy = 4 / 3 * r2 * t;
|
var hy = (4 / 3) * r2 * t;
|
||||||
|
|
||||||
var m1 = [x1, y1];
|
var m1 = [x1, y1];
|
||||||
var m2 = [x1 + hx * s1, y1 - hy * c1];
|
var m2 = [x1 + hx * s1, y1 - hy * c1];
|
||||||
|
@ -478,8 +533,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
if (_recursive) {
|
if (_recursive) {
|
||||||
return [m2, m3, m4].concat(params);
|
return [m2, m3, m4].concat(params);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
params = [m2, m3, m4].concat(params);
|
params = [m2, m3, m4].concat(params);
|
||||||
|
|
||||||
var curves = [];
|
var curves = [];
|
||||||
|
@ -497,7 +551,10 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
var clonePathData = function (pathData) {
|
var clonePathData = function (pathData) {
|
||||||
return pathData.map(function (seg) {
|
return pathData.map(function (seg) {
|
||||||
return { type: seg.type, values: Array.prototype.slice.call(seg.values) }
|
return {
|
||||||
|
type: seg.type,
|
||||||
|
values: Array.prototype.slice.call(seg.values),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -526,9 +583,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "m") {
|
||||||
|
|
||||||
else if (type === "m") {
|
|
||||||
var x = currentX + seg.values[0];
|
var x = currentX + seg.values[0];
|
||||||
var y = currentY + seg.values[1];
|
var y = currentY + seg.values[1];
|
||||||
|
|
||||||
|
@ -539,9 +594,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "L") {
|
||||||
|
|
||||||
else if (type === "L") {
|
|
||||||
var x = seg.values[0];
|
var x = seg.values[0];
|
||||||
var y = seg.values[1];
|
var y = seg.values[1];
|
||||||
|
|
||||||
|
@ -549,9 +602,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "l") {
|
||||||
|
|
||||||
else if (type === "l") {
|
|
||||||
var x = currentX + seg.values[0];
|
var x = currentX + seg.values[0];
|
||||||
var y = currentY + seg.values[1];
|
var y = currentY + seg.values[1];
|
||||||
|
|
||||||
|
@ -559,9 +610,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "C") {
|
||||||
|
|
||||||
else if (type === "C") {
|
|
||||||
var x1 = seg.values[0];
|
var x1 = seg.values[0];
|
||||||
var y1 = seg.values[1];
|
var y1 = seg.values[1];
|
||||||
var x2 = seg.values[2];
|
var x2 = seg.values[2];
|
||||||
|
@ -569,13 +618,14 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
var x = seg.values[4];
|
var x = seg.values[4];
|
||||||
var y = seg.values[5];
|
var y = seg.values[5];
|
||||||
|
|
||||||
absolutizedPathData.push({ type: "C", values: [x1, y1, x2, y2, x, y] });
|
absolutizedPathData.push({
|
||||||
|
type: "C",
|
||||||
|
values: [x1, y1, x2, y2, x, y],
|
||||||
|
});
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "c") {
|
||||||
|
|
||||||
else if (type === "c") {
|
|
||||||
var x1 = currentX + seg.values[0];
|
var x1 = currentX + seg.values[0];
|
||||||
var y1 = currentY + seg.values[1];
|
var y1 = currentY + seg.values[1];
|
||||||
var x2 = currentX + seg.values[2];
|
var x2 = currentX + seg.values[2];
|
||||||
|
@ -583,13 +633,14 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
var x = currentX + seg.values[4];
|
var x = currentX + seg.values[4];
|
||||||
var y = currentY + seg.values[5];
|
var y = currentY + seg.values[5];
|
||||||
|
|
||||||
absolutizedPathData.push({ type: "C", values: [x1, y1, x2, y2, x, y] });
|
absolutizedPathData.push({
|
||||||
|
type: "C",
|
||||||
|
values: [x1, y1, x2, y2, x, y],
|
||||||
|
});
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "Q") {
|
||||||
|
|
||||||
else if (type === "Q") {
|
|
||||||
var x1 = seg.values[0];
|
var x1 = seg.values[0];
|
||||||
var y1 = seg.values[1];
|
var y1 = seg.values[1];
|
||||||
var x = seg.values[2];
|
var x = seg.values[2];
|
||||||
|
@ -599,9 +650,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "q") {
|
||||||
|
|
||||||
else if (type === "q") {
|
|
||||||
var x1 = currentX + seg.values[0];
|
var x1 = currentX + seg.values[0];
|
||||||
var y1 = currentY + seg.values[1];
|
var y1 = currentY + seg.values[1];
|
||||||
var x = currentX + seg.values[2];
|
var x = currentX + seg.values[2];
|
||||||
|
@ -611,59 +660,61 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "A") {
|
||||||
|
|
||||||
else if (type === "A") {
|
|
||||||
var x = seg.values[5];
|
var x = seg.values[5];
|
||||||
var y = seg.values[6];
|
var y = seg.values[6];
|
||||||
|
|
||||||
absolutizedPathData.push({
|
absolutizedPathData.push({
|
||||||
type: "A",
|
type: "A",
|
||||||
values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y]
|
values: [
|
||||||
|
seg.values[0],
|
||||||
|
seg.values[1],
|
||||||
|
seg.values[2],
|
||||||
|
seg.values[3],
|
||||||
|
seg.values[4],
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "a") {
|
||||||
|
|
||||||
else if (type === "a") {
|
|
||||||
var x = currentX + seg.values[5];
|
var x = currentX + seg.values[5];
|
||||||
var y = currentY + seg.values[6];
|
var y = currentY + seg.values[6];
|
||||||
|
|
||||||
absolutizedPathData.push({
|
absolutizedPathData.push({
|
||||||
type: "A",
|
type: "A",
|
||||||
values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y]
|
values: [
|
||||||
|
seg.values[0],
|
||||||
|
seg.values[1],
|
||||||
|
seg.values[2],
|
||||||
|
seg.values[3],
|
||||||
|
seg.values[4],
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "H") {
|
||||||
|
|
||||||
else if (type === "H") {
|
|
||||||
var x = seg.values[0];
|
var x = seg.values[0];
|
||||||
absolutizedPathData.push({ type: "H", values: [x] });
|
absolutizedPathData.push({ type: "H", values: [x] });
|
||||||
currentX = x;
|
currentX = x;
|
||||||
}
|
} else if (type === "h") {
|
||||||
|
|
||||||
else if (type === "h") {
|
|
||||||
var x = currentX + seg.values[0];
|
var x = currentX + seg.values[0];
|
||||||
absolutizedPathData.push({ type: "H", values: [x] });
|
absolutizedPathData.push({ type: "H", values: [x] });
|
||||||
currentX = x;
|
currentX = x;
|
||||||
}
|
} else if (type === "V") {
|
||||||
|
|
||||||
else if (type === "V") {
|
|
||||||
var y = seg.values[0];
|
var y = seg.values[0];
|
||||||
absolutizedPathData.push({ type: "V", values: [y] });
|
absolutizedPathData.push({ type: "V", values: [y] });
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "v") {
|
||||||
|
|
||||||
else if (type === "v") {
|
|
||||||
var y = currentY + seg.values[0];
|
var y = currentY + seg.values[0];
|
||||||
absolutizedPathData.push({ type: "V", values: [y] });
|
absolutizedPathData.push({ type: "V", values: [y] });
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "S") {
|
||||||
|
|
||||||
else if (type === "S") {
|
|
||||||
var x2 = seg.values[0];
|
var x2 = seg.values[0];
|
||||||
var y2 = seg.values[1];
|
var y2 = seg.values[1];
|
||||||
var x = seg.values[2];
|
var x = seg.values[2];
|
||||||
|
@ -673,9 +724,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "s") {
|
||||||
|
|
||||||
else if (type === "s") {
|
|
||||||
var x2 = currentX + seg.values[0];
|
var x2 = currentX + seg.values[0];
|
||||||
var y2 = currentY + seg.values[1];
|
var y2 = currentY + seg.values[1];
|
||||||
var x = currentX + seg.values[2];
|
var x = currentX + seg.values[2];
|
||||||
|
@ -685,29 +734,23 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "T") {
|
||||||
|
|
||||||
else if (type === "T") {
|
|
||||||
var x = seg.values[0];
|
var x = seg.values[0];
|
||||||
var y = seg.values[1]
|
var y = seg.values[1];
|
||||||
|
|
||||||
absolutizedPathData.push({ type: "T", values: [x, y] });
|
absolutizedPathData.push({ type: "T", values: [x, y] });
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "t") {
|
||||||
|
|
||||||
else if (type === "t") {
|
|
||||||
var x = currentX + seg.values[0];
|
var x = currentX + seg.values[0];
|
||||||
var y = currentY + seg.values[1]
|
var y = currentY + seg.values[1];
|
||||||
|
|
||||||
absolutizedPathData.push({ type: "T", values: [x, y] });
|
absolutizedPathData.push({ type: "T", values: [x, y] });
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (type === "Z" || type === "z") {
|
||||||
|
|
||||||
else if (type === "Z" || type === "z") {
|
|
||||||
absolutizedPathData.push({ type: "Z", values: [] });
|
absolutizedPathData.push({ type: "Z", values: [] });
|
||||||
|
|
||||||
currentX = subpathX;
|
currentX = subpathX;
|
||||||
|
@ -746,9 +789,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (seg.type === "C") {
|
||||||
|
|
||||||
else if (seg.type === "C") {
|
|
||||||
var x1 = seg.values[0];
|
var x1 = seg.values[0];
|
||||||
var y1 = seg.values[1];
|
var y1 = seg.values[1];
|
||||||
var x2 = seg.values[2];
|
var x2 = seg.values[2];
|
||||||
|
@ -763,9 +804,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (seg.type === "L") {
|
||||||
|
|
||||||
else if (seg.type === "L") {
|
|
||||||
var x = seg.values[0];
|
var x = seg.values[0];
|
||||||
var y = seg.values[1];
|
var y = seg.values[1];
|
||||||
|
|
||||||
|
@ -773,25 +812,19 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (seg.type === "H") {
|
||||||
|
|
||||||
else if (seg.type === "H") {
|
|
||||||
var x = seg.values[0];
|
var x = seg.values[0];
|
||||||
|
|
||||||
reducedPathData.push({ type: "L", values: [x, currentY] });
|
reducedPathData.push({ type: "L", values: [x, currentY] });
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
}
|
} else if (seg.type === "V") {
|
||||||
|
|
||||||
else if (seg.type === "V") {
|
|
||||||
var y = seg.values[0];
|
var y = seg.values[0];
|
||||||
|
|
||||||
reducedPathData.push({ type: "L", values: [currentX, y] });
|
reducedPathData.push({ type: "L", values: [currentX, y] });
|
||||||
|
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (seg.type === "S") {
|
||||||
|
|
||||||
else if (seg.type === "S") {
|
|
||||||
var x2 = seg.values[0];
|
var x2 = seg.values[0];
|
||||||
var y2 = seg.values[1];
|
var y2 = seg.values[1];
|
||||||
var x = seg.values[2];
|
var x = seg.values[2];
|
||||||
|
@ -802,8 +835,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
if (lastType === "C" || lastType === "S") {
|
if (lastType === "C" || lastType === "S") {
|
||||||
cx1 = currentX + (currentX - lastControlX);
|
cx1 = currentX + (currentX - lastControlX);
|
||||||
cy1 = currentY + (currentY - lastControlY);
|
cy1 = currentY + (currentY - lastControlY);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
cx1 = currentX;
|
cx1 = currentX;
|
||||||
cy1 = currentY;
|
cy1 = currentY;
|
||||||
}
|
}
|
||||||
|
@ -815,9 +847,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (seg.type === "T") {
|
||||||
|
|
||||||
else if (seg.type === "T") {
|
|
||||||
var x = seg.values[0];
|
var x = seg.values[0];
|
||||||
var y = seg.values[1];
|
var y = seg.values[1];
|
||||||
|
|
||||||
|
@ -826,47 +856,48 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
if (lastType === "Q" || lastType === "T") {
|
if (lastType === "Q" || lastType === "T") {
|
||||||
x1 = currentX + (currentX - lastControlX);
|
x1 = currentX + (currentX - lastControlX);
|
||||||
y1 = currentY + (currentY - lastControlY);
|
y1 = currentY + (currentY - lastControlY);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
x1 = currentX;
|
x1 = currentX;
|
||||||
y1 = currentY;
|
y1 = currentY;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cx1 = currentX + 2 * (x1 - currentX) / 3;
|
var cx1 = currentX + (2 * (x1 - currentX)) / 3;
|
||||||
var cy1 = currentY + 2 * (y1 - currentY) / 3;
|
var cy1 = currentY + (2 * (y1 - currentY)) / 3;
|
||||||
var cx2 = x + 2 * (x1 - x) / 3;
|
var cx2 = x + (2 * (x1 - x)) / 3;
|
||||||
var cy2 = y + 2 * (y1 - y) / 3;
|
var cy2 = y + (2 * (y1 - y)) / 3;
|
||||||
|
|
||||||
reducedPathData.push({ type: "C", values: [cx1, cy1, cx2, cy2, x, y] });
|
reducedPathData.push({
|
||||||
|
type: "C",
|
||||||
|
values: [cx1, cy1, cx2, cy2, x, y],
|
||||||
|
});
|
||||||
|
|
||||||
lastControlX = x1;
|
lastControlX = x1;
|
||||||
lastControlY = y1;
|
lastControlY = y1;
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (seg.type === "Q") {
|
||||||
|
|
||||||
else if (seg.type === "Q") {
|
|
||||||
var x1 = seg.values[0];
|
var x1 = seg.values[0];
|
||||||
var y1 = seg.values[1];
|
var y1 = seg.values[1];
|
||||||
var x = seg.values[2];
|
var x = seg.values[2];
|
||||||
var y = seg.values[3];
|
var y = seg.values[3];
|
||||||
|
|
||||||
var cx1 = currentX + 2 * (x1 - currentX) / 3;
|
var cx1 = currentX + (2 * (x1 - currentX)) / 3;
|
||||||
var cy1 = currentY + 2 * (y1 - currentY) / 3;
|
var cy1 = currentY + (2 * (y1 - currentY)) / 3;
|
||||||
var cx2 = x + 2 * (x1 - x) / 3;
|
var cx2 = x + (2 * (x1 - x)) / 3;
|
||||||
var cy2 = y + 2 * (y1 - y) / 3;
|
var cy2 = y + (2 * (y1 - y)) / 3;
|
||||||
|
|
||||||
reducedPathData.push({ type: "C", values: [cx1, cy1, cx2, cy2, x, y] });
|
reducedPathData.push({
|
||||||
|
type: "C",
|
||||||
|
values: [cx1, cy1, cx2, cy2, x, y],
|
||||||
|
});
|
||||||
|
|
||||||
lastControlX = x1;
|
lastControlX = x1;
|
||||||
lastControlY = y1;
|
lastControlY = y1;
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else if (seg.type === "A") {
|
||||||
|
|
||||||
else if (seg.type === "A") {
|
|
||||||
var r1 = Math.abs(seg.values[0]);
|
var r1 = Math.abs(seg.values[0]);
|
||||||
var r2 = Math.abs(seg.values[1]);
|
var r2 = Math.abs(seg.values[1]);
|
||||||
var angle = seg.values[2];
|
var angle = seg.values[2];
|
||||||
|
@ -876,14 +907,26 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
var y = seg.values[6];
|
var y = seg.values[6];
|
||||||
|
|
||||||
if (r1 === 0 || r2 === 0) {
|
if (r1 === 0 || r2 === 0) {
|
||||||
reducedPathData.push({ type: "C", values: [currentX, currentY, x, y, x, y] });
|
reducedPathData.push({
|
||||||
|
type: "C",
|
||||||
|
values: [currentX, currentY, x, y, x, y],
|
||||||
|
});
|
||||||
|
|
||||||
currentX = x;
|
currentX = x;
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (currentX !== x || currentY !== y) {
|
if (currentX !== x || currentY !== y) {
|
||||||
var curves = arcToCubicCurves(currentX, currentY, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
|
var curves = arcToCubicCurves(
|
||||||
|
currentX,
|
||||||
|
currentY,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
r1,
|
||||||
|
r2,
|
||||||
|
angle,
|
||||||
|
largeArcFlag,
|
||||||
|
sweepFlag,
|
||||||
|
);
|
||||||
|
|
||||||
curves.forEach(function (curve) {
|
curves.forEach(function (curve) {
|
||||||
reducedPathData.push({ type: "C", values: curve });
|
reducedPathData.push({ type: "C", values: curve });
|
||||||
|
@ -893,9 +936,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
currentY = y;
|
currentY = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (seg.type === "Z") {
|
||||||
|
|
||||||
else if (seg.type === "Z") {
|
|
||||||
reducedPathData.push(seg);
|
reducedPathData.push(seg);
|
||||||
|
|
||||||
currentX = subpathX;
|
currentX = subpathX;
|
||||||
|
@ -930,14 +971,12 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
if (options && options.normalize) {
|
if (options && options.normalize) {
|
||||||
if (this[$cachedNormalizedPathData]) {
|
if (this[$cachedNormalizedPathData]) {
|
||||||
return clonePathData(this[$cachedNormalizedPathData]);
|
return clonePathData(this[$cachedNormalizedPathData]);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var pathData;
|
var pathData;
|
||||||
|
|
||||||
if (this[$cachedPathData]) {
|
if (this[$cachedPathData]) {
|
||||||
pathData = clonePathData(this[$cachedPathData]);
|
pathData = clonePathData(this[$cachedPathData]);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
pathData = parsePathDataString(this.getAttribute("d") || "");
|
pathData = parsePathDataString(this.getAttribute("d") || "");
|
||||||
this[$cachedPathData] = clonePathData(pathData);
|
this[$cachedPathData] = clonePathData(pathData);
|
||||||
}
|
}
|
||||||
|
@ -946,12 +985,10 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
this[$cachedNormalizedPathData] = clonePathData(normalizedPathData);
|
this[$cachedNormalizedPathData] = clonePathData(normalizedPathData);
|
||||||
return normalizedPathData;
|
return normalizedPathData;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (this[$cachedPathData]) {
|
if (this[$cachedPathData]) {
|
||||||
return clonePathData(this[$cachedPathData]);
|
return clonePathData(this[$cachedPathData]);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var pathData = parsePathDataString(this.getAttribute("d") || "");
|
var pathData = parsePathDataString(this.getAttribute("d") || "");
|
||||||
this[$cachedPathData] = clonePathData(pathData);
|
this[$cachedPathData] = clonePathData(pathData);
|
||||||
return pathData;
|
return pathData;
|
||||||
|
@ -964,12 +1001,10 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
if (isIE) {
|
if (isIE) {
|
||||||
// @bugfix https://github.com/mbostock/d3/issues/1737
|
// @bugfix https://github.com/mbostock/d3/issues/1737
|
||||||
this.setAttribute("d", "");
|
this.setAttribute("d", "");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.removeAttribute("d");
|
this.removeAttribute("d");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var d = "";
|
var d = "";
|
||||||
|
|
||||||
for (var i = 0, l = pathData.length; i < l; i += 1) {
|
for (var i = 0, l = pathData.length; i < l; i += 1) {
|
||||||
|
@ -995,8 +1030,12 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
var y = this.y.baseVal.value;
|
var y = this.y.baseVal.value;
|
||||||
var width = this.width.baseVal.value;
|
var width = this.width.baseVal.value;
|
||||||
var height = this.height.baseVal.value;
|
var height = this.height.baseVal.value;
|
||||||
var rx = this.hasAttribute("rx") ? this.rx.baseVal.value : this.ry.baseVal.value;
|
var rx = this.hasAttribute("rx")
|
||||||
var ry = this.hasAttribute("ry") ? this.ry.baseVal.value : this.rx.baseVal.value;
|
? this.rx.baseVal.value
|
||||||
|
: this.ry.baseVal.value;
|
||||||
|
var ry = this.hasAttribute("ry")
|
||||||
|
? this.ry.baseVal.value
|
||||||
|
: this.rx.baseVal.value;
|
||||||
|
|
||||||
if (rx > width / 2) {
|
if (rx > width / 2) {
|
||||||
rx = width / 2;
|
rx = width / 2;
|
||||||
|
@ -1016,12 +1055,14 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
{ type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
|
{ type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
|
||||||
{ type: "V", values: [y + ry] },
|
{ type: "V", values: [y + ry] },
|
||||||
{ type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
|
{ type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
|
||||||
{ type: "Z", values: [] }
|
{ type: "Z", values: [] },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Get rid of redundant "A" segs when either rx or ry is 0
|
// Get rid of redundant "A" segs when either rx or ry is 0
|
||||||
pathData = pathData.filter(function (s) {
|
pathData = pathData.filter(function (s) {
|
||||||
return s.type === "A" && (s.values[0] === 0 || s.values[1] === 0) ? false : true;
|
return s.type === "A" && (s.values[0] === 0 || s.values[1] === 0)
|
||||||
|
? false
|
||||||
|
: true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options && options.normalize === true) {
|
if (options && options.normalize === true) {
|
||||||
|
@ -1042,7 +1083,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
{ type: "A", values: [r, r, 0, 0, 1, cx - r, cy] },
|
{ type: "A", values: [r, r, 0, 0, 1, cx - r, cy] },
|
||||||
{ type: "A", values: [r, r, 0, 0, 1, cx, cy - r] },
|
{ type: "A", values: [r, r, 0, 0, 1, cx, cy - r] },
|
||||||
{ type: "A", values: [r, r, 0, 0, 1, cx + r, cy] },
|
{ type: "A", values: [r, r, 0, 0, 1, cx + r, cy] },
|
||||||
{ type: "Z", values: [] }
|
{ type: "Z", values: [] },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (options && options.normalize === true) {
|
if (options && options.normalize === true) {
|
||||||
|
@ -1064,7 +1105,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
{ type: "A", values: [rx, ry, 0, 0, 1, cx - rx, cy] },
|
{ type: "A", values: [rx, ry, 0, 0, 1, cx - rx, cy] },
|
||||||
{ type: "A", values: [rx, ry, 0, 0, 1, cx, cy - ry] },
|
{ type: "A", values: [rx, ry, 0, 0, 1, cx, cy - ry] },
|
||||||
{ type: "A", values: [rx, ry, 0, 0, 1, cx + rx, cy] },
|
{ type: "A", values: [rx, ry, 0, 0, 1, cx + rx, cy] },
|
||||||
{ type: "Z", values: [] }
|
{ type: "Z", values: [] },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (options && options.normalize === true) {
|
if (options && options.normalize === true) {
|
||||||
|
@ -1077,7 +1118,7 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
SVGLineElement.prototype.getPathData = function () {
|
SVGLineElement.prototype.getPathData = function () {
|
||||||
return [
|
return [
|
||||||
{ type: "M", values: [this.x1.baseVal.value, this.y1.baseVal.value] },
|
{ type: "M", values: [this.x1.baseVal.value, this.y1.baseVal.value] },
|
||||||
{ type: "L", values: [this.x2.baseVal.value, this.y2.baseVal.value] }
|
{ type: "L", values: [this.x2.baseVal.value, this.y2.baseVal.value] },
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1088,8 +1129,8 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
var point = this.points.getItem(i);
|
var point = this.points.getItem(i);
|
||||||
|
|
||||||
pathData.push({
|
pathData.push({
|
||||||
type: (i === 0 ? "M" : "L"),
|
type: i === 0 ? "M" : "L",
|
||||||
values: [point.x, point.y]
|
values: [point.x, point.y],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1103,14 +1144,14 @@ if (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathDa
|
||||||
var point = this.points.getItem(i);
|
var point = this.points.getItem(i);
|
||||||
|
|
||||||
pathData.push({
|
pathData.push({
|
||||||
type: (i === 0 ? "M" : "L"),
|
type: i === 0 ? "M" : "L",
|
||||||
values: [point.x, point.y]
|
values: [point.x, point.y],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pathData.push({
|
pathData.push({
|
||||||
type: "Z",
|
type: "Z",
|
||||||
values: []
|
values: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
return pathData;
|
return pathData;
|
||||||
|
|
|
@ -24,13 +24,14 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function clear() { //Code isolation
|
(function clear() {
|
||||||
|
//Code isolation
|
||||||
|
|
||||||
function clearBoard() {
|
function clearBoard() {
|
||||||
var msg = {
|
var msg = {
|
||||||
"type": "clear",
|
type: "clear",
|
||||||
"id": "",
|
id: "",
|
||||||
"token": Tools.token
|
token: Tools.token,
|
||||||
};
|
};
|
||||||
Tools.drawAndSend(msg, Tools.list["Clear"]);
|
Tools.drawAndSend(msg, Tools.list["Clear"]);
|
||||||
}
|
}
|
||||||
|
@ -39,15 +40,15 @@
|
||||||
Tools.drawingArea.innerHTML = "";
|
Tools.drawingArea.innerHTML = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools.add({ //The new tool
|
Tools.add({
|
||||||
"name": "Clear",
|
//The new tool
|
||||||
"shortcut": "c",
|
name: "Clear",
|
||||||
"listeners": {},
|
shortcut: "c",
|
||||||
"icon": "tools/clear/clear.svg",
|
listeners: {},
|
||||||
"oneTouch": true,
|
icon: "tools/clear/clear.svg",
|
||||||
"onstart": clearBoard,
|
oneTouch: true,
|
||||||
"draw": draw,
|
onstart: clearBoard,
|
||||||
"mouseCursor": "crosshair",
|
draw: draw,
|
||||||
|
mouseCursor: "crosshair",
|
||||||
});
|
});
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
|
@ -24,10 +24,14 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () { // Code isolation
|
(function () {
|
||||||
|
// Code isolation
|
||||||
|
|
||||||
// Allocate half of the maximum server updates to cursor updates
|
// Allocate half of the maximum server updates to cursor updates
|
||||||
var MIN_CURSOR_UPDATES_INTERVAL_MS = Tools.server_config.MAX_EMIT_COUNT_PERIOD / Tools.server_config.MAX_EMIT_COUNT * 2;
|
var MIN_CURSOR_UPDATES_INTERVAL_MS =
|
||||||
|
(Tools.server_config.MAX_EMIT_COUNT_PERIOD /
|
||||||
|
Tools.server_config.MAX_EMIT_COUNT) *
|
||||||
|
2;
|
||||||
|
|
||||||
var CURSOR_DELETE_AFTER_MS = 1000 * 5;
|
var CURSOR_DELETE_AFTER_MS = 1000 * 5;
|
||||||
|
|
||||||
|
@ -35,16 +39,20 @@
|
||||||
var sending = true;
|
var sending = true;
|
||||||
|
|
||||||
var cursorTool = {
|
var cursorTool = {
|
||||||
"name": "Cursor",
|
name: "Cursor",
|
||||||
"listeners": {
|
listeners: {
|
||||||
"press": function () { sending = false },
|
press: function () {
|
||||||
"move": handleMarker,
|
sending = false;
|
||||||
"release": function () { sending = true },
|
|
||||||
},
|
},
|
||||||
"onSizeChange": onSizeChange,
|
move: handleMarker,
|
||||||
"draw": draw,
|
release: function () {
|
||||||
"mouseCursor": "crosshair",
|
sending = true;
|
||||||
"icon": "tools/pencil/icon.svg",
|
},
|
||||||
|
},
|
||||||
|
onSizeChange: onSizeChange,
|
||||||
|
draw: draw,
|
||||||
|
mouseCursor: "crosshair",
|
||||||
|
icon: "tools/pencil/icon.svg",
|
||||||
};
|
};
|
||||||
Tools.register(cursorTool);
|
Tools.register(cursorTool);
|
||||||
Tools.addToolListeners(cursorTool);
|
Tools.addToolListeners(cursorTool);
|
||||||
|
@ -74,8 +82,10 @@
|
||||||
function updateMarker() {
|
function updateMarker() {
|
||||||
if (!Tools.showMarker || !Tools.showMyCursor) return;
|
if (!Tools.showMarker || !Tools.showMyCursor) return;
|
||||||
var cur_time = Date.now();
|
var cur_time = Date.now();
|
||||||
if (cur_time - lastCursorUpdate > MIN_CURSOR_UPDATES_INTERVAL_MS &&
|
if (
|
||||||
(sending || Tools.curTool.showMarker)) {
|
cur_time - lastCursorUpdate > MIN_CURSOR_UPDATES_INTERVAL_MS &&
|
||||||
|
(sending || Tools.curTool.showMarker)
|
||||||
|
) {
|
||||||
Tools.drawAndSend(message, cursorTool);
|
Tools.drawAndSend(message, cursorTool);
|
||||||
lastCursorUpdate = cur_time;
|
lastCursorUpdate = cur_time;
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,7 +96,10 @@
|
||||||
var cursorsElem = Tools.svg.getElementById("cursors");
|
var cursorsElem = Tools.svg.getElementById("cursors");
|
||||||
|
|
||||||
function createCursor(id) {
|
function createCursor(id) {
|
||||||
var cursor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
var cursor = document.createElementNS(
|
||||||
|
"http://www.w3.org/2000/svg",
|
||||||
|
"circle",
|
||||||
|
);
|
||||||
cursor.setAttributeNS(null, "class", "opcursor");
|
cursor.setAttributeNS(null, "class", "opcursor");
|
||||||
cursor.setAttributeNS(null, "id", id);
|
cursor.setAttributeNS(null, "id", id);
|
||||||
cursor.setAttributeNS(null, "cx", 0);
|
cursor.setAttributeNS(null, "cx", 0);
|
||||||
|
@ -104,9 +117,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw(message) {
|
function draw(message) {
|
||||||
var cursor = getCursor("cursor-" + (message.socket || 'me'));
|
var cursor = getCursor("cursor-" + (message.socket || "me"));
|
||||||
cursor.style.transform = "translate(" + message.x + "px, " + message.y + "px)";
|
cursor.style.transform =
|
||||||
if (Tools.isIE) cursor.setAttributeNS(null, "transform", "translate(" + message.x + " " + message.y + ")");
|
"translate(" + message.x + "px, " + message.y + "px)";
|
||||||
|
if (Tools.isIE)
|
||||||
|
cursor.setAttributeNS(
|
||||||
|
null,
|
||||||
|
"transform",
|
||||||
|
"translate(" + message.x + " " + message.y + ")",
|
||||||
|
);
|
||||||
cursor.setAttributeNS(null, "fill", message.color);
|
cursor.setAttributeNS(null, "fill", message.color);
|
||||||
cursor.setAttributeNS(null, "r", message.size / 2);
|
cursor.setAttributeNS(null, "r", message.size / 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function download() { //Code isolation
|
(function download() {
|
||||||
|
//Code isolation
|
||||||
|
|
||||||
function downloadSVGFile() {
|
function downloadSVGFile() {
|
||||||
var canvasCopy = Tools.svg.cloneNode(true);
|
var canvasCopy = Tools.svg.cloneNode(true);
|
||||||
|
@ -34,8 +35,11 @@
|
||||||
// Copy the stylesheets from the whiteboard to the exported SVG
|
// Copy the stylesheets from the whiteboard to the exported SVG
|
||||||
styleNode.innerHTML = Array.from(document.styleSheets)
|
styleNode.innerHTML = Array.from(document.styleSheets)
|
||||||
.filter(function (stylesheet) {
|
.filter(function (stylesheet) {
|
||||||
if (stylesheet.href && (stylesheet.href.match(/boards\/tools\/.*\.css/)
|
if (
|
||||||
|| stylesheet.href.match(/board\.css/))) {
|
stylesheet.href &&
|
||||||
|
(stylesheet.href.match(/boards\/tools\/.*\.css/) ||
|
||||||
|
stylesheet.href.match(/board\.css/))
|
||||||
|
) {
|
||||||
// This is a Stylesheet from a Tool or the Board itself, so we should include it
|
// This is a Stylesheet from a Tool or the Board itself, so we should include it
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -43,25 +47,29 @@
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.map(function (stylesheet) {
|
.map(function (stylesheet) {
|
||||||
return Array.from(stylesheet.cssRules)
|
return Array.from(stylesheet.cssRules).map(function (rule) {
|
||||||
.map(function (rule) { return rule.cssText })
|
return rule.cssText;
|
||||||
}).join("\n")
|
});
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
canvasCopy.appendChild(styleNode);
|
canvasCopy.appendChild(styleNode);
|
||||||
var outerHTML = canvasCopy.outerHTML || new XMLSerializer().serializeToString(canvasCopy);
|
var outerHTML =
|
||||||
var blob = new Blob([outerHTML], { type: 'image/svg+xml;charset=utf-8' });
|
canvasCopy.outerHTML || new XMLSerializer().serializeToString(canvasCopy);
|
||||||
|
var blob = new Blob([outerHTML], { type: "image/svg+xml;charset=utf-8" });
|
||||||
downloadContent(blob, Tools.boardName + ".svg");
|
downloadContent(blob, Tools.boardName + ".svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadContent(blob, filename) {
|
function downloadContent(blob, filename) {
|
||||||
if (window.navigator.msSaveBlob) { // Internet Explorer
|
if (window.navigator.msSaveBlob) {
|
||||||
|
// Internet Explorer
|
||||||
window.navigator.msSaveBlob(blob, filename);
|
window.navigator.msSaveBlob(blob, filename);
|
||||||
} else {
|
} else {
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
var element = document.createElement('a');
|
var element = document.createElement("a");
|
||||||
element.setAttribute('href', url);
|
element.setAttribute("href", url);
|
||||||
element.setAttribute('download', filename);
|
element.setAttribute("download", filename);
|
||||||
element.style.display = 'none';
|
element.style.display = "none";
|
||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
element.click();
|
element.click();
|
||||||
document.body.removeChild(element);
|
document.body.removeChild(element);
|
||||||
|
@ -69,14 +77,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools.add({ //The new tool
|
Tools.add({
|
||||||
"name": "Download",
|
//The new tool
|
||||||
"shortcut": "d",
|
name: "Download",
|
||||||
"listeners": {},
|
shortcut: "d",
|
||||||
"icon": "tools/download/download.svg",
|
listeners: {},
|
||||||
"oneTouch": true,
|
icon: "tools/download/download.svg",
|
||||||
"onstart": downloadSVGFile,
|
oneTouch: true,
|
||||||
"mouseCursor": "crosshair",
|
onstart: downloadSVGFile,
|
||||||
|
mouseCursor: "crosshair",
|
||||||
});
|
});
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
|
@ -24,35 +24,36 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () { //Code isolation
|
(function () {
|
||||||
var curUpdate = { //The data of the message that will be sent for every new point
|
//Code isolation
|
||||||
'type': 'update',
|
var curUpdate = {
|
||||||
'id': "",
|
//The data of the message that will be sent for every new point
|
||||||
'x': 0,
|
type: "update",
|
||||||
'y': 0,
|
id: "",
|
||||||
'x2': 0,
|
x: 0,
|
||||||
'y2': 0
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 0,
|
||||||
},
|
},
|
||||||
lastPos = { x: 0, y: 0 },
|
lastPos = { x: 0, y: 0 },
|
||||||
lastTime = performance.now(); //The time at which the last point was drawn
|
lastTime = performance.now(); //The time at which the last point was drawn
|
||||||
|
|
||||||
function start(x, y, evt) {
|
function start(x, y, evt) {
|
||||||
|
|
||||||
//Prevent the press from being interpreted by the browser
|
//Prevent the press from being interpreted by the browser
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
||||||
curUpdate.id = Tools.generateUID("e"); //"e" for ellipse
|
curUpdate.id = Tools.generateUID("e"); //"e" for ellipse
|
||||||
|
|
||||||
Tools.drawAndSend({
|
Tools.drawAndSend({
|
||||||
'type': 'ellipse',
|
type: "ellipse",
|
||||||
'id': curUpdate.id,
|
id: curUpdate.id,
|
||||||
'color': Tools.getColor(),
|
color: Tools.getColor(),
|
||||||
'size': Tools.getSize(),
|
size: Tools.getSize(),
|
||||||
'opacity': Tools.getOpacity(),
|
opacity: Tools.getOpacity(),
|
||||||
'x': x,
|
x: x,
|
||||||
'y': y,
|
y: y,
|
||||||
'x2': x,
|
x2: x,
|
||||||
'y2': y
|
y2: y,
|
||||||
});
|
});
|
||||||
|
|
||||||
curUpdate.id = curUpdate.id;
|
curUpdate.id = curUpdate.id;
|
||||||
|
@ -74,14 +75,16 @@
|
||||||
function doUpdate(force) {
|
function doUpdate(force) {
|
||||||
if (!curUpdate.id) return; // Not currently drawing
|
if (!curUpdate.id) return; // Not currently drawing
|
||||||
if (drawingCircle()) {
|
if (drawingCircle()) {
|
||||||
var x0 = curUpdate['x'], y0 = curUpdate['y'];
|
var x0 = curUpdate["x"],
|
||||||
var deltaX = lastPos.x - x0, deltaY = lastPos.y - y0;
|
y0 = curUpdate["y"];
|
||||||
|
var deltaX = lastPos.x - x0,
|
||||||
|
deltaY = lastPos.y - y0;
|
||||||
var diameter = Math.max(Math.abs(deltaX), Math.abs(deltaY));
|
var diameter = Math.max(Math.abs(deltaX), Math.abs(deltaY));
|
||||||
curUpdate['x2'] = x0 + (deltaX > 0 ? diameter : -diameter);
|
curUpdate["x2"] = x0 + (deltaX > 0 ? diameter : -diameter);
|
||||||
curUpdate['y2'] = y0 + (deltaY > 0 ? diameter : -diameter);
|
curUpdate["y2"] = y0 + (deltaY > 0 ? diameter : -diameter);
|
||||||
} else {
|
} else {
|
||||||
curUpdate['x2'] = lastPos.x;
|
curUpdate["x2"] = lastPos.x;
|
||||||
curUpdate['y2'] = lastPos.y;
|
curUpdate["y2"] = lastPos.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (performance.now() - lastTime > 70 || force) {
|
if (performance.now() - lastTime > 70 || force) {
|
||||||
|
@ -106,13 +109,17 @@
|
||||||
createShape(data);
|
createShape(data);
|
||||||
break;
|
break;
|
||||||
case "update":
|
case "update":
|
||||||
var shape = svg.getElementById(data['id']);
|
var shape = svg.getElementById(data["id"]);
|
||||||
if (!shape) {
|
if (!shape) {
|
||||||
console.error("Ellipse: Hmmm... I received an update for a shape that has not been created (%s).", data['id']);
|
console.error(
|
||||||
createShape({ //create a new shape in order not to loose the points
|
"Ellipse: Hmmm... I received an update for a shape that has not been created (%s).",
|
||||||
"id": data['id'],
|
data["id"],
|
||||||
"x": data['x2'],
|
);
|
||||||
"y": data['y2']
|
createShape({
|
||||||
|
//create a new shape in order not to loose the points
|
||||||
|
id: data["id"],
|
||||||
|
x: data["x2"],
|
||||||
|
y: data["y2"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateShape(shape, data);
|
updateShape(shape, data);
|
||||||
|
@ -126,47 +133,51 @@
|
||||||
var svg = Tools.svg;
|
var svg = Tools.svg;
|
||||||
function createShape(data) {
|
function createShape(data) {
|
||||||
//Creates a new shape on the canvas, or update a shape that already exists with new information
|
//Creates a new shape on the canvas, or update a shape that already exists with new information
|
||||||
var shape = svg.getElementById(data.id) || Tools.createSVGElement("ellipse");
|
var shape =
|
||||||
|
svg.getElementById(data.id) || Tools.createSVGElement("ellipse");
|
||||||
updateShape(shape, data);
|
updateShape(shape, data);
|
||||||
shape.id = data.id;
|
shape.id = data.id;
|
||||||
//If some data is not provided, choose default value. The shape may be updated later
|
//If some data is not provided, choose default value. The shape may be updated later
|
||||||
shape.setAttribute("stroke", data.color || "black");
|
shape.setAttribute("stroke", data.color || "black");
|
||||||
shape.setAttribute("stroke-width", data.size || 10);
|
shape.setAttribute("stroke-width", data.size || 10);
|
||||||
shape.setAttribute("opacity", Math.max(0.1, Math.min(1, data.opacity)) || 1);
|
shape.setAttribute(
|
||||||
|
"opacity",
|
||||||
|
Math.max(0.1, Math.min(1, data.opacity)) || 1,
|
||||||
|
);
|
||||||
Tools.drawingArea.appendChild(shape);
|
Tools.drawingArea.appendChild(shape);
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateShape(shape, data) {
|
function updateShape(shape, data) {
|
||||||
shape.cx.baseVal.value = Math.round((data['x2'] + data['x']) / 2);
|
shape.cx.baseVal.value = Math.round((data["x2"] + data["x"]) / 2);
|
||||||
shape.cy.baseVal.value = Math.round((data['y2'] + data['y']) / 2);
|
shape.cy.baseVal.value = Math.round((data["y2"] + data["y"]) / 2);
|
||||||
shape.rx.baseVal.value = Math.abs(data['x2'] - data['x']) / 2;
|
shape.rx.baseVal.value = Math.abs(data["x2"] - data["x"]) / 2;
|
||||||
shape.ry.baseVal.value = Math.abs(data['y2'] - data['y']) / 2;
|
shape.ry.baseVal.value = Math.abs(data["y2"] - data["y"]) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawingCircle() {
|
function drawingCircle() {
|
||||||
return circleTool.secondary.active;
|
return circleTool.secondary.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
var circleTool = { //The new tool
|
var circleTool = {
|
||||||
"name": "Ellipse",
|
//The new tool
|
||||||
"icon": "tools/ellipse/icon-ellipse.svg",
|
name: "Ellipse",
|
||||||
"secondary": {
|
icon: "tools/ellipse/icon-ellipse.svg",
|
||||||
"name": "Circle",
|
secondary: {
|
||||||
"icon": "tools/ellipse/icon-circle.svg",
|
name: "Circle",
|
||||||
"active": false,
|
icon: "tools/ellipse/icon-circle.svg",
|
||||||
"switch": doUpdate,
|
active: false,
|
||||||
|
switch: doUpdate,
|
||||||
},
|
},
|
||||||
"shortcut": "c",
|
shortcut: "c",
|
||||||
"listeners": {
|
listeners: {
|
||||||
"press": start,
|
press: start,
|
||||||
"move": move,
|
move: move,
|
||||||
"release": stop,
|
release: stop,
|
||||||
},
|
},
|
||||||
"draw": draw,
|
draw: draw,
|
||||||
"mouseCursor": "crosshair",
|
mouseCursor: "crosshair",
|
||||||
"stylesheet": "tools/ellipse/ellipse.css"
|
stylesheet: "tools/ellipse/ellipse.css",
|
||||||
};
|
};
|
||||||
Tools.add(circleTool);
|
Tools.add(circleTool);
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function eraser() { //Code isolation
|
(function eraser() {
|
||||||
|
//Code isolation
|
||||||
|
|
||||||
var erasing = false;
|
var erasing = false;
|
||||||
|
|
||||||
|
@ -36,8 +37,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = {
|
var msg = {
|
||||||
"type": "delete",
|
type: "delete",
|
||||||
"id": ""
|
id: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
function inDrawingArea(elem) {
|
function inDrawingArea(elem) {
|
||||||
|
@ -53,7 +54,12 @@
|
||||||
var touch = evt.touches[0];
|
var touch = evt.touches[0];
|
||||||
target = document.elementFromPoint(touch.clientX, touch.clientY);
|
target = document.elementFromPoint(touch.clientX, touch.clientY);
|
||||||
}
|
}
|
||||||
if (erasing && target !== Tools.svg && target !== Tools.drawingArea && inDrawingArea(target)) {
|
if (
|
||||||
|
erasing &&
|
||||||
|
target !== Tools.svg &&
|
||||||
|
target !== Tools.drawingArea &&
|
||||||
|
inDrawingArea(target)
|
||||||
|
) {
|
||||||
msg.id = target.id;
|
msg.id = target.id;
|
||||||
Tools.drawAndSend(msg);
|
Tools.drawAndSend(msg);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +75,10 @@
|
||||||
//TODO: add the ability to erase only some points in a line
|
//TODO: add the ability to erase only some points in a line
|
||||||
case "delete":
|
case "delete":
|
||||||
elem = svg.getElementById(data.id);
|
elem = svg.getElementById(data.id);
|
||||||
if (elem === null) console.error("Eraser: Tried to delete an element that does not exist.");
|
if (elem === null)
|
||||||
|
console.error(
|
||||||
|
"Eraser: Tried to delete an element that does not exist.",
|
||||||
|
);
|
||||||
else Tools.drawingArea.removeChild(elem);
|
else Tools.drawingArea.removeChild(elem);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -80,18 +89,18 @@
|
||||||
|
|
||||||
var svg = Tools.svg;
|
var svg = Tools.svg;
|
||||||
|
|
||||||
Tools.add({ //The new tool
|
Tools.add({
|
||||||
"name": "Eraser",
|
//The new tool
|
||||||
"shortcut": "e",
|
name: "Eraser",
|
||||||
"listeners": {
|
shortcut: "e",
|
||||||
"press": startErasing,
|
listeners: {
|
||||||
"move": erase,
|
press: startErasing,
|
||||||
"release": stopErasing,
|
move: erase,
|
||||||
|
release: stopErasing,
|
||||||
},
|
},
|
||||||
"draw": draw,
|
draw: draw,
|
||||||
"icon": "tools/eraser/icon.svg",
|
icon: "tools/eraser/icon.svg",
|
||||||
"mouseCursor": "crosshair",
|
mouseCursor: "crosshair",
|
||||||
"showMarker": true,
|
showMarker: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function grid() { //Code isolation
|
(function grid() {
|
||||||
|
//Code isolation
|
||||||
|
|
||||||
var index = 0; //grid off by default
|
var index = 0; //grid off by default
|
||||||
var states = ["none", "url(#grid)", "url(#dots)"];
|
var states = ["none", "url(#grid)", "url(#dots)"];
|
||||||
|
@ -41,34 +42,37 @@
|
||||||
id: "smallGrid",
|
id: "smallGrid",
|
||||||
width: "30",
|
width: "30",
|
||||||
height: "30",
|
height: "30",
|
||||||
patternUnits: "userSpaceOnUse"
|
patternUnits: "userSpaceOnUse",
|
||||||
});
|
});
|
||||||
smallGrid.appendChild(
|
smallGrid.appendChild(
|
||||||
Tools.createSVGElement("path", {
|
Tools.createSVGElement("path", {
|
||||||
d: "M 30 0 L 0 0 0 30",
|
d: "M 30 0 L 0 0 0 30",
|
||||||
fill: "none",
|
fill: "none",
|
||||||
stroke: "gray",
|
stroke: "gray",
|
||||||
'stroke-width': "0.5"
|
"stroke-width": "0.5",
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
// (outer) grid
|
// (outer) grid
|
||||||
var grid = Tools.createSVGElement("pattern", {
|
var grid = Tools.createSVGElement("pattern", {
|
||||||
id: "grid",
|
id: "grid",
|
||||||
width: "300",
|
width: "300",
|
||||||
height: "300",
|
height: "300",
|
||||||
patternUnits: "userSpaceOnUse"
|
patternUnits: "userSpaceOnUse",
|
||||||
});
|
});
|
||||||
grid.appendChild(Tools.createSVGElement("rect", {
|
grid.appendChild(
|
||||||
|
Tools.createSVGElement("rect", {
|
||||||
width: "300",
|
width: "300",
|
||||||
height: "300",
|
height: "300",
|
||||||
fill: "url(#smallGrid)"
|
fill: "url(#smallGrid)",
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
grid.appendChild(
|
grid.appendChild(
|
||||||
Tools.createSVGElement("path", {
|
Tools.createSVGElement("path", {
|
||||||
d: "M 300 0 L 0 0 0 300",
|
d: "M 300 0 L 0 0 0 300",
|
||||||
fill: "none",
|
fill: "none",
|
||||||
stroke: "gray", 'stroke-width': "1"
|
stroke: "gray",
|
||||||
})
|
"stroke-width": "1",
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
// dots
|
// dots
|
||||||
var dots = Tools.createSVGElement("pattern", {
|
var dots = Tools.createSVGElement("pattern", {
|
||||||
|
@ -77,14 +81,16 @@
|
||||||
height: "30",
|
height: "30",
|
||||||
x: "-10",
|
x: "-10",
|
||||||
y: "-10",
|
y: "-10",
|
||||||
patternUnits: "userSpaceOnUse"
|
patternUnits: "userSpaceOnUse",
|
||||||
});
|
});
|
||||||
dots.appendChild(Tools.createSVGElement("circle", {
|
dots.appendChild(
|
||||||
|
Tools.createSVGElement("circle", {
|
||||||
fill: "gray",
|
fill: "gray",
|
||||||
cx: "10",
|
cx: "10",
|
||||||
cy: "10",
|
cy: "10",
|
||||||
r: "2"
|
r: "2",
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
var defs = Tools.svg.getElementById("defs");
|
var defs = Tools.svg.getElementById("defs");
|
||||||
defs.appendChild(smallGrid);
|
defs.appendChild(smallGrid);
|
||||||
|
@ -98,21 +104,22 @@
|
||||||
// create grid container
|
// create grid container
|
||||||
var gridContainer = Tools.createSVGElement("rect", {
|
var gridContainer = Tools.createSVGElement("rect", {
|
||||||
id: "gridContainer",
|
id: "gridContainer",
|
||||||
width: "100%", height: "100%",
|
width: "100%",
|
||||||
fill: states[index]
|
height: "100%",
|
||||||
|
fill: states[index],
|
||||||
});
|
});
|
||||||
Tools.svg.insertBefore(gridContainer, Tools.drawingArea);
|
Tools.svg.insertBefore(gridContainer, Tools.drawingArea);
|
||||||
return gridContainer;
|
return gridContainer;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
Tools.add({ //The new tool
|
Tools.add({
|
||||||
"name": "Grid",
|
//The new tool
|
||||||
"shortcut": "g",
|
name: "Grid",
|
||||||
"listeners": {},
|
shortcut: "g",
|
||||||
"icon": "tools/grid/icon.svg",
|
listeners: {},
|
||||||
"oneTouch": true,
|
icon: "tools/grid/icon.svg",
|
||||||
"onstart": toggleGrid,
|
oneTouch: true,
|
||||||
"mouseCursor": "crosshair",
|
onstart: toggleGrid,
|
||||||
|
mouseCursor: "crosshair",
|
||||||
});
|
});
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
|
@ -24,12 +24,13 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function hand() { //Code isolation
|
(function hand() {
|
||||||
|
//Code isolation
|
||||||
var selectorStates = {
|
var selectorStates = {
|
||||||
pointing: 0,
|
pointing: 0,
|
||||||
selecting: 1,
|
selecting: 1,
|
||||||
transform: 2
|
transform: 2,
|
||||||
}
|
};
|
||||||
var selected = null;
|
var selected = null;
|
||||||
var selected_els = [];
|
var selected_els = [];
|
||||||
var selectionRect = createSelectorRect();
|
var selectionRect = createSelectorRect();
|
||||||
|
@ -40,7 +41,11 @@
|
||||||
var last_sent = 0;
|
var last_sent = 0;
|
||||||
var blockedSelectionButtons = Tools.server_config.BLOCKED_SELECTION_BUTTONS;
|
var blockedSelectionButtons = Tools.server_config.BLOCKED_SELECTION_BUTTONS;
|
||||||
var selectionButtons = [
|
var selectionButtons = [
|
||||||
createButton("delete", "delete", 24, 24,
|
createButton(
|
||||||
|
"delete",
|
||||||
|
"delete",
|
||||||
|
24,
|
||||||
|
24,
|
||||||
function (me, bbox, s) {
|
function (me, bbox, s) {
|
||||||
me.width.baseVal.value = me.origWidth / s;
|
me.width.baseVal.value = me.origWidth / s;
|
||||||
me.height.baseVal.value = me.origHeight / s;
|
me.height.baseVal.value = me.origHeight / s;
|
||||||
|
@ -48,9 +53,14 @@
|
||||||
me.y.baseVal.value = bbox.r[1] - (me.origHeight + 3) / s;
|
me.y.baseVal.value = bbox.r[1] - (me.origHeight + 3) / s;
|
||||||
me.style.display = "";
|
me.style.display = "";
|
||||||
},
|
},
|
||||||
deleteSelection),
|
deleteSelection,
|
||||||
|
),
|
||||||
|
|
||||||
createButton("duplicate", "duplicate", 24, 24,
|
createButton(
|
||||||
|
"duplicate",
|
||||||
|
"duplicate",
|
||||||
|
24,
|
||||||
|
24,
|
||||||
function (me, bbox, s) {
|
function (me, bbox, s) {
|
||||||
me.width.baseVal.value = me.origWidth / s;
|
me.width.baseVal.value = me.origWidth / s;
|
||||||
me.height.baseVal.value = me.origHeight / s;
|
me.height.baseVal.value = me.origHeight / s;
|
||||||
|
@ -58,9 +68,14 @@
|
||||||
me.y.baseVal.value = bbox.r[1] - (me.origHeight + 3) / s;
|
me.y.baseVal.value = bbox.r[1] - (me.origHeight + 3) / s;
|
||||||
me.style.display = "";
|
me.style.display = "";
|
||||||
},
|
},
|
||||||
duplicateSelection),
|
duplicateSelection,
|
||||||
|
),
|
||||||
|
|
||||||
createButton("scaleHandle", "handle", 14, 14,
|
createButton(
|
||||||
|
"scaleHandle",
|
||||||
|
"handle",
|
||||||
|
14,
|
||||||
|
14,
|
||||||
function (me, bbox, s) {
|
function (me, bbox, s) {
|
||||||
me.width.baseVal.value = me.origWidth / s;
|
me.width.baseVal.value = me.origWidth / s;
|
||||||
me.height.baseVal.value = me.origHeight / s;
|
me.height.baseVal.value = me.origHeight / s;
|
||||||
|
@ -68,7 +83,8 @@
|
||||||
me.y.baseVal.value = bbox.r[1] + bbox.b[1] - me.origHeight / (2 * s);
|
me.y.baseVal.value = bbox.r[1] + bbox.b[1] - me.origHeight / (2 * s);
|
||||||
me.style.display = "";
|
me.style.display = "";
|
||||||
},
|
},
|
||||||
startScalingTransform)
|
startScalingTransform,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (i in blockedSelectionButtons) {
|
for (i in blockedSelectionButtons) {
|
||||||
|
@ -88,7 +104,7 @@
|
||||||
var parentMathematics = els.find(function (el) {
|
var parentMathematics = els.find(function (el) {
|
||||||
return el.getAttribute("class") === "MathElement";
|
return el.getAttribute("class") === "MathElement";
|
||||||
});
|
});
|
||||||
if ((parentMathematics) && parentMathematics.tagName === "svg") {
|
if (parentMathematics && parentMathematics.tagName === "svg") {
|
||||||
target = parentMathematics;
|
target = parentMathematics;
|
||||||
}
|
}
|
||||||
return target || el;
|
return target || el;
|
||||||
|
@ -96,22 +112,22 @@
|
||||||
|
|
||||||
function deleteSelection() {
|
function deleteSelection() {
|
||||||
var msgs = selected_els.map(function (el) {
|
var msgs = selected_els.map(function (el) {
|
||||||
return ({
|
return {
|
||||||
"type": "delete",
|
type: "delete",
|
||||||
"id": el.id
|
id: el.id,
|
||||||
});
|
};
|
||||||
});
|
});
|
||||||
var data = {
|
var data = {
|
||||||
_children: msgs
|
_children: msgs,
|
||||||
}
|
};
|
||||||
Tools.drawAndSend(data);
|
Tools.drawAndSend(data);
|
||||||
selected_els = [];
|
selected_els = [];
|
||||||
hideSelectionUI();
|
hideSelectionUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
function duplicateSelection() {
|
function duplicateSelection() {
|
||||||
if (!(selectorState == selectorStates.pointing)
|
if (!(selectorState == selectorStates.pointing) || selected_els.length == 0)
|
||||||
|| (selected_els.length == 0)) return;
|
return;
|
||||||
var msgs = [];
|
var msgs = [];
|
||||||
var newids = [];
|
var newids = [];
|
||||||
for (var i = 0; i < selected_els.length; i++) {
|
for (var i = 0; i < selected_els.length; i++) {
|
||||||
|
@ -119,7 +135,7 @@
|
||||||
msgs[i] = {
|
msgs[i] = {
|
||||||
type: "copy",
|
type: "copy",
|
||||||
id: id,
|
id: id,
|
||||||
newid: Tools.generateUID(id[0])
|
newid: Tools.generateUID(id[0]),
|
||||||
};
|
};
|
||||||
newids[i] = id;
|
newids[i] = id;
|
||||||
}
|
}
|
||||||
|
@ -146,10 +162,18 @@
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createButton(name, icon, width, height, drawCallback, clickCallback) {
|
function createButton(
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
drawCallback,
|
||||||
|
clickCallback,
|
||||||
|
) {
|
||||||
var shape = Tools.createSVGElement("image", {
|
var shape = Tools.createSVGElement("image", {
|
||||||
href: "tools/hand/" + icon + ".svg",
|
href: "tools/hand/" + icon + ".svg",
|
||||||
width: width, height: height
|
width: width,
|
||||||
|
height: height,
|
||||||
});
|
});
|
||||||
shape.style.display = "none";
|
shape.style.display = "none";
|
||||||
shape.origWidth = width;
|
shape.origWidth = width;
|
||||||
|
@ -164,9 +188,11 @@
|
||||||
var scale = getScale();
|
var scale = getScale();
|
||||||
var selectionBBox = selectionRect.transformedBBox();
|
var selectionBBox = selectionRect.transformedBBox();
|
||||||
for (var i = 0; i < selectionButtons.length; i++) {
|
for (var i = 0; i < selectionButtons.length; i++) {
|
||||||
selectionButtons[i].drawCallback(selectionButtons[i],
|
selectionButtons[i].drawCallback(
|
||||||
|
selectionButtons[i],
|
||||||
selectionBBox,
|
selectionBBox,
|
||||||
scale);
|
scale,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +219,12 @@
|
||||||
transform_elements = selected_els.map(function (el) {
|
transform_elements = selected_els.map(function (el) {
|
||||||
var tmatrix = get_transform_matrix(el);
|
var tmatrix = get_transform_matrix(el);
|
||||||
return {
|
return {
|
||||||
a: tmatrix.a, b: tmatrix.b, c: tmatrix.c,
|
a: tmatrix.a,
|
||||||
d: tmatrix.d, e: tmatrix.e, f: tmatrix.f
|
b: tmatrix.b,
|
||||||
|
c: tmatrix.c,
|
||||||
|
d: tmatrix.d,
|
||||||
|
e: tmatrix.e,
|
||||||
|
f: tmatrix.f,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
var tmatrix = get_transform_matrix(selectionRect);
|
var tmatrix = get_transform_matrix(selectionRect);
|
||||||
|
@ -215,14 +245,20 @@
|
||||||
transform_elements = selected_els.map(function (el) {
|
transform_elements = selected_els.map(function (el) {
|
||||||
var tmatrix = get_transform_matrix(el);
|
var tmatrix = get_transform_matrix(el);
|
||||||
return {
|
return {
|
||||||
a: tmatrix.a, b: tmatrix.b, c: tmatrix.c,
|
a: tmatrix.a,
|
||||||
d: tmatrix.d, e: tmatrix.e, f: tmatrix.f
|
b: tmatrix.b,
|
||||||
|
c: tmatrix.c,
|
||||||
|
d: tmatrix.d,
|
||||||
|
e: tmatrix.e,
|
||||||
|
f: tmatrix.f,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
var tmatrix = get_transform_matrix(selectionRect);
|
var tmatrix = get_transform_matrix(selectionRect);
|
||||||
selectionRectTransform = {
|
selectionRectTransform = {
|
||||||
a: tmatrix.a, d: tmatrix.d,
|
a: tmatrix.a,
|
||||||
e: tmatrix.e, f: tmatrix.f
|
d: tmatrix.d,
|
||||||
|
e: tmatrix.e,
|
||||||
|
f: tmatrix.f,
|
||||||
};
|
};
|
||||||
currentTransform = scaleSelection;
|
currentTransform = scaleSelection;
|
||||||
}
|
}
|
||||||
|
@ -242,13 +278,14 @@
|
||||||
tmatrix.f = 0;
|
tmatrix.f = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function calculateSelection() {
|
function calculateSelection() {
|
||||||
var selectionTBBox = selectionRect.transformedBBox();
|
var selectionTBBox = selectionRect.transformedBBox();
|
||||||
var elements = Tools.drawingArea.children;
|
var elements = Tools.drawingArea.children;
|
||||||
var selected = [];
|
var selected = [];
|
||||||
for (var i = 0; i < elements.length; i++) {
|
for (var i = 0; i < elements.length; i++) {
|
||||||
if (transformedBBoxIntersects(selectionTBBox, elements[i].transformedBBox()))
|
if (
|
||||||
|
transformedBBoxIntersects(selectionTBBox, elements[i].transformedBBox())
|
||||||
|
)
|
||||||
selected.push(Tools.drawingArea.children[i]);
|
selected.push(Tools.drawingArea.children[i]);
|
||||||
}
|
}
|
||||||
return selected;
|
return selected;
|
||||||
|
@ -268,12 +305,12 @@
|
||||||
c: oldTransform.c,
|
c: oldTransform.c,
|
||||||
d: oldTransform.d,
|
d: oldTransform.d,
|
||||||
e: dx + oldTransform.e,
|
e: dx + oldTransform.e,
|
||||||
f: dy + oldTransform.f
|
f: dy + oldTransform.f,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
var msg = {
|
var msg = {
|
||||||
_children: msgs
|
_children: msgs,
|
||||||
};
|
};
|
||||||
var tmatrix = get_transform_matrix(selectionRect);
|
var tmatrix = get_transform_matrix(selectionRect);
|
||||||
tmatrix.e = dx + selectionRectTransform.x;
|
tmatrix.e = dx + selectionRectTransform.x;
|
||||||
|
@ -288,18 +325,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function scaleSelection(x, y) {
|
function scaleSelection(x, y) {
|
||||||
var rx = (x - selected.x) / (selected.w);
|
var rx = (x - selected.x) / selected.w;
|
||||||
var ry = (y - selected.y) / (selected.h);
|
var ry = (y - selected.y) / selected.h;
|
||||||
var msgs = selected_els.map(function (el, i) {
|
var msgs = selected_els.map(function (el, i) {
|
||||||
var oldTransform = transform_elements[i];
|
var oldTransform = transform_elements[i];
|
||||||
var x = el.transformedBBox().r[0];
|
var x = el.transformedBBox().r[0];
|
||||||
var y = el.transformedBBox().r[1];
|
var y = el.transformedBBox().r[1];
|
||||||
var a = oldTransform.a * rx;
|
var a = oldTransform.a * rx;
|
||||||
var d = oldTransform.d * ry;
|
var d = oldTransform.d * ry;
|
||||||
var e = selected.x * (1 - rx) - x * a +
|
var e =
|
||||||
(x * oldTransform.a + oldTransform.e) * rx
|
selected.x * (1 - rx) -
|
||||||
var f = selected.y * (1 - ry) - y * d +
|
x * a +
|
||||||
(y * oldTransform.d + oldTransform.f) * ry
|
(x * oldTransform.a + oldTransform.e) * rx;
|
||||||
|
var f =
|
||||||
|
selected.y * (1 - ry) -
|
||||||
|
y * d +
|
||||||
|
(y * oldTransform.d + oldTransform.f) * ry;
|
||||||
return {
|
return {
|
||||||
type: "update",
|
type: "update",
|
||||||
id: el.id,
|
id: el.id,
|
||||||
|
@ -309,21 +350,23 @@
|
||||||
c: oldTransform.c,
|
c: oldTransform.c,
|
||||||
d: d,
|
d: d,
|
||||||
e: e,
|
e: e,
|
||||||
f: f
|
f: f,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
var msg = {
|
var msg = {
|
||||||
_children: msgs
|
_children: msgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
var tmatrix = get_transform_matrix(selectionRect);
|
var tmatrix = get_transform_matrix(selectionRect);
|
||||||
tmatrix.a = rx;
|
tmatrix.a = rx;
|
||||||
tmatrix.d = ry;
|
tmatrix.d = ry;
|
||||||
tmatrix.e = selectionRectTransform.e +
|
tmatrix.e =
|
||||||
selectionRect.x.baseVal.value * (selectionRectTransform.a - rx)
|
selectionRectTransform.e +
|
||||||
tmatrix.f = selectionRectTransform.f +
|
selectionRect.x.baseVal.value * (selectionRectTransform.a - rx);
|
||||||
selectionRect.y.baseVal.value * (selectionRectTransform.d - ry)
|
tmatrix.f =
|
||||||
|
selectionRectTransform.f +
|
||||||
|
selectionRect.y.baseVal.value * (selectionRectTransform.d - ry);
|
||||||
var now = performance.now();
|
var now = performance.now();
|
||||||
if (now - last_sent > 70) {
|
if (now - last_sent > 70) {
|
||||||
last_sent = now;
|
last_sent = now;
|
||||||
|
@ -347,8 +390,12 @@
|
||||||
selectionRect.y.baseVal.value = bbox.r[1];
|
selectionRect.y.baseVal.value = bbox.r[1];
|
||||||
selectionRect.width.baseVal.value = bbox.a[0];
|
selectionRect.width.baseVal.value = bbox.a[0];
|
||||||
selectionRect.height.baseVal.value = bbox.b[1];
|
selectionRect.height.baseVal.value = bbox.b[1];
|
||||||
tmatrix.a = 1; tmatrix.b = 0; tmatrix.c = 0;
|
tmatrix.a = 1;
|
||||||
tmatrix.d = 1; tmatrix.e = 0; tmatrix.f = 0;
|
tmatrix.b = 0;
|
||||||
|
tmatrix.c = 0;
|
||||||
|
tmatrix.d = 1;
|
||||||
|
tmatrix.e = 0;
|
||||||
|
tmatrix.f = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_transform_matrix(elem) {
|
function get_transform_matrix(elem) {
|
||||||
|
@ -364,7 +411,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (transform == null) {
|
if (transform == null) {
|
||||||
transform = elem.transform.baseVal.createSVGTransformFromMatrix(Tools.svg.createSVGMatrix());
|
transform = elem.transform.baseVal.createSVGTransformFromMatrix(
|
||||||
|
Tools.svg.createSVGMatrix(),
|
||||||
|
);
|
||||||
elem.transform.baseVal.appendItem(transform);
|
elem.transform.baseVal.appendItem(transform);
|
||||||
}
|
}
|
||||||
return transform.matrix;
|
return transform.matrix;
|
||||||
|
@ -373,15 +422,17 @@
|
||||||
function draw(data) {
|
function draw(data) {
|
||||||
if (data._children) {
|
if (data._children) {
|
||||||
batchCall(draw, data._children);
|
batchCall(draw, data._children);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case "update":
|
case "update":
|
||||||
var elem = Tools.svg.getElementById(data.id);
|
var elem = Tools.svg.getElementById(data.id);
|
||||||
if (!elem) throw new Error("Mover: Tried to move an element that does not exist.");
|
if (!elem)
|
||||||
|
throw new Error(
|
||||||
|
"Mover: Tried to move an element that does not exist.",
|
||||||
|
);
|
||||||
var tmatrix = get_transform_matrix(elem);
|
var tmatrix = get_transform_matrix(elem);
|
||||||
for (i in data.transform) {
|
for (i in data.transform) {
|
||||||
tmatrix[i] = data.transform[i]
|
tmatrix[i] = data.transform[i];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "copy":
|
case "copy":
|
||||||
|
@ -394,7 +445,10 @@
|
||||||
messageForTool(data);
|
messageForTool(data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("Mover: 'move' instruction with unknown type. ", data);
|
throw new Error(
|
||||||
|
"Mover: 'move' instruction with unknown type. ",
|
||||||
|
data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,7 +462,9 @@
|
||||||
}
|
}
|
||||||
if (button) {
|
if (button) {
|
||||||
button.clickCallback(x, y, evt);
|
button.clickCallback(x, y, evt);
|
||||||
} else if (pointInTransformedBBox([x, y], selectionRect.transformedBBox())) {
|
} else if (
|
||||||
|
pointInTransformedBBox([x, y], selectionRect.transformedBBox())
|
||||||
|
) {
|
||||||
hideSelectionButtons();
|
hideSelectionButtons();
|
||||||
startMovingElements(x, y, evt);
|
startMovingElements(x, y, evt);
|
||||||
} else if (Tools.drawingArea.contains(evt.target)) {
|
} else if (Tools.drawingArea.contains(evt.target)) {
|
||||||
|
@ -427,8 +483,7 @@
|
||||||
if (selected_els.length == 0) {
|
if (selected_els.length == 0) {
|
||||||
hideSelectionUI();
|
hideSelectionUI();
|
||||||
}
|
}
|
||||||
} else if (selectorState == selectorStates.transform)
|
} else if (selectorState == selectorStates.transform) resetSelectionRect();
|
||||||
resetSelectionRect();
|
|
||||||
if (selected_els.length != 0) showSelectionButtons();
|
if (selected_els.length != 0) showSelectionButtons();
|
||||||
transform_elements = [];
|
transform_elements = [];
|
||||||
selectorState = selectorStates.pointing;
|
selectorState = selectorStates.pointing;
|
||||||
|
@ -447,11 +502,12 @@
|
||||||
selected = {
|
selected = {
|
||||||
x: document.documentElement.scrollLeft + evt.clientX,
|
x: document.documentElement.scrollLeft + evt.clientX,
|
||||||
y: document.documentElement.scrollTop + evt.clientY,
|
y: document.documentElement.scrollTop + evt.clientY,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function moveHand(x, y, evt, isTouchEvent) {
|
function moveHand(x, y, evt, isTouchEvent) {
|
||||||
if (selected && !isTouchEvent) { //Let the browser handle touch to scroll
|
if (selected && !isTouchEvent) {
|
||||||
|
//Let the browser handle touch to scroll
|
||||||
window.scrollTo(selected.x - evt.clientX, selected.y - evt.clientY);
|
window.scrollTo(selected.x - evt.clientX, selected.y - evt.clientY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,7 +517,6 @@
|
||||||
else clickSelector(x, y, evt, isTouchEvent);
|
else clickSelector(x, y, evt, isTouchEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function move(x, y, evt, isTouchEvent) {
|
function move(x, y, evt, isTouchEvent) {
|
||||||
if (!handTool.secondary.active) moveHand(x, y, evt, isTouchEvent);
|
if (!handTool.secondary.active) moveHand(x, y, evt, isTouchEvent);
|
||||||
else moveSelector(x, y, evt, isTouchEvent);
|
else moveSelector(x, y, evt, isTouchEvent);
|
||||||
|
@ -474,14 +529,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteShortcut(e) {
|
function deleteShortcut(e) {
|
||||||
if (e.key == "Delete" &&
|
if (e.key == "Delete" && !e.target.matches("input[type=text], textarea"))
|
||||||
!e.target.matches("input[type=text], textarea"))
|
|
||||||
deleteSelection();
|
deleteSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
function duplicateShortcut(e) {
|
function duplicateShortcut(e) {
|
||||||
if (e.key == "d" &&
|
if (e.key == "d" && !e.target.matches("input[type=text], textarea"))
|
||||||
!e.target.matches("input[type=text], textarea"))
|
|
||||||
duplicateSelection();
|
duplicateSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,25 +553,26 @@
|
||||||
window.removeEventListener("keydown", duplicateShortcut);
|
window.removeEventListener("keydown", duplicateShortcut);
|
||||||
}
|
}
|
||||||
|
|
||||||
var handTool = { //The new tool
|
var handTool = {
|
||||||
"name": "Hand",
|
//The new tool
|
||||||
"shortcut": "h",
|
name: "Hand",
|
||||||
"listeners": {
|
shortcut: "h",
|
||||||
"press": press,
|
listeners: {
|
||||||
"move": move,
|
press: press,
|
||||||
"release": release,
|
move: move,
|
||||||
|
release: release,
|
||||||
},
|
},
|
||||||
"onquit": onquit,
|
onquit: onquit,
|
||||||
"secondary": {
|
secondary: {
|
||||||
"name": "Selector",
|
name: "Selector",
|
||||||
"icon": "tools/hand/selector.svg",
|
icon: "tools/hand/selector.svg",
|
||||||
"active": false,
|
active: false,
|
||||||
"switch": switchTool,
|
switch: switchTool,
|
||||||
},
|
},
|
||||||
"draw": draw,
|
draw: draw,
|
||||||
"icon": "tools/hand/hand.svg",
|
icon: "tools/hand/hand.svg",
|
||||||
"mouseCursor": "move",
|
mouseCursor: "move",
|
||||||
"showMarker": true,
|
showMarker: true,
|
||||||
};
|
};
|
||||||
Tools.add(handTool);
|
Tools.add(handTool);
|
||||||
Tools.change("Hand"); // Use the hand tool by default
|
Tools.change("Hand"); // Use the hand tool by default
|
||||||
|
|
|
@ -24,33 +24,33 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () { //Code isolation
|
(function () {
|
||||||
|
//Code isolation
|
||||||
//Indicates the id of the line the user is currently drawing or an empty string while the user is not drawing
|
//Indicates the id of the line the user is currently drawing or an empty string while the user is not drawing
|
||||||
var curLine = null,
|
var curLine = null,
|
||||||
lastTime = performance.now(); //The time at which the last point was drawn
|
lastTime = performance.now(); //The time at which the last point was drawn
|
||||||
|
|
||||||
//The data of the message that will be sent for every update
|
//The data of the message that will be sent for every update
|
||||||
function UpdateMessage(x, y) {
|
function UpdateMessage(x, y) {
|
||||||
this.type = 'update';
|
this.type = "update";
|
||||||
this.id = curLine.id;
|
this.id = curLine.id;
|
||||||
this.x2 = x;
|
this.x2 = x;
|
||||||
this.y2 = y;
|
this.y2 = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
function startLine(x, y, evt) {
|
function startLine(x, y, evt) {
|
||||||
|
|
||||||
//Prevent the press from being interpreted by the browser
|
//Prevent the press from being interpreted by the browser
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
||||||
curLine = {
|
curLine = {
|
||||||
'type': 'straight',
|
type: "straight",
|
||||||
'id': Tools.generateUID("s"), //"s" for straight line
|
id: Tools.generateUID("s"), //"s" for straight line
|
||||||
'color': Tools.getColor(),
|
color: Tools.getColor(),
|
||||||
'size': Tools.getSize(),
|
size: Tools.getSize(),
|
||||||
'opacity': Tools.getOpacity(),
|
opacity: Tools.getOpacity(),
|
||||||
'x': x,
|
x: x,
|
||||||
'y': y
|
y: y,
|
||||||
}
|
};
|
||||||
|
|
||||||
Tools.drawAndSend(curLine);
|
Tools.drawAndSend(curLine);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
if (lineTool.secondary.active) {
|
if (lineTool.secondary.active) {
|
||||||
var alpha = Math.atan2(y - curLine.y, x - curLine.x);
|
var alpha = Math.atan2(y - curLine.y, x - curLine.x);
|
||||||
var d = Math.hypot(y - curLine.y, x - curLine.x);
|
var d = Math.hypot(y - curLine.y, x - curLine.x);
|
||||||
var increment = 2 * Math.PI / 16;
|
var increment = (2 * Math.PI) / 16;
|
||||||
alpha = Math.round(alpha / increment) * increment;
|
alpha = Math.round(alpha / increment) * increment;
|
||||||
x = curLine.x + d * Math.cos(alpha);
|
x = curLine.x + d * Math.cos(alpha);
|
||||||
y = curLine.y + d * Math.sin(alpha);
|
y = curLine.y + d * Math.sin(alpha);
|
||||||
|
@ -89,19 +89,26 @@
|
||||||
createLine(data);
|
createLine(data);
|
||||||
break;
|
break;
|
||||||
case "update":
|
case "update":
|
||||||
var line = svg.getElementById(data['id']);
|
var line = svg.getElementById(data["id"]);
|
||||||
if (!line) {
|
if (!line) {
|
||||||
console.error("Straight line: Hmmm... I received a point of a line that has not been created (%s).", data['id']);
|
console.error(
|
||||||
createLine({ //create a new line in order not to loose the points
|
"Straight line: Hmmm... I received a point of a line that has not been created (%s).",
|
||||||
"id": data['id'],
|
data["id"],
|
||||||
"x": data['x2'],
|
);
|
||||||
"y": data['y2']
|
createLine({
|
||||||
|
//create a new line in order not to loose the points
|
||||||
|
id: data["id"],
|
||||||
|
x: data["x2"],
|
||||||
|
y: data["y2"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateLine(line, data);
|
updateLine(line, data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Straight Line: Draw instruction with unknown type. ", data);
|
console.error(
|
||||||
|
"Straight Line: Draw instruction with unknown type. ",
|
||||||
|
data,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,42 +116,46 @@
|
||||||
var svg = Tools.svg;
|
var svg = Tools.svg;
|
||||||
function createLine(lineData) {
|
function createLine(lineData) {
|
||||||
//Creates a new line on the canvas, or update a line that already exists with new information
|
//Creates a new line on the canvas, or update a line that already exists with new information
|
||||||
var line = svg.getElementById(lineData.id) || Tools.createSVGElement("line");
|
var line =
|
||||||
|
svg.getElementById(lineData.id) || Tools.createSVGElement("line");
|
||||||
line.id = lineData.id;
|
line.id = lineData.id;
|
||||||
line.x1.baseVal.value = lineData['x'];
|
line.x1.baseVal.value = lineData["x"];
|
||||||
line.y1.baseVal.value = lineData['y'];
|
line.y1.baseVal.value = lineData["y"];
|
||||||
line.x2.baseVal.value = lineData['x2'] || lineData['x'];
|
line.x2.baseVal.value = lineData["x2"] || lineData["x"];
|
||||||
line.y2.baseVal.value = lineData['y2'] || lineData['y'];
|
line.y2.baseVal.value = lineData["y2"] || lineData["y"];
|
||||||
//If some data is not provided, choose default value. The line may be updated later
|
//If some data is not provided, choose default value. The line may be updated later
|
||||||
line.setAttribute("stroke", lineData.color || "black");
|
line.setAttribute("stroke", lineData.color || "black");
|
||||||
line.setAttribute("stroke-width", lineData.size || 10);
|
line.setAttribute("stroke-width", lineData.size || 10);
|
||||||
line.setAttribute("opacity", Math.max(0.1, Math.min(1, lineData.opacity)) || 1);
|
line.setAttribute(
|
||||||
|
"opacity",
|
||||||
|
Math.max(0.1, Math.min(1, lineData.opacity)) || 1,
|
||||||
|
);
|
||||||
Tools.drawingArea.appendChild(line);
|
Tools.drawingArea.appendChild(line);
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLine(line, data) {
|
function updateLine(line, data) {
|
||||||
line.x2.baseVal.value = data['x2'];
|
line.x2.baseVal.value = data["x2"];
|
||||||
line.y2.baseVal.value = data['y2'];
|
line.y2.baseVal.value = data["y2"];
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineTool = {
|
var lineTool = {
|
||||||
"name": "Straight line",
|
name: "Straight line",
|
||||||
"shortcut": "l",
|
shortcut: "l",
|
||||||
"listeners": {
|
listeners: {
|
||||||
"press": startLine,
|
press: startLine,
|
||||||
"move": continueLine,
|
move: continueLine,
|
||||||
"release": stopLine,
|
release: stopLine,
|
||||||
},
|
},
|
||||||
"secondary": {
|
secondary: {
|
||||||
"name": "Straight line",
|
name: "Straight line",
|
||||||
"icon": "tools/line/icon-straight.svg",
|
icon: "tools/line/icon-straight.svg",
|
||||||
"active": false,
|
active: false,
|
||||||
},
|
},
|
||||||
"draw": draw,
|
draw: draw,
|
||||||
"mouseCursor": "crosshair",
|
mouseCursor: "crosshair",
|
||||||
"icon": "tools/line/icon.svg",
|
icon: "tools/line/icon.svg",
|
||||||
"stylesheet": "tools/line/line.css"
|
stylesheet: "tools/line/line.css",
|
||||||
};
|
};
|
||||||
Tools.add(lineTool);
|
Tools.add(lineTool);
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () { //Code isolation
|
(function () {
|
||||||
|
//Code isolation
|
||||||
|
|
||||||
// Allocate the full maximum server update rate to pencil messages.
|
// Allocate the full maximum server update rate to pencil messages.
|
||||||
// This feels a bit risky in terms of dropped messages, but any less
|
// This feels a bit risky in terms of dropped messages, but any less
|
||||||
|
@ -32,7 +33,9 @@
|
||||||
// seems to work, either because writing tends to happen in bursts, or
|
// seems to work, either because writing tends to happen in bursts, or
|
||||||
// maybe because the messages are sent when the time interval is *greater*
|
// maybe because the messages are sent when the time interval is *greater*
|
||||||
// than this?
|
// than this?
|
||||||
var MIN_PENCIL_INTERVAL_MS = Tools.server_config.MAX_EMIT_COUNT_PERIOD / Tools.server_config.MAX_EMIT_COUNT;
|
var MIN_PENCIL_INTERVAL_MS =
|
||||||
|
Tools.server_config.MAX_EMIT_COUNT_PERIOD /
|
||||||
|
Tools.server_config.MAX_EMIT_COUNT;
|
||||||
|
|
||||||
var AUTO_FINGER_WHITEOUT = Tools.server_config.AUTO_FINGER_WHITEOUT;
|
var AUTO_FINGER_WHITEOUT = Tools.server_config.AUTO_FINGER_WHITEOUT;
|
||||||
var hasUsedStylus = false;
|
var hasUsedStylus = false;
|
||||||
|
@ -43,7 +46,7 @@
|
||||||
|
|
||||||
//The data of the message that will be sent for every new point
|
//The data of the message that will be sent for every new point
|
||||||
function PointMessage(x, y) {
|
function PointMessage(x, y) {
|
||||||
this.type = 'child';
|
this.type = "child";
|
||||||
this.parent = curLineId;
|
this.parent = curLineId;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
@ -67,7 +70,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function startLine(x, y, evt) {
|
function startLine(x, y, evt) {
|
||||||
|
|
||||||
//Prevent the press from being interpreted by the browser
|
//Prevent the press from being interpreted by the browser
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
||||||
|
@ -76,11 +78,11 @@
|
||||||
curLineId = Tools.generateUID("l"); //"l" for line
|
curLineId = Tools.generateUID("l"); //"l" for line
|
||||||
|
|
||||||
Tools.drawAndSend({
|
Tools.drawAndSend({
|
||||||
'type': 'line',
|
type: "line",
|
||||||
'id': curLineId,
|
id: curLineId,
|
||||||
'color': (pencilTool.secondary.active ? "#ffffff" : Tools.getColor()),
|
color: pencilTool.secondary.active ? "#ffffff" : Tools.getColor(),
|
||||||
'size': Tools.getSize(),
|
size: Tools.getSize(),
|
||||||
'opacity': (pencilTool.secondary.active ? 1 : Tools.getOpacity()),
|
opacity: pencilTool.secondary.active ? 1 : Tools.getOpacity(),
|
||||||
});
|
});
|
||||||
|
|
||||||
//Immediatly add a point to the line
|
//Immediatly add a point to the line
|
||||||
|
@ -90,7 +92,10 @@
|
||||||
function continueLine(x, y, evt) {
|
function continueLine(x, y, evt) {
|
||||||
/*Wait 70ms before adding any point to the currently drawing line.
|
/*Wait 70ms before adding any point to the currently drawing line.
|
||||||
This allows the animation to be smother*/
|
This allows the animation to be smother*/
|
||||||
if (curLineId !== "" && performance.now() - lastTime > MIN_PENCIL_INTERVAL_MS) {
|
if (
|
||||||
|
curLineId !== "" &&
|
||||||
|
performance.now() - lastTime > MIN_PENCIL_INTERVAL_MS
|
||||||
|
) {
|
||||||
Tools.drawAndSend(new PointMessage(x, y));
|
Tools.drawAndSend(new PointMessage(x, y));
|
||||||
lastTime = performance.now();
|
lastTime = performance.now();
|
||||||
}
|
}
|
||||||
|
@ -115,10 +120,16 @@
|
||||||
renderingLine = createLine(data);
|
renderingLine = createLine(data);
|
||||||
break;
|
break;
|
||||||
case "child":
|
case "child":
|
||||||
var line = (renderingLine.id === data.parent) ? renderingLine : svg.getElementById(data.parent);
|
var line =
|
||||||
|
renderingLine.id === data.parent
|
||||||
|
? renderingLine
|
||||||
|
: svg.getElementById(data.parent);
|
||||||
if (!line) {
|
if (!line) {
|
||||||
console.error("Pencil: Hmmm... I received a point of a line that has not been created (%s).", data.parent);
|
console.error(
|
||||||
line = renderingLine = createLine({ "id": data.parent }); //create a new line in order not to loose the points
|
"Pencil: Hmmm... I received a point of a line that has not been created (%s).",
|
||||||
|
data.parent,
|
||||||
|
);
|
||||||
|
line = renderingLine = createLine({ id: data.parent }); //create a new line in order not to loose the points
|
||||||
}
|
}
|
||||||
addPoint(line, data.x, data.y);
|
addPoint(line, data.x, data.y);
|
||||||
break;
|
break;
|
||||||
|
@ -151,12 +162,16 @@
|
||||||
|
|
||||||
function createLine(lineData) {
|
function createLine(lineData) {
|
||||||
//Creates a new line on the canvas, or update a line that already exists with new information
|
//Creates a new line on the canvas, or update a line that already exists with new information
|
||||||
var line = svg.getElementById(lineData.id) || Tools.createSVGElement("path");
|
var line =
|
||||||
|
svg.getElementById(lineData.id) || Tools.createSVGElement("path");
|
||||||
line.id = lineData.id;
|
line.id = lineData.id;
|
||||||
//If some data is not provided, choose default value. The line may be updated later
|
//If some data is not provided, choose default value. The line may be updated later
|
||||||
line.setAttribute("stroke", lineData.color || "black");
|
line.setAttribute("stroke", lineData.color || "black");
|
||||||
line.setAttribute("stroke-width", lineData.size || 10);
|
line.setAttribute("stroke-width", lineData.size || 10);
|
||||||
line.setAttribute("opacity", Math.max(0.1, Math.min(1, lineData.opacity)) || 1);
|
line.setAttribute(
|
||||||
|
"opacity",
|
||||||
|
Math.max(0.1, Math.min(1, lineData.opacity)) || 1,
|
||||||
|
);
|
||||||
Tools.drawingArea.appendChild(line);
|
Tools.drawingArea.appendChild(line);
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
@ -189,43 +204,42 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var pencilTool = {
|
var pencilTool = {
|
||||||
"name": "Pencil",
|
name: "Pencil",
|
||||||
"shortcut": "p",
|
shortcut: "p",
|
||||||
"listeners": {
|
listeners: {
|
||||||
"press": startLine,
|
press: startLine,
|
||||||
"move": continueLine,
|
move: continueLine,
|
||||||
"release": stopLineAt,
|
release: stopLineAt,
|
||||||
},
|
},
|
||||||
"draw": draw,
|
draw: draw,
|
||||||
"onstart": function(oldTool) {
|
onstart: function (oldTool) {
|
||||||
//Reset stylus
|
//Reset stylus
|
||||||
hasUsedStylus = false;
|
hasUsedStylus = false;
|
||||||
},
|
},
|
||||||
"secondary": {
|
secondary: {
|
||||||
"name": "White-out",
|
name: "White-out",
|
||||||
"icon": "tools/pencil/whiteout_tape.svg",
|
icon: "tools/pencil/whiteout_tape.svg",
|
||||||
"active": false,
|
active: false,
|
||||||
"switch": function() {
|
switch: function () {
|
||||||
stopLine();
|
stopLine();
|
||||||
toggleSize();
|
toggleSize();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"onstart": function() {
|
onstart: function () {
|
||||||
//When switching from another tool to white-out, restore white-out size
|
//When switching from another tool to white-out, restore white-out size
|
||||||
if (pencilTool.secondary.active) {
|
if (pencilTool.secondary.active) {
|
||||||
restoreWhiteOutSize();
|
restoreWhiteOutSize();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"onquit": function() {
|
onquit: function () {
|
||||||
//When switching from white-out to another tool, restore drawing size
|
//When switching from white-out to another tool, restore drawing size
|
||||||
if (pencilTool.secondary.active) {
|
if (pencilTool.secondary.active) {
|
||||||
restoreDrawingSize();
|
restoreDrawingSize();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mouseCursor": "url('tools/pencil/cursor.svg'), crosshair",
|
mouseCursor: "url('tools/pencil/cursor.svg'), crosshair",
|
||||||
"icon": "tools/pencil/icon.svg",
|
icon: "tools/pencil/icon.svg",
|
||||||
"stylesheet": "tools/pencil/pencil.css",
|
stylesheet: "tools/pencil/pencil.css",
|
||||||
};
|
};
|
||||||
Tools.add(pencilTool);
|
Tools.add(pencilTool);
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
||||||
|
|
|
@ -40,9 +40,12 @@
|
||||||
case 2: //There are two points. The initial move and a line of zero length to make it visible
|
case 2: //There are two points. The initial move and a line of zero length to make it visible
|
||||||
//Draw a curve that is segment between the old point and the new one
|
//Draw a curve that is segment between the old point and the new one
|
||||||
npoint = new PathDataPoint("C", [
|
npoint = new PathDataPoint("C", [
|
||||||
pts[0].values[0], pts[0].values[1],
|
pts[0].values[0],
|
||||||
x, y,
|
pts[0].values[1],
|
||||||
x, y,
|
x,
|
||||||
|
y,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
default: //There are at least two points in the line
|
default: //There are at least two points in the line
|
||||||
|
@ -62,10 +65,9 @@
|
||||||
var ante_x = ante_values[ante_values.length - 2];
|
var ante_x = ante_values[ante_values.length - 2];
|
||||||
var ante_y = ante_values[ante_values.length - 1];
|
var ante_y = ante_values[ante_values.length - 1];
|
||||||
|
|
||||||
|
|
||||||
//We don't want to add the same point twice consecutively
|
//We don't want to add the same point twice consecutively
|
||||||
if ((prev_x === x && prev_y === y)
|
if ((prev_x === x && prev_y === y) || (ante_x === x && ante_y === y))
|
||||||
|| (ante_x === x && ante_y === y)) return;
|
return;
|
||||||
|
|
||||||
var vectx = x - ante_x,
|
var vectx = x - ante_x,
|
||||||
vecty = y - ante_y;
|
vecty = y - ante_y;
|
||||||
|
@ -82,12 +84,8 @@
|
||||||
prev_values[2] = cx1;
|
prev_values[2] = cx1;
|
||||||
prev_values[3] = cy1;
|
prev_values[3] = cy1;
|
||||||
|
|
||||||
return new PathDataPoint("C", [
|
return new PathDataPoint("C", [cx2, cy2, x, y, x, y]);
|
||||||
cx2, cy2,
|
|
||||||
x, y,
|
|
||||||
x, y,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
global["wboPencilPoint"] = wboPencilPoint;
|
global["wboPencilPoint"] = wboPencilPoint;
|
||||||
})("object" === typeof module && module.exports || window);
|
})(("object" === typeof module && module.exports) || window);
|
||||||
|
|
|
@ -24,37 +24,38 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () { //Code isolation
|
(function () {
|
||||||
|
//Code isolation
|
||||||
//Indicates the id of the shape the user is currently drawing or an empty string while the user is not drawing
|
//Indicates the id of the shape the user is currently drawing or an empty string while the user is not drawing
|
||||||
var end = false,
|
var end = false,
|
||||||
curId = "",
|
curId = "",
|
||||||
curUpdate = { //The data of the message that will be sent for every new point
|
curUpdate = {
|
||||||
'type': 'update',
|
//The data of the message that will be sent for every new point
|
||||||
'id': "",
|
type: "update",
|
||||||
'x': 0,
|
id: "",
|
||||||
'y': 0,
|
x: 0,
|
||||||
'x2': 0,
|
y: 0,
|
||||||
'y2': 0
|
x2: 0,
|
||||||
|
y2: 0,
|
||||||
},
|
},
|
||||||
lastTime = performance.now(); //The time at which the last point was drawn
|
lastTime = performance.now(); //The time at which the last point was drawn
|
||||||
|
|
||||||
function start(x, y, evt) {
|
function start(x, y, evt) {
|
||||||
|
|
||||||
//Prevent the press from being interpreted by the browser
|
//Prevent the press from being interpreted by the browser
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
||||||
curId = Tools.generateUID("r"); //"r" for rectangle
|
curId = Tools.generateUID("r"); //"r" for rectangle
|
||||||
|
|
||||||
Tools.drawAndSend({
|
Tools.drawAndSend({
|
||||||
'type': 'rect',
|
type: "rect",
|
||||||
'id': curId,
|
id: curId,
|
||||||
'color': Tools.getColor(),
|
color: Tools.getColor(),
|
||||||
'size': Tools.getSize(),
|
size: Tools.getSize(),
|
||||||
'opacity': Tools.getOpacity(),
|
opacity: Tools.getOpacity(),
|
||||||
'x': x,
|
x: x,
|
||||||
'y': y,
|
y: y,
|
||||||
'x2': x,
|
x2: x,
|
||||||
'y2': y
|
y2: y,
|
||||||
});
|
});
|
||||||
|
|
||||||
curUpdate.id = curId;
|
curUpdate.id = curId;
|
||||||
|
@ -73,7 +74,8 @@
|
||||||
x = curUpdate.x + (dx > 0 ? d : -d);
|
x = curUpdate.x + (dx > 0 ? d : -d);
|
||||||
y = curUpdate.y + (dy > 0 ? d : -d);
|
y = curUpdate.y + (dy > 0 ? d : -d);
|
||||||
}
|
}
|
||||||
curUpdate['x2'] = x; curUpdate['y2'] = y;
|
curUpdate["x2"] = x;
|
||||||
|
curUpdate["y2"] = y;
|
||||||
if (performance.now() - lastTime > 70 || end) {
|
if (performance.now() - lastTime > 70 || end) {
|
||||||
Tools.drawAndSend(curUpdate);
|
Tools.drawAndSend(curUpdate);
|
||||||
lastTime = performance.now();
|
lastTime = performance.now();
|
||||||
|
@ -99,19 +101,26 @@
|
||||||
createShape(data);
|
createShape(data);
|
||||||
break;
|
break;
|
||||||
case "update":
|
case "update":
|
||||||
var shape = svg.getElementById(data['id']);
|
var shape = svg.getElementById(data["id"]);
|
||||||
if (!shape) {
|
if (!shape) {
|
||||||
console.error("Straight shape: Hmmm... I received a point of a rect that has not been created (%s).", data['id']);
|
console.error(
|
||||||
createShape({ //create a new shape in order not to loose the points
|
"Straight shape: Hmmm... I received a point of a rect that has not been created (%s).",
|
||||||
"id": data['id'],
|
data["id"],
|
||||||
"x": data['x2'],
|
);
|
||||||
"y": data['y2']
|
createShape({
|
||||||
|
//create a new shape in order not to loose the points
|
||||||
|
id: data["id"],
|
||||||
|
x: data["x2"],
|
||||||
|
y: data["y2"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateShape(shape, data);
|
updateShape(shape, data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error("Straight shape: Draw instruction with unknown type. ", data);
|
console.error(
|
||||||
|
"Straight shape: Draw instruction with unknown type. ",
|
||||||
|
data,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,36 +134,38 @@
|
||||||
//If some data is not provided, choose default value. The shape may be updated later
|
//If some data is not provided, choose default value. The shape may be updated later
|
||||||
shape.setAttribute("stroke", data.color || "black");
|
shape.setAttribute("stroke", data.color || "black");
|
||||||
shape.setAttribute("stroke-width", data.size || 10);
|
shape.setAttribute("stroke-width", data.size || 10);
|
||||||
shape.setAttribute("opacity", Math.max(0.1, Math.min(1, data.opacity)) || 1);
|
shape.setAttribute(
|
||||||
|
"opacity",
|
||||||
|
Math.max(0.1, Math.min(1, data.opacity)) || 1,
|
||||||
|
);
|
||||||
Tools.drawingArea.appendChild(shape);
|
Tools.drawingArea.appendChild(shape);
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateShape(shape, data) {
|
function updateShape(shape, data) {
|
||||||
shape.x.baseVal.value = Math.min(data['x2'], data['x']);
|
shape.x.baseVal.value = Math.min(data["x2"], data["x"]);
|
||||||
shape.y.baseVal.value = Math.min(data['y2'], data['y']);
|
shape.y.baseVal.value = Math.min(data["y2"], data["y"]);
|
||||||
shape.width.baseVal.value = Math.abs(data['x2'] - data['x']);
|
shape.width.baseVal.value = Math.abs(data["x2"] - data["x"]);
|
||||||
shape.height.baseVal.value = Math.abs(data['y2'] - data['y']);
|
shape.height.baseVal.value = Math.abs(data["y2"] - data["y"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rectangleTool = {
|
var rectangleTool = {
|
||||||
"name": "Rectangle",
|
name: "Rectangle",
|
||||||
"shortcut": "r",
|
shortcut: "r",
|
||||||
"listeners": {
|
listeners: {
|
||||||
"press": start,
|
press: start,
|
||||||
"move": move,
|
move: move,
|
||||||
"release": stop,
|
release: stop,
|
||||||
},
|
},
|
||||||
"secondary": {
|
secondary: {
|
||||||
"name": "Square",
|
name: "Square",
|
||||||
"icon": "tools/rect/icon-square.svg",
|
icon: "tools/rect/icon-square.svg",
|
||||||
"active": false,
|
active: false,
|
||||||
},
|
},
|
||||||
"draw": draw,
|
draw: draw,
|
||||||
"mouseCursor": "crosshair",
|
mouseCursor: "crosshair",
|
||||||
"icon": "tools/rect/icon.svg",
|
icon: "tools/rect/icon.svg",
|
||||||
"stylesheet": "tools/rect/rect.css"
|
stylesheet: "tools/rect/rect.css",
|
||||||
};
|
};
|
||||||
Tools.add(rectangleTool);
|
Tools.add(rectangleTool);
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
#textToolInput {
|
#textToolInput {
|
||||||
position:fixed;
|
position: fixed;
|
||||||
top:-1000px; /*Hidden*/
|
top: -1000px; /*Hidden*/
|
||||||
left: 80px;
|
left: 80px;
|
||||||
width:500px;
|
width: 500px;
|
||||||
}
|
}
|
||||||
#textToolInput:focus {
|
#textToolInput:focus {
|
||||||
top: 5px;
|
top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
text {
|
text {
|
||||||
font-family:"Arial", "Helvetica", sans-serif;
|
font-family: "Arial", "Helvetica", sans-serif;
|
||||||
user-select:none;
|
user-select: none;
|
||||||
-moz-user-select:none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () { //Code isolation
|
(function () {
|
||||||
|
//Code isolation
|
||||||
var board = Tools.board;
|
var board = Tools.board;
|
||||||
|
|
||||||
var input = document.createElement("input");
|
var input = document.createElement("input");
|
||||||
|
@ -33,21 +34,20 @@
|
||||||
input.setAttribute("autocomplete", "off");
|
input.setAttribute("autocomplete", "off");
|
||||||
|
|
||||||
var curText = {
|
var curText = {
|
||||||
"x": 0,
|
x: 0,
|
||||||
"y": 0,
|
y: 0,
|
||||||
"size": 36,
|
size: 36,
|
||||||
"rawSize": 16,
|
rawSize: 16,
|
||||||
"oldSize": 0,
|
oldSize: 0,
|
||||||
"opacity": 1,
|
opacity: 1,
|
||||||
"color": "#000",
|
color: "#000",
|
||||||
"id": 0,
|
id: 0,
|
||||||
"sentText": "",
|
sentText: "",
|
||||||
"lastSending": 0
|
lastSending: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
var active = false;
|
var active = false;
|
||||||
|
|
||||||
|
|
||||||
function onStart() {
|
function onStart() {
|
||||||
curText.oldSize = Tools.getSize();
|
curText.oldSize = Tools.getSize();
|
||||||
Tools.setSize(curText.rawSize);
|
Tools.setSize(curText.rawSize);
|
||||||
|
@ -82,7 +82,8 @@
|
||||||
curText.id = elem.id;
|
curText.id = elem.id;
|
||||||
var r = elem.getBoundingClientRect();
|
var r = elem.getBoundingClientRect();
|
||||||
var x = (r.left + document.documentElement.scrollLeft) / Tools.scale;
|
var x = (r.left + document.documentElement.scrollLeft) / Tools.scale;
|
||||||
var y = (r.top + r.height + document.documentElement.scrollTop) / Tools.scale;
|
var y =
|
||||||
|
(r.top + r.height + document.documentElement.scrollTop) / Tools.scale;
|
||||||
|
|
||||||
curText.x = x;
|
curText.x = x;
|
||||||
curText.y = y;
|
curText.y = y;
|
||||||
|
@ -98,15 +99,19 @@
|
||||||
active = true;
|
active = true;
|
||||||
if (!input.parentNode) board.appendChild(input);
|
if (!input.parentNode) board.appendChild(input);
|
||||||
input.value = "";
|
input.value = "";
|
||||||
var left = curText.x - document.documentElement.scrollLeft + 'px';
|
var left = curText.x - document.documentElement.scrollLeft + "px";
|
||||||
var clientW = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
|
var clientW = Math.max(
|
||||||
|
document.documentElement.clientWidth,
|
||||||
|
window.innerWidth || 0,
|
||||||
|
);
|
||||||
var x = curText.x * Tools.scale - document.documentElement.scrollLeft;
|
var x = curText.x * Tools.scale - document.documentElement.scrollLeft;
|
||||||
if (x + 250 > clientW) {
|
if (x + 250 > clientW) {
|
||||||
x = Math.max(60, clientW - 260)
|
x = Math.max(60, clientW - 260);
|
||||||
}
|
}
|
||||||
|
|
||||||
input.style.left = x + 'px';
|
input.style.left = x + "px";
|
||||||
input.style.top = curText.y * Tools.scale - document.documentElement.scrollTop + 20 + 'px';
|
input.style.top =
|
||||||
|
curText.y * Tools.scale - document.documentElement.scrollTop + 20 + "px";
|
||||||
input.focus();
|
input.focus();
|
||||||
input.addEventListener("keyup", textChangeHandler);
|
input.addEventListener("keyup", textChangeHandler);
|
||||||
input.addEventListener("blur", textChangeHandler);
|
input.addEventListener("blur", textChangeHandler);
|
||||||
|
@ -114,7 +119,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopEdit() {
|
function stopEdit() {
|
||||||
try { input.blur(); } catch (e) { /* Internet Explorer */ }
|
try {
|
||||||
|
input.blur();
|
||||||
|
} catch (e) {
|
||||||
|
/* Internet Explorer */
|
||||||
|
}
|
||||||
active = false;
|
active = false;
|
||||||
blur();
|
blur();
|
||||||
curText.id = 0;
|
curText.id = 0;
|
||||||
|
@ -125,15 +134,17 @@
|
||||||
|
|
||||||
function blur() {
|
function blur() {
|
||||||
if (active) return;
|
if (active) return;
|
||||||
input.style.top = '-1000px';
|
input.style.top = "-1000px";
|
||||||
}
|
}
|
||||||
|
|
||||||
function textChangeHandler(evt) {
|
function textChangeHandler(evt) {
|
||||||
if (evt.which === 13) { // enter
|
if (evt.which === 13) {
|
||||||
|
// enter
|
||||||
curText.y += 1.5 * curText.size;
|
curText.y += 1.5 * curText.size;
|
||||||
stopEdit();
|
stopEdit();
|
||||||
startEdit();
|
startEdit();
|
||||||
} else if (evt.which === 27) { // escape
|
} else if (evt.which === 27) {
|
||||||
|
// escape
|
||||||
stopEdit();
|
stopEdit();
|
||||||
}
|
}
|
||||||
if (performance.now() - curText.lastSending > 100) {
|
if (performance.now() - curText.lastSending > 100) {
|
||||||
|
@ -142,19 +153,19 @@
|
||||||
if (curText.id === 0) {
|
if (curText.id === 0) {
|
||||||
curText.id = Tools.generateUID("t"); //"t" for text
|
curText.id = Tools.generateUID("t"); //"t" for text
|
||||||
Tools.drawAndSend({
|
Tools.drawAndSend({
|
||||||
'type': 'new',
|
type: "new",
|
||||||
'id': curText.id,
|
id: curText.id,
|
||||||
'color': curText.color,
|
color: curText.color,
|
||||||
'size': curText.size,
|
size: curText.size,
|
||||||
'opacity': curText.opacity,
|
opacity: curText.opacity,
|
||||||
'x': curText.x,
|
x: curText.x,
|
||||||
'y': curText.y
|
y: curText.y,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
Tools.drawAndSend({
|
Tools.drawAndSend({
|
||||||
'type': "update",
|
type: "update",
|
||||||
'id': curText.id,
|
id: curText.id,
|
||||||
'txt': input.value.slice(0, 280)
|
txt: input.value.slice(0, 280),
|
||||||
});
|
});
|
||||||
curText.sentText = input.value;
|
curText.sentText = input.value;
|
||||||
curText.lastSending = performance.now();
|
curText.lastSending = performance.now();
|
||||||
|
@ -174,7 +185,9 @@
|
||||||
case "update":
|
case "update":
|
||||||
var textField = document.getElementById(data.id);
|
var textField = document.getElementById(data.id);
|
||||||
if (textField === null) {
|
if (textField === null) {
|
||||||
console.error("Text: Hmmm... I received text that belongs to an unknown text field");
|
console.error(
|
||||||
|
"Text: Hmmm... I received text that belongs to an unknown text field",
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
updateText(textField, data.txt);
|
updateText(textField, data.txt);
|
||||||
|
@ -196,24 +209,27 @@
|
||||||
elem.setAttribute("y", fieldData.y);
|
elem.setAttribute("y", fieldData.y);
|
||||||
elem.setAttribute("font-size", fieldData.size);
|
elem.setAttribute("font-size", fieldData.size);
|
||||||
elem.setAttribute("fill", fieldData.color);
|
elem.setAttribute("fill", fieldData.color);
|
||||||
elem.setAttribute("opacity", Math.max(0.1, Math.min(1, fieldData.opacity)) || 1);
|
elem.setAttribute(
|
||||||
|
"opacity",
|
||||||
|
Math.max(0.1, Math.min(1, fieldData.opacity)) || 1,
|
||||||
|
);
|
||||||
if (fieldData.txt) elem.textContent = fieldData.txt;
|
if (fieldData.txt) elem.textContent = fieldData.txt;
|
||||||
Tools.drawingArea.appendChild(elem);
|
Tools.drawingArea.appendChild(elem);
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools.add({ //The new tool
|
Tools.add({
|
||||||
"name": "Text",
|
//The new tool
|
||||||
"shortcut": "t",
|
name: "Text",
|
||||||
"listeners": {
|
shortcut: "t",
|
||||||
"press": clickHandler,
|
listeners: {
|
||||||
|
press: clickHandler,
|
||||||
},
|
},
|
||||||
"onstart": onStart,
|
onstart: onStart,
|
||||||
"onquit": onQuit,
|
onquit: onQuit,
|
||||||
"draw": draw,
|
draw: draw,
|
||||||
"stylesheet": "tools/text/text.css",
|
stylesheet: "tools/text/text.css",
|
||||||
"icon": "tools/text/icon.svg",
|
icon: "tools/text/icon.svg",
|
||||||
"mouseCursor": "text"
|
mouseCursor: "text",
|
||||||
});
|
});
|
||||||
|
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
||||||
|
|
|
@ -24,24 +24,26 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () { //Code isolation
|
(function () {
|
||||||
var ZOOM_FACTOR = .5;
|
//Code isolation
|
||||||
|
var ZOOM_FACTOR = 0.5;
|
||||||
var origin = {
|
var origin = {
|
||||||
scrollX: document.documentElement.scrollLeft,
|
scrollX: document.documentElement.scrollLeft,
|
||||||
scrollY: document.documentElement.scrollTop,
|
scrollY: document.documentElement.scrollTop,
|
||||||
x: 0.0,
|
x: 0.0,
|
||||||
y: 0.0,
|
y: 0.0,
|
||||||
clientY: 0,
|
clientY: 0,
|
||||||
scale: 1.0
|
scale: 1.0,
|
||||||
};
|
};
|
||||||
var moved = false, pressed = false;
|
var moved = false,
|
||||||
|
pressed = false;
|
||||||
|
|
||||||
function zoom(origin, scale) {
|
function zoom(origin, scale) {
|
||||||
var oldScale = origin.scale;
|
var oldScale = origin.scale;
|
||||||
var newScale = Tools.setScale(scale);
|
var newScale = Tools.setScale(scale);
|
||||||
window.scrollTo(
|
window.scrollTo(
|
||||||
origin.scrollX + origin.x * (newScale - oldScale),
|
origin.scrollX + origin.x * (newScale - oldScale),
|
||||||
origin.scrollY + origin.y * (newScale - oldScale)
|
origin.scrollY + origin.y * (newScale - oldScale),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
var delta = getClientY(evt, isTouchEvent) - origin.clientY;
|
var delta = getClientY(evt, isTouchEvent) - origin.clientY;
|
||||||
var scale = origin.scale * (1 + delta * ZOOM_FACTOR / 100);
|
var scale = origin.scale * (1 + (delta * ZOOM_FACTOR) / 100);
|
||||||
if (Math.abs(delta) > 1) moved = true;
|
if (Math.abs(delta) > 1) moved = true;
|
||||||
animation = animate(scale);
|
animation = animate(scale);
|
||||||
}
|
}
|
||||||
|
@ -82,10 +84,13 @@
|
||||||
function onwheel(evt) {
|
function onwheel(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
var multiplier =
|
var multiplier =
|
||||||
(evt.deltaMode === WheelEvent.DOM_DELTA_LINE) ? 30 :
|
evt.deltaMode === WheelEvent.DOM_DELTA_LINE
|
||||||
(evt.deltaMode === WheelEvent.DOM_DELTA_PAGE) ? 1000 :
|
? 30
|
||||||
1;
|
: evt.deltaMode === WheelEvent.DOM_DELTA_PAGE
|
||||||
var deltaX = evt.deltaX * multiplier, deltaY = evt.deltaY * multiplier;
|
? 1000
|
||||||
|
: 1;
|
||||||
|
var deltaX = evt.deltaX * multiplier,
|
||||||
|
deltaY = evt.deltaY * multiplier;
|
||||||
if (!evt.ctrlKey) {
|
if (!evt.ctrlKey) {
|
||||||
// zoom
|
// zoom
|
||||||
var scale = Tools.getScale();
|
var scale = Tools.getScale();
|
||||||
|
@ -97,23 +102,33 @@
|
||||||
// make finer changes if shift is being held
|
// make finer changes if shift is being held
|
||||||
var change = evt.shiftKey ? 1 : 5;
|
var change = evt.shiftKey ? 1 : 5;
|
||||||
// change tool size
|
// change tool size
|
||||||
Tools.setSize(Tools.getSize() - deltaY / 100 * change);
|
Tools.setSize(Tools.getSize() - (deltaY / 100) * change);
|
||||||
} else if (evt.shiftKey) {
|
} else if (evt.shiftKey) {
|
||||||
// scroll horizontally
|
// scroll horizontally
|
||||||
window.scrollTo(document.documentElement.scrollLeft + deltaY, document.documentElement.scrollTop + deltaX);
|
window.scrollTo(
|
||||||
|
document.documentElement.scrollLeft + deltaY,
|
||||||
|
document.documentElement.scrollTop + deltaX,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// regular scrolling
|
// regular scrolling
|
||||||
window.scrollTo(document.documentElement.scrollLeft + deltaX, document.documentElement.scrollTop + deltaY);
|
window.scrollTo(
|
||||||
|
document.documentElement.scrollLeft + deltaX,
|
||||||
|
document.documentElement.scrollTop + deltaY,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Tools.board.addEventListener("wheel", onwheel, { passive: false });
|
Tools.board.addEventListener("wheel", onwheel, { passive: false });
|
||||||
|
|
||||||
Tools.board.addEventListener("touchmove", function ontouchmove(evt) {
|
Tools.board.addEventListener(
|
||||||
|
"touchmove",
|
||||||
|
function ontouchmove(evt) {
|
||||||
// 2-finger pan to zoom
|
// 2-finger pan to zoom
|
||||||
var touches = evt.touches;
|
var touches = evt.touches;
|
||||||
if (touches.length === 2) {
|
if (touches.length === 2) {
|
||||||
var x0 = touches[0].clientX, x1 = touches[1].clientX,
|
var x0 = touches[0].clientX,
|
||||||
y0 = touches[0].clientY, y1 = touches[1].clientY,
|
x1 = touches[1].clientX,
|
||||||
|
y0 = touches[0].clientY,
|
||||||
|
y1 = touches[1].clientY,
|
||||||
dx = x0 - x1,
|
dx = x0 - x1,
|
||||||
dy = y0 - y1;
|
dy = y0 - y1;
|
||||||
var x = (touches[0].pageX + touches[1].pageX) / 2 / Tools.getScale(),
|
var x = (touches[0].pageX + touches[1].pageX) / 2 / Tools.getScale(),
|
||||||
|
@ -125,11 +140,13 @@
|
||||||
origin.distance = distance;
|
origin.distance = distance;
|
||||||
} else {
|
} else {
|
||||||
var delta = distance - origin.distance;
|
var delta = distance - origin.distance;
|
||||||
var scale = origin.scale * (1 + delta * ZOOM_FACTOR / 100);
|
var scale = origin.scale * (1 + (delta * ZOOM_FACTOR) / 100);
|
||||||
animate(scale);
|
animate(scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { passive: true });
|
},
|
||||||
|
{ passive: true },
|
||||||
|
);
|
||||||
function touchend() {
|
function touchend() {
|
||||||
pressed = false;
|
pressed = false;
|
||||||
}
|
}
|
||||||
|
@ -138,7 +155,7 @@
|
||||||
|
|
||||||
function release(x, y, evt, isTouchEvent) {
|
function release(x, y, evt, isTouchEvent) {
|
||||||
if (pressed && !moved) {
|
if (pressed && !moved) {
|
||||||
var delta = (evt.shiftKey === true) ? -1 : 1;
|
var delta = evt.shiftKey === true ? -1 : 1;
|
||||||
var scale = Tools.getScale() * (1 + delta * ZOOM_FACTOR);
|
var scale = Tools.getScale() * (1 + delta * ZOOM_FACTOR);
|
||||||
zoom(origin, scale);
|
zoom(origin, scale);
|
||||||
}
|
}
|
||||||
|
@ -150,7 +167,7 @@
|
||||||
if (evt.key === "Shift") {
|
if (evt.key === "Shift") {
|
||||||
Tools.svg.style.cursor = "zoom-" + (down ? "out" : "in");
|
Tools.svg.style.cursor = "zoom-" + (down ? "out" : "in");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClientY(evt, isTouchEvent) {
|
function getClientY(evt, isTouchEvent) {
|
||||||
|
@ -170,19 +187,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var zoomTool = {
|
var zoomTool = {
|
||||||
"name": "Zoom",
|
name: "Zoom",
|
||||||
"shortcut": "z",
|
shortcut: "z",
|
||||||
"listeners": {
|
listeners: {
|
||||||
"press": press,
|
press: press,
|
||||||
"move": move,
|
move: move,
|
||||||
"release": release,
|
release: release,
|
||||||
},
|
},
|
||||||
"onstart": onstart,
|
onstart: onstart,
|
||||||
"onquit": onquit,
|
onquit: onquit,
|
||||||
"mouseCursor": "zoom-in",
|
mouseCursor: "zoom-in",
|
||||||
"icon": "tools/zoom/icon.svg",
|
icon: "tools/zoom/icon.svg",
|
||||||
"helpText": "click_to_zoom",
|
helpText: "click_to_zoom",
|
||||||
"showMarker": true,
|
showMarker: true,
|
||||||
};
|
};
|
||||||
Tools.add(zoomTool);
|
Tools.add(zoomTool);
|
||||||
})(); //End of code isolation
|
})(); //End of code isolation
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
version: '3'
|
version: "3"
|
||||||
services:
|
services:
|
||||||
www:
|
www:
|
||||||
volumes:
|
volumes:
|
||||||
- /opt/app/server-data:/opt/app/server-data
|
- /opt/app/server-data:/opt/app/server-data
|
||||||
ports:
|
ports:
|
||||||
- '80:80'
|
- "80:80"
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
@ -14,4 +14,3 @@ services:
|
||||||
delay: 5s
|
delay: 5s
|
||||||
max_attempts: 5
|
max_attempts: 5
|
||||||
window: 60s
|
window: 60s
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
// Autogenerated by Nightwatch
|
// Autogenerated by Nightwatch
|
||||||
// Refer to the online docs for more details: https://nightwatchjs.org/gettingstarted/configuration/
|
// Refer to the online docs for more details: https://nightwatchjs.org/gettingstarted/configuration/
|
||||||
const Services = {}; loadServices();
|
const Services = {};
|
||||||
|
loadServices();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"src_folders": ["tests"],
|
src_folders: ["tests"],
|
||||||
|
|
||||||
"webdriver": {
|
webdriver: {
|
||||||
"start_process": true,
|
start_process: true,
|
||||||
"server_path": "./node_modules/.bin/geckodriver",
|
server_path: "./node_modules/.bin/geckodriver",
|
||||||
"cli_args": [
|
cli_args: ["--log", "debug"],
|
||||||
"--log", "debug"
|
port: 4444,
|
||||||
],
|
|
||||||
"port": 4444
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"test_settings": {
|
test_settings: {
|
||||||
"default": {
|
default: {
|
||||||
"desiredCapabilities": {
|
desiredCapabilities: {
|
||||||
"browserName": "firefox",
|
browserName: "firefox",
|
||||||
"acceptInsecureCerts": true,
|
acceptInsecureCerts: true,
|
||||||
"alwaysMatch": {
|
alwaysMatch: {
|
||||||
"moz:firefoxOptions": {
|
"moz:firefoxOptions": {
|
||||||
"args": ["-headless"]
|
args: ["-headless"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
jwt: {
|
||||||
|
globals: {
|
||||||
|
token:
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.5mhBHqs5_DTLdINd9p5m7ZJ6XD0Xc55kIaCRY5r6HRA",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"jwt": {
|
|
||||||
"globals": {
|
|
||||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.5mhBHqs5_DTLdINd9p5m7ZJ6XD0Xc55kIaCRY5r6HRA"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadServices() {
|
function loadServices() {
|
||||||
try {
|
try {
|
||||||
Services.seleniumServer = require('selenium-server');
|
Services.seleniumServer = require("selenium-server");
|
||||||
} catch (err) { }
|
} catch (err) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Services.chromedriver = require('chromedriver');
|
Services.chromedriver = require("chromedriver");
|
||||||
} catch (err) { }
|
} catch (err) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Services.geckodriver = require('geckodriver');
|
Services.geckodriver = require("geckodriver");
|
||||||
} catch (err) { }
|
} catch (err) {}
|
||||||
}
|
}
|
||||||
|
|
24
package-lock.json
generated
24
package-lock.json
generated
|
@ -21,7 +21,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"geckodriver": "^4.2.1",
|
"geckodriver": "^4.2.1",
|
||||||
"nightwatch": "^3.2.1"
|
"nightwatch": "^3.2.1",
|
||||||
|
"prettier": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@assemblyscript/loader": {
|
"node_modules/@assemblyscript/loader": {
|
||||||
|
@ -3959,6 +3960,21 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process-nextick-args": {
|
"node_modules/process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
@ -8397,6 +8413,12 @@
|
||||||
"toposort": "^2.0.2"
|
"toposort": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"process-nextick-args": {
|
"process-nextick-args": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
|
|
@ -20,7 +20,9 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./server/server.js",
|
"start": "node ./server/server.js",
|
||||||
"test": "nightwatch tests && nightwatch tests --env jwt"
|
"test": "nightwatch tests && nightwatch tests --env jwt",
|
||||||
|
"prettier": "prettier . --write",
|
||||||
|
"prettier-check": "prettier . --check"
|
||||||
},
|
},
|
||||||
"main": "./server/server.js",
|
"main": "./server/server.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -29,6 +31,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"geckodriver": "^4.2.1",
|
"geckodriver": "^4.2.1",
|
||||||
"nightwatch": "^3.2.1"
|
"nightwatch": "^3.2.1",
|
||||||
|
"prettier": "^3.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ class BoardData {
|
||||||
this.board = {};
|
this.board = {};
|
||||||
this.file = path.join(
|
this.file = path.join(
|
||||||
config.HISTORY_DIR,
|
config.HISTORY_DIR,
|
||||||
"board-" + encodeURIComponent(name) + ".json"
|
"board-" + encodeURIComponent(name) + ".json",
|
||||||
);
|
);
|
||||||
this.assetsDir = path.join(
|
this.assetsDir = path.join(
|
||||||
config.HISTORY_DIR,
|
config.HISTORY_DIR,
|
||||||
|
@ -202,7 +202,7 @@ class BoardData {
|
||||||
this.addChild(message.parent, message);
|
this.addChild(message.parent, message);
|
||||||
break;
|
break;
|
||||||
case "clear":
|
case "clear":
|
||||||
if(jwtauth.roleInBoard(message.token,message.board) === 'moderator') {
|
if (jwtauth.roleInBoard(message.token, message.board) === "moderator") {
|
||||||
this.clear();
|
this.clear();
|
||||||
} else {
|
} else {
|
||||||
throw new Error("User is not a moderator");
|
throw new Error("User is not a moderator");
|
||||||
|
|
|
@ -24,7 +24,8 @@ async function get_error(directory) {
|
||||||
let err_msg = "does not allow file creation and deletion. ";
|
let err_msg = "does not allow file creation and deletion. ";
|
||||||
try {
|
try {
|
||||||
const { uid, gid } = os.userInfo();
|
const { uid, gid } = os.userInfo();
|
||||||
err_msg += "Check the permissions of the directory, and if needed change them so that " +
|
err_msg +=
|
||||||
|
"Check the permissions of the directory, and if needed change them so that " +
|
||||||
`user with UID ${uid} has access to them. This can be achieved by running the command: chown ${uid}:${gid} on the directory`;
|
`user with UID ${uid} has access to them. This can be achieved by running the command: chown ${uid}:${gid} on the directory`;
|
||||||
} finally {
|
} finally {
|
||||||
return err_msg;
|
return err_msg;
|
||||||
|
@ -40,7 +41,7 @@ async function get_error(directory) {
|
||||||
fileChecks.push(
|
fileChecks.push(
|
||||||
fs.promises.access(elemPath, R_OK | W_OK).catch(function () {
|
fs.promises.access(elemPath, R_OK | W_OK).catch(function () {
|
||||||
return elemPath;
|
return elemPath;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +68,7 @@ function check_output_directory(directory) {
|
||||||
console.error(
|
console.error(
|
||||||
`The configured history directory in which boards are stored ${error}.` +
|
`The configured history directory in which boards are stored ${error}.` +
|
||||||
`\nThe history directory can be configured with the environment variable WBO_HISTORY_DIR. ` +
|
`\nThe history directory can be configured with the environment variable WBO_HISTORY_DIR. ` +
|
||||||
`It is currently set to "${directory}".`
|
`It is currently set to "${directory}".`,
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,9 @@ module.exports = {
|
||||||
BLOCKED_TOOLS: (process.env["WBO_BLOCKED_TOOLS"] || "").split(","),
|
BLOCKED_TOOLS: (process.env["WBO_BLOCKED_TOOLS"] || "").split(","),
|
||||||
|
|
||||||
/** Selection Buttons. A comma-separated list of selection buttons that should not be available. */
|
/** Selection Buttons. A comma-separated list of selection buttons that should not be available. */
|
||||||
BLOCKED_SELECTION_BUTTONS: (process.env["WBO_BLOCKED_SELECTION_BUTTONS"] || "").split(","),
|
BLOCKED_SELECTION_BUTTONS: (
|
||||||
|
process.env["WBO_BLOCKED_SELECTION_BUTTONS"] || ""
|
||||||
|
).split(","),
|
||||||
|
|
||||||
/** Automatically switch to White-out on finger touch after drawing
|
/** Automatically switch to White-out on finger touch after drawing
|
||||||
with Pencil using a stylus. Only supported on iPad with Apple Pencil. */
|
with Pencil using a stylus. Only supported on iPad with Apple Pencil. */
|
||||||
|
@ -59,9 +61,8 @@ module.exports = {
|
||||||
STATSD_URL: process.env["STATSD_URL"],
|
STATSD_URL: process.env["STATSD_URL"],
|
||||||
|
|
||||||
/** Secret key for jwt */
|
/** Secret key for jwt */
|
||||||
AUTH_SECRET_KEY: (process.env["AUTH_SECRET_KEY"] || ""),
|
AUTH_SECRET_KEY: process.env["AUTH_SECRET_KEY"] || "",
|
||||||
|
|
||||||
/** If this variable is set, automatically redirect to this board from the root of the application. */
|
/** If this variable is set, automatically redirect to this board from the root of the application. */
|
||||||
DEFAULT_BOARD: (process.env["WBO_DEFAULT_BOARD"]),
|
DEFAULT_BOARD: process.env["WBO_DEFAULT_BOARD"],
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const fs = require("./fs_promises.js"),
|
const fs = require("./fs_promises.js"),
|
||||||
path = require("path"),
|
path = require("path"),
|
||||||
wboPencilPoint = require("../client-data/tools/pencil/wbo_pencil_point.js")
|
wboPencilPoint =
|
||||||
.wboPencilPoint;
|
require("../client-data/tools/pencil/wbo_pencil_point.js").wboPencilPoint;
|
||||||
|
|
||||||
function htmlspecialchars(str) {
|
function htmlspecialchars(str) {
|
||||||
if (typeof str !== "string") return "";
|
if (typeof str !== "string") return "";
|
||||||
|
@ -181,7 +181,7 @@ async function toSVG(obj, writeable) {
|
||||||
Math.max((elem.y + margin + (elem.deltay | 0)) | 0, dim[1]),
|
Math.max((elem.y + margin + (elem.deltay | 0)) | 0, dim[1]),
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[margin, margin]
|
[margin, margin],
|
||||||
);
|
);
|
||||||
writeable.write(
|
writeable.write(
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" ' +
|
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" ' +
|
||||||
|
@ -194,7 +194,7 @@ async function toSVG(obj, writeable) {
|
||||||
'text {font-family:"Arial"}' +
|
'text {font-family:"Arial"}' +
|
||||||
"path {fill:none;stroke-linecap:round;stroke-linejoin:round;}" +
|
"path {fill:none;stroke-linecap:round;stroke-linejoin:round;}" +
|
||||||
"rect {fill:none}" +
|
"rect {fill:none}" +
|
||||||
"]]></style></defs>"
|
"]]></style></defs>",
|
||||||
);
|
);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
elems.map(async function (elem) {
|
elems.map(async function (elem) {
|
||||||
|
@ -202,7 +202,7 @@ async function toSVG(obj, writeable) {
|
||||||
const renderFun = Tools[elem.tool];
|
const renderFun = Tools[elem.tool];
|
||||||
if (renderFun) writeable.write(renderFun(elem));
|
if (renderFun) writeable.write(renderFun(elem));
|
||||||
else console.warn("Missing render function for tool", elem.tool);
|
else console.warn("Missing render function for tool", elem.tool);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
writeable.write("</svg>");
|
writeable.write("</svg>");
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
config = require("./configuration.js"),
|
(config = require("./configuration.js")),
|
||||||
jsonwebtoken = require("jsonwebtoken");
|
(jsonwebtoken = require("jsonwebtoken"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function checks if a board name is set in the roles claim.
|
* This function checks if a board name is set in the roles claim.
|
||||||
|
@ -38,14 +38,14 @@ config = require("./configuration.js"),
|
||||||
|
|
||||||
function checkBoardnameInToken(url, boardNameIn) {
|
function checkBoardnameInToken(url, boardNameIn) {
|
||||||
var token = url.searchParams.get("token");
|
var token = url.searchParams.get("token");
|
||||||
if (roleInBoard(token, boardNameIn) === 'forbidden') {
|
if (roleInBoard(token, boardNameIn) === "forbidden") {
|
||||||
throw new Error("Acess Forbidden");
|
throw new Error("Acess Forbidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse_role(role) {
|
function parse_role(role) {
|
||||||
let [_, role_name, board_name] = role.match(/^([^:]*):?(.*)$/);
|
let [_, role_name, board_name] = role.match(/^([^:]*):?(.*)$/);
|
||||||
return {role_name, board_name}
|
return { role_name, board_name };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +70,7 @@ function roleInBoard(token, board = null) {
|
||||||
for (var line of roles) {
|
for (var line of roles) {
|
||||||
var role = parse_role(line);
|
var role = parse_role(line);
|
||||||
|
|
||||||
if (role.board_name !== '') {
|
if (role.board_name !== "") {
|
||||||
oneHasBoardName = true;
|
oneHasBoardName = true;
|
||||||
}
|
}
|
||||||
if (role.role_name === "moderator") {
|
if (role.role_name === "moderator") {
|
||||||
|
@ -96,4 +96,4 @@ function roleInBoard(token, board = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {checkBoardnameInToken, roleInBoard};
|
module.exports = { checkBoardnameInToken, roleInBoard };
|
||||||
|
|
|
@ -24,10 +24,9 @@
|
||||||
* @licend
|
* @licend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
(config = require("./configuration.js")),
|
||||||
config = require("./configuration.js"),
|
(jsonwebtoken = require("jsonwebtoken"));
|
||||||
jsonwebtoken = require("jsonwebtoken");
|
const { roleInBoard } = require("./jwtBoardnameAuth");
|
||||||
const {roleInBoard} = require("./jwtBoardnameAuth");
|
|
||||||
/**
|
/**
|
||||||
* Validates jwt and returns whether user is a moderator
|
* Validates jwt and returns whether user is a moderator
|
||||||
* @param {URL} url
|
* @param {URL} url
|
||||||
|
@ -48,5 +47,4 @@ function checkUserPermission(url) {
|
||||||
return isModerator;
|
return isModerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = { checkUserPermission };
|
module.exports = { checkUserPermission };
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
var app = require("http").createServer(handler),
|
var app = require("http").createServer(handler),
|
||||||
sockets = require("./sockets.js"),
|
sockets = require("./sockets.js"),
|
||||||
{log, monitorFunction} = require("./log.js"),
|
{ log, monitorFunction } = require("./log.js"),
|
||||||
path = require("path"),
|
path = require("path"),
|
||||||
fs = require("./fs_promises.js"),
|
fs = require("./fs_promises.js"),
|
||||||
crypto = require("crypto"),
|
crypto = require("crypto"),
|
||||||
|
@ -25,7 +25,7 @@ if (parseFloat(process.versions.node) < MIN_NODE_VERSION) {
|
||||||
process.version +
|
process.version +
|
||||||
", wbo requires at least " +
|
", wbo requires at least " +
|
||||||
MIN_NODE_VERSION +
|
MIN_NODE_VERSION +
|
||||||
" !!!"
|
" !!!",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,10 +109,10 @@ function handler(request, response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const boardTemplate = new templating.BoardTemplate(
|
const boardTemplate = new templating.BoardTemplate(
|
||||||
path.join(config.WEBROOT, "board.html")
|
path.join(config.WEBROOT, "board.html"),
|
||||||
);
|
);
|
||||||
const indexTemplate = new templating.Template(
|
const indexTemplate = new templating.Template(
|
||||||
path.join(config.WEBROOT, "index.html")
|
path.join(config.WEBROOT, "index.html"),
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,10 +145,10 @@ async function handleRequest(request, response) {
|
||||||
if (parts[0] === "") parts.shift();
|
if (parts[0] === "") parts.shift();
|
||||||
|
|
||||||
var fileExt = path.extname(parsedUrl.pathname);
|
var fileExt = path.extname(parsedUrl.pathname);
|
||||||
var staticResources = ['.js','.css', '.svg', '.ico', '.png', '.jpg', 'gif'];
|
var staticResources = [".js", ".css", ".svg", ".ico", ".png", ".jpg", "gif"];
|
||||||
// If we're not being asked for a file, then we should check permissions.
|
// If we're not being asked for a file, then we should check permissions.
|
||||||
var isModerator = false;
|
var isModerator = false;
|
||||||
if(!staticResources.includes(fileExt)) {
|
if (!staticResources.includes(fileExt)) {
|
||||||
isModerator = jwtauth.checkUserPermission(parsedUrl);
|
isModerator = jwtauth.checkUserPermission(parsedUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ async function handleRequest(request, response) {
|
||||||
var boardName = validateBoardName(parts[1]),
|
var boardName = validateBoardName(parts[1]),
|
||||||
history_file = path.join(
|
history_file = path.join(
|
||||||
config.HISTORY_DIR,
|
config.HISTORY_DIR,
|
||||||
"board-" + boardName + ".json"
|
"board-" + boardName + ".json",
|
||||||
);
|
);
|
||||||
jwtBoardName.checkBoardnameInToken(parsedUrl, boardName);
|
jwtBoardName.checkBoardnameInToken(parsedUrl, boardName);
|
||||||
if (parts.length > 2 && /^[0-9A-Za-z.\-]+$/.test(parts[2])) {
|
if (parts.length > 2 && /^[0-9A-Za-z.\-]+$/.test(parts[2])) {
|
||||||
|
@ -260,7 +260,7 @@ async function handleRequest(request, response) {
|
||||||
var boardName = validateBoardName(parts[1]),
|
var boardName = validateBoardName(parts[1]),
|
||||||
history_file = path.join(
|
history_file = path.join(
|
||||||
config.HISTORY_DIR,
|
config.HISTORY_DIR,
|
||||||
"board-" + boardName + ".json"
|
"board-" + boardName + ".json",
|
||||||
);
|
);
|
||||||
jwtBoardName.checkBoardnameInToken(parsedUrl, boardName);
|
jwtBoardName.checkBoardnameInToken(parsedUrl, boardName);
|
||||||
response.writeHead(200, {
|
response.writeHead(200, {
|
||||||
|
@ -310,7 +310,7 @@ async function handleRequest(request, response) {
|
||||||
.then(function (bundleString) {
|
.then(function (bundleString) {
|
||||||
response.setHeader(
|
response.setHeader(
|
||||||
"Cache-Control",
|
"Cache-Control",
|
||||||
"private, max-age=172800, stale-while-revalidate=1728000"
|
"private, max-age=172800, stale-while-revalidate=1728000",
|
||||||
);
|
);
|
||||||
response.setHeader("Vary", "User-Agent");
|
response.setHeader("Vary", "User-Agent");
|
||||||
response.setHeader("Content-Type", "application/javascript");
|
response.setHeader("Content-Type", "application/javascript");
|
||||||
|
@ -321,10 +321,11 @@ async function handleRequest(request, response) {
|
||||||
case "": // Index page
|
case "": // Index page
|
||||||
logRequest(request);
|
logRequest(request);
|
||||||
if (config.DEFAULT_BOARD) {
|
if (config.DEFAULT_BOARD) {
|
||||||
response.writeHead(302, { Location: 'boards/' + encodeURIComponent(config.DEFAULT_BOARD) });
|
response.writeHead(302, {
|
||||||
|
Location: "boards/" + encodeURIComponent(config.DEFAULT_BOARD),
|
||||||
|
});
|
||||||
response.end(name);
|
response.end(name);
|
||||||
} else
|
} else indexTemplate.serve(request, response);
|
||||||
indexTemplate.serve(request, response);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -33,12 +33,17 @@ function startIO(app, boardDataList) {
|
||||||
io = iolib(app);
|
io = iolib(app);
|
||||||
if (config.AUTH_SECRET_KEY) {
|
if (config.AUTH_SECRET_KEY) {
|
||||||
// Middleware to check for valid jwt
|
// Middleware to check for valid jwt
|
||||||
io.use(function(socket, next) {
|
io.use(function (socket, next) {
|
||||||
if(socket.handshake.query && socket.handshake.query.token) {
|
if (socket.handshake.query && socket.handshake.query.token) {
|
||||||
jsonwebtoken.verify(socket.handshake.query.token, config.AUTH_SECRET_KEY, function(err, decoded) {
|
jsonwebtoken.verify(
|
||||||
if(err) return next(new Error("Authentication error: Invalid JWT"));
|
socket.handshake.query.token,
|
||||||
|
config.AUTH_SECRET_KEY,
|
||||||
|
function (err, decoded) {
|
||||||
|
if (err)
|
||||||
|
return next(new Error("Authentication error: Invalid JWT"));
|
||||||
next();
|
next();
|
||||||
})
|
},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
next(new Error("Authentication error: No jwt provided"));
|
next(new Error("Authentication error: No jwt provided"));
|
||||||
}
|
}
|
||||||
|
@ -92,7 +97,7 @@ function handleSocketConnection(socket) {
|
||||||
"error",
|
"error",
|
||||||
noFail(function onSocketError(error) {
|
noFail(function onSocketError(error) {
|
||||||
log("ERROR", error);
|
log("ERROR", error);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.on("getboard", async function onGetBoard(name) {
|
socket.on("getboard", async function onGetBoard(name) {
|
||||||
|
@ -152,7 +157,7 @@ function handleSocketConnection(socket) {
|
||||||
|
|
||||||
//Send data to all other users connected on the same board
|
//Send data to all other users connected on the same board
|
||||||
socket.broadcast.to(boardName).emit("broadcast", data);
|
socket.broadcast.to(boardName).emit("broadcast", data);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.on("disconnecting", function onDisconnecting(reason) {
|
socket.on("disconnecting", function onDisconnecting(reason) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ const client_config = require("./client_configuration");
|
||||||
* @type {object}
|
* @type {object}
|
||||||
*/
|
*/
|
||||||
const TRANSLATIONS = JSON.parse(
|
const TRANSLATIONS = JSON.parse(
|
||||||
fs.readFileSync(path.join(__dirname, "translations.json"))
|
fs.readFileSync(path.join(__dirname, "translations.json")),
|
||||||
);
|
);
|
||||||
const languages = Object.keys(TRANSLATIONS);
|
const languages = Object.keys(TRANSLATIONS);
|
||||||
|
|
||||||
|
@ -33,7 +33,8 @@ class Template {
|
||||||
this.template = handlebars.compile(contents);
|
this.template = handlebars.compile(contents);
|
||||||
}
|
}
|
||||||
parameters(parsedUrl, request, isModerator) {
|
parameters(parsedUrl, request, isModerator) {
|
||||||
const accept_language_str = parsedUrl.query.lang || request.headers["accept-language"];
|
const accept_language_str =
|
||||||
|
parsedUrl.query.lang || request.headers["accept-language"];
|
||||||
const accept_languages = accept_language_parser.parse(accept_language_str);
|
const accept_languages = accept_language_parser.parse(accept_language_str);
|
||||||
const opts = { loose: true };
|
const opts = { loose: true };
|
||||||
let language =
|
let language =
|
||||||
|
@ -41,7 +42,8 @@ class Template {
|
||||||
// The loose matcher returns the first language that partially matches, so we need to
|
// The loose matcher returns the first language that partially matches, so we need to
|
||||||
// check if the preferred language is supported to return it
|
// check if the preferred language is supported to return it
|
||||||
if (accept_languages.length > 0) {
|
if (accept_languages.length > 0) {
|
||||||
const preferred_language = accept_languages[0].code + "-" + accept_languages[0].region;
|
const preferred_language =
|
||||||
|
accept_languages[0].code + "-" + accept_languages[0].region;
|
||||||
if (languages.includes(preferred_language)) {
|
if (languages.includes(preferred_language)) {
|
||||||
language = preferred_language;
|
language = preferred_language;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +53,14 @@ class Template {
|
||||||
const prefix = request.url.split("/boards/")[0].substr(1);
|
const prefix = request.url.split("/boards/")[0].substr(1);
|
||||||
const baseUrl = findBaseUrl(request) + (prefix ? prefix + "/" : "");
|
const baseUrl = findBaseUrl(request) + (prefix ? prefix + "/" : "");
|
||||||
const moderator = isModerator;
|
const moderator = isModerator;
|
||||||
return { baseUrl, languages, language, translations, configuration, moderator };
|
return {
|
||||||
|
baseUrl,
|
||||||
|
languages,
|
||||||
|
language,
|
||||||
|
translations,
|
||||||
|
configuration,
|
||||||
|
moderator,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
serve(request, response, isModerator) {
|
serve(request, response, isModerator) {
|
||||||
const parsedUrl = url.parse(request.url, true);
|
const parsedUrl = url.parse(request.url, true);
|
||||||
|
|
|
@ -2,17 +2,18 @@ const fs = require("../server/fs_promises.js");
|
||||||
const os = require("os");
|
const os = require("os");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const PORT = 8487
|
const PORT = 8487;
|
||||||
const SERVER = 'http://localhost:' + PORT;
|
const SERVER = "http://localhost:" + PORT;
|
||||||
|
|
||||||
let wbo, data_path, tokenQuery, currentPath;
|
let wbo, data_path, tokenQuery, currentPath;
|
||||||
|
|
||||||
async function beforeEach(browser, done) {
|
async function beforeEach(browser, done) {
|
||||||
data_path = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'wbo-test-data-'));
|
data_path = await fs.promises.mkdtemp(
|
||||||
currentPath = process.cwd();
|
path.join(os.tmpdir(), "wbo-test-data-"),
|
||||||
|
);
|
||||||
process.env["PORT"] = PORT;
|
process.env["PORT"] = PORT;
|
||||||
process.env["WBO_HISTORY_DIR"] = data_path;
|
process.env["WBO_HISTORY_DIR"] = data_path;
|
||||||
if(browser.globals.token) {
|
if (browser.globals.token) {
|
||||||
process.env["AUTH_SECRET_KEY"] = "test";
|
process.env["AUTH_SECRET_KEY"] = "test";
|
||||||
tokenQuery = "token=" + browser.globals.token;
|
tokenQuery = "token=" + browser.globals.token;
|
||||||
}
|
}
|
||||||
|
@ -27,23 +28,25 @@ async function afterEach(browser, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testPencil(browser) {
|
function testPencil(browser) {
|
||||||
return browser
|
return browser.assert
|
||||||
.assert.titleContains('WBO')
|
.titleContains("WBO")
|
||||||
.click('.tool[title ~= Crayon]') // pencil
|
.click(".tool[title ~= Crayon]") // pencil
|
||||||
.assert.cssClassPresent('.tool[title ~= Crayon]', ['curTool'])
|
.assert.cssClassPresent(".tool[title ~= Crayon]", ["curTool"])
|
||||||
.executeAsync(async function (done) {
|
.executeAsync(async function (done) {
|
||||||
function sleep(t) {
|
function sleep(t) {
|
||||||
return new Promise(function (accept) { setTimeout(accept, t); });
|
return new Promise(function (accept) {
|
||||||
|
setTimeout(accept, t);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// A straight path with just two points
|
// A straight path with just two points
|
||||||
Tools.setColor('#123456');
|
Tools.setColor("#123456");
|
||||||
Tools.curTool.listeners.press(100, 200, new Event("mousedown"));
|
Tools.curTool.listeners.press(100, 200, new Event("mousedown"));
|
||||||
await sleep(80);
|
await sleep(80);
|
||||||
Tools.curTool.listeners.release(300, 400, new Event("mouseup"));
|
Tools.curTool.listeners.release(300, 400, new Event("mouseup"));
|
||||||
|
|
||||||
// A line with three points that form an "U" shape
|
// A line with three points that form an "U" shape
|
||||||
await sleep(80);
|
await sleep(80);
|
||||||
Tools.setColor('#abcdef');
|
Tools.setColor("#abcdef");
|
||||||
Tools.curTool.listeners.press(0, 0, new Event("mousedown"));
|
Tools.curTool.listeners.press(0, 0, new Event("mousedown"));
|
||||||
await sleep(80);
|
await sleep(80);
|
||||||
Tools.curTool.listeners.move(90, 120, new Event("mousemove"));
|
Tools.curTool.listeners.move(90, 120, new Event("mousemove"));
|
||||||
|
@ -51,22 +54,34 @@ function testPencil(browser) {
|
||||||
Tools.curTool.listeners.release(180, 0, new Event("mouseup"));
|
Tools.curTool.listeners.release(180, 0, new Event("mouseup"));
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.assert.visible("path[d='M 100 200 L 100 200 C 100 200 300 400 300 400'][stroke='#123456']")
|
.assert.visible(
|
||||||
.assert.visible("path[d='M 0 0 L 0 0 C 0 0 40 120 90 120 C 140 120 180 0 180 0'][stroke='#abcdef']")
|
"path[d='M 100 200 L 100 200 C 100 200 300 400 300 400'][stroke='#123456']",
|
||||||
|
)
|
||||||
|
.assert.visible(
|
||||||
|
"path[d='M 0 0 L 0 0 C 0 0 40 120 90 120 C 140 120 180 0 180 0'][stroke='#abcdef']",
|
||||||
|
)
|
||||||
.refresh()
|
.refresh()
|
||||||
.waitForElementVisible("path[d='M 100 200 L 100 200 C 100 200 300 400 300 400'][stroke='#123456']")
|
.waitForElementVisible(
|
||||||
.assert.visible("path[d='M 0 0 L 0 0 C 0 0 40 120 90 120 C 140 120 180 0 180 0'][stroke='#abcdef']")
|
"path[d='M 100 200 L 100 200 C 100 200 300 400 300 400'][stroke='#123456']",
|
||||||
.url(SERVER + '/preview/anonymous?' + tokenQuery)
|
)
|
||||||
.waitForElementVisible("path[d='M 100 200 L 100 200 C 100 200 300 400 300 400'][stroke='#123456']")
|
.assert.visible(
|
||||||
.assert.visible("path[d='M 0 0 L 0 0 C 0 0 40 120 90 120 C 140 120 180 0 180 0'][stroke='#abcdef']")
|
"path[d='M 0 0 L 0 0 C 0 0 40 120 90 120 C 140 120 180 0 180 0'][stroke='#abcdef']",
|
||||||
.back()
|
)
|
||||||
|
.url(SERVER + "/preview/anonymous?" + tokenQuery)
|
||||||
|
.waitForElementVisible(
|
||||||
|
"path[d='M 100 200 L 100 200 C 100 200 300 400 300 400'][stroke='#123456']",
|
||||||
|
)
|
||||||
|
.assert.visible(
|
||||||
|
"path[d='M 0 0 L 0 0 C 0 0 40 120 90 120 C 140 120 180 0 180 0'][stroke='#abcdef']",
|
||||||
|
)
|
||||||
|
.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCircle(browser) {
|
function testCircle(browser) {
|
||||||
return browser
|
return browser
|
||||||
.click('#toolID-Ellipse')
|
.click("#toolID-Ellipse")
|
||||||
.executeAsync(function (done) {
|
.executeAsync(function (done) {
|
||||||
Tools.setColor('#112233');
|
Tools.setColor("#112233");
|
||||||
Tools.curTool.listeners.press(200, 400, new Event("mousedown"));
|
Tools.curTool.listeners.press(200, 400, new Event("mousedown"));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const evt = new Event("mousemove");
|
const evt = new Event("mousemove");
|
||||||
|
@ -75,25 +90,34 @@ function testCircle(browser) {
|
||||||
done();
|
done();
|
||||||
}, 100);
|
}, 100);
|
||||||
})
|
})
|
||||||
.assert.visible("ellipse[cx='0'][cy='200'][rx='200'][ry='200'][stroke='#112233']")
|
.assert.visible(
|
||||||
|
"ellipse[cx='0'][cy='200'][rx='200'][ry='200'][stroke='#112233']",
|
||||||
|
)
|
||||||
.refresh()
|
.refresh()
|
||||||
.waitForElementVisible("ellipse[cx='0'][cy='200'][rx='200'][ry='200'][stroke='#112233']", 15000)
|
.waitForElementVisible(
|
||||||
.click('#toolID-Ellipse') // Click the ellipse tool
|
"ellipse[cx='0'][cy='200'][rx='200'][ry='200'][stroke='#112233']",
|
||||||
.click('#toolID-Ellipse') // Click again to toggle
|
15000,
|
||||||
.assert.containsText('#toolID-Ellipse .tool-name', 'Cercle') // Circle in french
|
)
|
||||||
|
.click("#toolID-Ellipse") // Click the ellipse tool
|
||||||
|
.click("#toolID-Ellipse") // Click again to toggle
|
||||||
|
.assert.containsText("#toolID-Ellipse .tool-name", "Cercle"); // Circle in french
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCursor(browser) {
|
function testCursor(browser) {
|
||||||
return browser
|
return browser
|
||||||
.execute(function (done) {
|
.execute(function (done) {
|
||||||
Tools.setColor('#456123'); // Move the cursor over the board
|
Tools.setColor("#456123"); // Move the cursor over the board
|
||||||
var e = new Event("mousemove");
|
var e = new Event("mousemove");
|
||||||
e.pageX = 150;
|
e.pageX = 150;
|
||||||
e.pageY = 200;
|
e.pageY = 200;
|
||||||
Tools.board.dispatchEvent(e)
|
Tools.board.dispatchEvent(e);
|
||||||
})
|
})
|
||||||
.assert.cssProperty("#cursor-me", "transform", "matrix(1, 0, 0, 1, 150, 200)")
|
.assert.cssProperty(
|
||||||
.assert.attributeEquals("#cursor-me", "fill", "#456123")
|
"#cursor-me",
|
||||||
|
"transform",
|
||||||
|
"matrix(1, 0, 0, 1, 150, 200)",
|
||||||
|
)
|
||||||
|
.assert.attributeEquals("#cursor-me", "fill", "#456123");
|
||||||
}
|
}
|
||||||
|
|
||||||
function testImageUpload(browser) {
|
function testImageUpload(browser) {
|
||||||
|
@ -132,36 +156,96 @@ function testImageUpload(browser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testBoard(browser) {
|
function testBoard(browser) {
|
||||||
var page = browser.url(SERVER + '/boards/anonymous?lang=fr&' + tokenQuery)
|
var page = browser
|
||||||
.waitForElementVisible('.tool[title ~= Crayon]') // pencil
|
.url(SERVER + "/boards/anonymous?lang=fr&" + tokenQuery)
|
||||||
|
.waitForElementVisible(".tool[title ~= Crayon]"); // pencil
|
||||||
page = testImageUpload(page);
|
page = testImageUpload(page);
|
||||||
page = testPencil(page);
|
page = testPencil(page);
|
||||||
page = testCircle(page);
|
page = testCircle(page);
|
||||||
page = testCursor(page);
|
page = testCursor(page);
|
||||||
|
|
||||||
// test hideMenu
|
// test hideMenu
|
||||||
browser.url(SERVER + '/boards/anonymous?lang=fr&hideMenu=true&' + tokenQuery).waitForElementNotVisible('#menu');
|
browser
|
||||||
browser.url(SERVER + '/boards/anonymous?lang=fr&hideMenu=false&' + tokenQuery).waitForElementVisible('#menu');
|
.url(SERVER + "/boards/anonymous?lang=fr&hideMenu=true&" + tokenQuery)
|
||||||
if(browser.globals.token) {
|
.waitForElementNotVisible("#menu");
|
||||||
|
browser
|
||||||
|
.url(SERVER + "/boards/anonymous?lang=fr&hideMenu=false&" + tokenQuery)
|
||||||
|
.waitForElementVisible("#menu");
|
||||||
|
if (browser.globals.token) {
|
||||||
//has moderator jwt and no board name
|
//has moderator jwt and no board name
|
||||||
browser.url(SERVER + '/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3IiXX0.PqYHmV0loeKwyLLYZ1a1eIXBCCaa3t5lYUTu_P_-i14').waitForElementVisible('#toolID-Clear');
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3IiXX0.PqYHmV0loeKwyLLYZ1a1eIXBCCaa3t5lYUTu_P_-i14",
|
||||||
|
)
|
||||||
|
.waitForElementVisible("#toolID-Clear");
|
||||||
//has moderator JWT and other board name
|
//has moderator JWT and other board name
|
||||||
browser.url(SERVER + '/boards/testboard123?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3IiXX0.PqYHmV0loeKwyLLYZ1a1eIXBCCaa3t5lYUTu_P_-i14').waitForElementVisible('#toolID-Clear');
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard123?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3IiXX0.PqYHmV0loeKwyLLYZ1a1eIXBCCaa3t5lYUTu_P_-i14",
|
||||||
|
)
|
||||||
|
.waitForElementVisible("#toolID-Clear");
|
||||||
//has moderator JWT and board name match board name in url
|
//has moderator JWT and board name match board name in url
|
||||||
browser.url(SERVER + '/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdGJvYXJkIl19.UVf6awGEChVxcWBbt6dYoNH0Scq7cVD_xfQn-U8A1lw').waitForElementVisible('#toolID-Clear');
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdGJvYXJkIl19.UVf6awGEChVxcWBbt6dYoNH0Scq7cVD_xfQn-U8A1lw",
|
||||||
|
)
|
||||||
|
.waitForElementVisible("#toolID-Clear");
|
||||||
//has moderator JWT and board name NOT match board name in url
|
//has moderator JWT and board name NOT match board name in url
|
||||||
browser.url(SERVER + '/boards/testboard123?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdGJvYXJkIl19.UVf6awGEChVxcWBbt6dYoNH0Scq7cVD_xfQn-U8A1lw').waitForElementNotPresent('#menu');
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard123?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdGJvYXJkIl19.UVf6awGEChVxcWBbt6dYoNH0Scq7cVD_xfQn-U8A1lw",
|
||||||
|
)
|
||||||
|
.waitForElementNotPresent("#menu");
|
||||||
//has editor JWT and no boardname provided
|
//has editor JWT and no boardname provided
|
||||||
browser.url(SERVER + '/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0b3IiXX0.IJehwM8tPVQFzJ2fZMBHveii1DRChVtzo7PEnSmmFt8').waitForElementNotPresent('#toolID-Clear');
|
browser
|
||||||
browser.url(SERVER + '/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0b3IiXX0.IJehwM8tPVQFzJ2fZMBHveii1DRChVtzo7PEnSmmFt8').waitForElementVisible('#menu')
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0b3IiXX0.IJehwM8tPVQFzJ2fZMBHveii1DRChVtzo7PEnSmmFt8",
|
||||||
|
)
|
||||||
|
.waitForElementNotPresent("#toolID-Clear");
|
||||||
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0b3IiXX0.IJehwM8tPVQFzJ2fZMBHveii1DRChVtzo7PEnSmmFt8",
|
||||||
|
)
|
||||||
|
.waitForElementVisible("#menu");
|
||||||
//has editor JWT and boardname provided and match to the board in the url
|
//has editor JWT and boardname provided and match to the board in the url
|
||||||
browser.url(SERVER + '/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0bzp0ZXN0Ym9hcmQiXX0.-P6gjYlPP5I2zgSoVTlADdesVPfSXV-JXZQK5uh3Xwo').waitForElementVisible('#menu');
|
browser
|
||||||
browser.url(SERVER + '/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0bzp0ZXN0Ym9hcmQiXX0.-P6gjYlPP5I2zgSoVTlADdesVPfSXV-JXZQK5uh3Xwo').waitForElementNotPresent('#toolID-Clear');
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0bzp0ZXN0Ym9hcmQiXX0.-P6gjYlPP5I2zgSoVTlADdesVPfSXV-JXZQK5uh3Xwo",
|
||||||
|
)
|
||||||
|
.waitForElementVisible("#menu");
|
||||||
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0bzp0ZXN0Ym9hcmQiXX0.-P6gjYlPP5I2zgSoVTlADdesVPfSXV-JXZQK5uh3Xwo",
|
||||||
|
)
|
||||||
|
.waitForElementNotPresent("#toolID-Clear");
|
||||||
//has editor JWT and boardname provided and and not match to the board in the url
|
//has editor JWT and boardname provided and and not match to the board in the url
|
||||||
browser.url(SERVER + '/boards/testboard123?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0bzp0ZXN0Ym9hcmQiXX0.-P6gjYlPP5I2zgSoVTlADdesVPfSXV-JXZQK5uh3Xwo').waitForElementNotPresent('#menu');
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard123?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJlZGl0bzp0ZXN0Ym9hcmQiXX0.-P6gjYlPP5I2zgSoVTlADdesVPfSXV-JXZQK5uh3Xwo",
|
||||||
|
)
|
||||||
|
.waitForElementNotPresent("#menu");
|
||||||
//is moderator and boardname contains ":"
|
//is moderator and boardname contains ":"
|
||||||
browser.url(SERVER + '/boards/test:board?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdDpib2FyZCJdfQ.LKYcDccheD2oXAMAemxSekDeowGsMl29CFkgJgwbkGE').waitForElementNotPresent('#menu');
|
browser
|
||||||
browser.url(SERVER + '/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdDpib2FyZCJdfQ.LKYcDccheD2oXAMAemxSekDeowGsMl29CFkgJgwbkGE').waitForElementNotPresent('#menu');
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/test:board?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdDpib2FyZCJdfQ.LKYcDccheD2oXAMAemxSekDeowGsMl29CFkgJgwbkGE",
|
||||||
|
)
|
||||||
|
.waitForElementNotPresent("#menu");
|
||||||
|
browser
|
||||||
|
.url(
|
||||||
|
SERVER +
|
||||||
|
"/boards/testboard?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJtb2RlcmF0b3I6dGVzdDpib2FyZCJdfQ.LKYcDccheD2oXAMAemxSekDeowGsMl29CFkgJgwbkGE",
|
||||||
|
)
|
||||||
|
.waitForElementNotPresent("#menu");
|
||||||
}
|
}
|
||||||
page.end();
|
page.end();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue