Commit e891c667 authored by Elias Boschung's avatar Elias Boschung

Initial commit based on sketchpad.pro

parents
Pipeline #729 failed with stages
*~
node_modules
.DS_STORE
This diff is collapsed.
Sketchpad.pro
==========
Sketchpad Pro is a simple graphic editor written for web. This drawing plugin uses HTML5 Canvas supported by all modern browsers (Chrome, Firefox, Opera, Internet Explorer...). You can use any device to draw on "sketchpad". Drawen sketches you can export to jepg/png or save as .json history file.
Sketchpad Pro is using inputs history to store drawings. This allows to cooperate multiple users in real-time using WebSocket server.
Sketchpad Pro is fully customisable javascript library written in ES5.
Using Sketchpad Pro with a CDN
Copy this script and paste into your page to include Sketchpad Pro from CDN server:
```
<script src="https://cdn.sketchpad.pro/dist/current/sketchpad.min.js"></script>
```
Build your own Sketchpad Pro
1. Download & install current Node.js.
2. Download {@link https://developers.sketchpad.pro Sketchpad Pro developer pack} and extract ``sketchpad/`` folder or clone project from GitHub:
```
git clone https://github.com/cojapacze/sketchpad.git
```
3. Run in terminal:
```
cd sketchpad
npm install
gulp
```
4. Check dist/ folder for your build files. Open test page: demos/online.html to test your build.
5. Run local server
```
cd server
node server
```
Try:
```
gulp watch
```
to watch files for changes while development.
## Demos
https://developers.sketchpad.pro/advanced.html
## Docker
Dockerfile and docker-compose are available in the Docker directory of the project.
## Documentation
The full documentation is available online at the following address:
https://developers.sketchpad.pro/documentation.html
## Checking Your Installation
The Sketchpad.pro comes with a few sample pages that can be used to verify that
installation proceeded properly. Take a look at the `demos` in installation directory.
Just call for example:
```
simple.html
```
## License
AGPL-3.
https://www.gnu.org/licenses/agpl-3.0.html
This diff is collapsed.
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3 https://www.gnu.org/licenses/agpl-3.0-standalone.html
/**
* @source: http://developers.sketchpad.pro/dist/src/eventsmanager.js
*
* Sketchpad.pro
* {@link http://sketchpad.pro|Sketchpad.pro - drawing board sketch free}
*
* @copyright 2016-2017 positivestudio.co
* @version 0.5.1
* @author positivestudio.co
*
* @licstart The following is the entire license notice for the
* JavaScript code in this page.
*
* Copyright (C) 2016 positivestudio.co
*
* Sketchpad.pro - drawing board sketch pad
* Copyright (C) 2016 positivestudio.co
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this page.
*
* @file Inputs stack
*
* Date: 2016-08-11T14:00Z
*/
/*global window, addEvent*/
/**
* Palette of colors
* @class
* @param {DOMElement} el
* @param {function} pickCb
*/
function Eventsmanager() {
"use strict";
/**
* Key array for registering callback functions for events
* @type {Object}
*/
this.registeredCallbacks = {};
this.registeredCallbacksOnce = {};
}
Eventsmanager.prototype = {
/**
* Register event
*
* @param {string} event - event name
* @param {function} callback - callback function
*/
on: function on(event, callbackFn) {
"use strict";
if (!this.registeredCallbacks[event]) {
this.registeredCallbacks[event] = [];
}
this.registeredCallbacks[event].push(callbackFn);
return this;
},
removeListener: function removeListener(event, callbackFn) {
"use strict";
if (!this.registeredCallbacks[event]) {
return;
}
var i;
for (i = this.registeredCallbacks[event].length - 1; i >= 0; i -= 1) {
if (this.registeredCallbacks[event][i] === callbackFn) {
this.registeredCallbacks[event].splice(i, 1);
}
}
this.registeredCallbacks[event].indexOf(callbackFn);
},
once: function on(event, callbackFn, uniqueId) {
"use strict";
if (!this.registeredCallbacksOnce[event]) {
this.registeredCallbacksOnce[event] = [];
}
var added = false;
if (uniqueId) {
this.registeredCallbacksOnce[event].forEach(function (callback) {
if (callback.id === uniqueId) {
callback.fn = callbackFn;
added = true;
return;
}
});
}
if (!added) {
this.registeredCallbacksOnce[event].push({
id: uniqueId,
fn: callbackFn
});
}
return this;
},
/**
* Dispatch event
* @param {string} event - event name
* @param {any} param - argument passed to callback functions
* @param {any} param2 - argument 2 passed to callback functions
* @param {any} param3 - argument 2 passed to callback functions ... dont f with Douglas
*/
dispatch: function dispatch(event, param, param2, param3) {
"use strict";
var that = this;
if (this.registeredCallbacks[event]) {
this.registeredCallbacks[event].forEach(function (callback) {
callback.call(that, param, param2, param3);
});
}
if (this.registeredCallbacksOnce[event]) {
this.registeredCallbacksOnce[event].forEach(function (callback) {
// console.log("CALLING ONCE", callback);
callback.fn.call(that, param, param2, param3);
});
delete this.registeredCallbacksOnce[event];
}
return this;
}
};
window.Eventsmanager = Eventsmanager;
\ No newline at end of file
This diff is collapsed.
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3 https://www.gnu.org/licenses/agpl-3.0-standalone.html
/**
* @source: http://developers.sketchpad.pro/dist/src/events.js
*
* Sketchpad.pro
* {@link http://sketchpad.pro|Sketchpad.pro - drawing board sketch free}
*
* @copyright 2016-2017 positivestudio.co
* @version 0.5.1
* @author positivestudio.co
*
* @licstart The following is the entire license notice for the
* JavaScript code in this page.
*
* Copyright (C) 2016 positivestudio.co
*
* Sketchpad.pro - drawing board sketch pad
* Copyright (C) 2016 positivestudio.co
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this page.
*
* @file Inputs stack
*
* Date: 2016-08-11T14:00Z
*/
/*global window, calculateOffsetXY*/
// Test via a getter in the options object to see if the passive property is accessed
var supportsPassive = false;
try {
var opts = Object.defineProperty({}, 'passive', {
get: function () {
"use strict";
supportsPassive = true;
}
});
window.addEventListener("test", null, opts);
} catch (e) {
console.log("ignore", e);
}
window.supportsPassive = supportsPassive;
/**
* Calculate element offset
*
* @param {HTMLElement} target - target element
* @param {number} screenX - coordinate y
* @param {number} screenY - coordinate x
* @return {object} - {x: number, y: number}
*/
function elementOffset(el) {
"use strict";
var
// style = el.currentStyle || window.getComputedStyle(el, null),
borderLeftWidth = 0,//parseInt(style.borderLeftWidth, 10),
borderTopWidth = 0,//parseInt(style.borderTopWidth, 10),
rect = el.getBoundingClientRect(),
offsetX = borderLeftWidth - rect.left,
offsetY = borderTopWidth - rect.top;
return {
x: offsetX,
y: offsetY
};
}
function addEvent(el, event, callback, useCapture) {
"use strict";
var third = false;
if (supportsPassive) {
third = {
passive: true
};
}
third = false;
function getSelectedText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection !== undefined && document.selection.type === "Text") {
text = document.selection.createRange().text;
}
return text;
}
function touch2mouse(e) {
var i,
touch,
offset = elementOffset(el);
for (i = 0; i < e.changedTouches.length; i += 1) {
touch = e.changedTouches[i];
touch.offsetX = touch.clientX + offset.x;
touch.offsetY = touch.clientY + offset.y;
callback(touch);
}
}
var firstTouchStart,
lastTouchEnd,
swipeThreshold = 30;
switch (event) {
case "swipe-right":
console.log("adding swipe right");
el.addEventListener("touchstart", function (e) {
// var i;
firstTouchStart = e.changedTouches[0];
// for (i = 0; i < e.changedTouches.length; i += 1) {
// callback(e.changedTouches[i]);
// }
}, useCapture);
el.addEventListener("mousedown", function (e) {
console.log("mdsr");
firstTouchStart = e;
}, useCapture);
el.addEventListener("touchend", function (e) {
lastTouchEnd = e.changedTouches[e.changedTouches.length - 1];
if (!lastTouchEnd || !firstTouchStart) {
// console.log("swipe-right-mouseup");
return;
}
if (lastTouchEnd.screenX - firstTouchStart.screenX > swipeThreshold && !getSelectedText() && !(document.activeElement.type === "text")) {
callback(e);
}
}, useCapture);
el.addEventListener("mouseup", function (e) {
lastTouchEnd = e;
if (!lastTouchEnd || !firstTouchStart) {
// console.log("swipe-right-mouseup");
return;
}
if (lastTouchEnd.screenX - firstTouchStart.screenX > swipeThreshold && !getSelectedText() && !(document.activeElement.type === "text")) {
callback(e);
}
}, useCapture);
// el.addEventListener("mouseover", callback, useCapture);
break;
case "hover-in":
el.addEventListener("touchstart", function (e) {
var i;
for (i = 0; i < e.changedTouches.length; i += 1) {
callback(e.changedTouches[i]);
}
}, useCapture);
el.addEventListener("touchmove", function (e) {
var i;
for (i = 0; i < e.changedTouches.length; i += 1) {
callback(e.changedTouches[i]);
}
}, useCapture);
el.addEventListener("mouseover", callback, useCapture);
break;
case "hover-out":
el.addEventListener("mouseout", callback, useCapture);
break;
case "tap":
// console.log("add tap", useCapture);
el.addEventListener("touchstart", function (e) {
e.preventDefault();
if (document.activeElement !== e.target && document.activeElement.tagName === "INPUT") {
document.activeElement.blur();
}
var i;
for (i = 0; i < e.changedTouches.length; i += 1) {
e.changedTouches[i].stopPropagation = e.stopPropagation && e.stopPropagation.bind(e);
e.changedTouches[i].preventDefault = e.preventDefault && e.preventDefault.bind(e);
callback(e.changedTouches[i]);
}
e.preventMouseFallback = true;
}, useCapture);
el.addEventListener("mousedown", function (e) {
callback(e);
}, useCapture);
// el.addEventListener("touchmove", function (e) {
// var i;
// for (i = 0; i < e.changedTouches.length; i += 1) {
// callback(e.changedTouches[i]);
// }
// });
break;
case "pop":
el.addEventListener("touchend", function (e) {
e.preventDefault();
if (document.activeElement !== e.target && document.activeElement.tagName === "INPUT") {
document.activeElement.blur();
}
var i;
for (i = 0; i < e.changedTouches.length; i += 1) {
e.changedTouches[i].stopPropagation = e.stopPropagation && e.stopPropagation.bind(e);
e.changedTouches[i].preventDefault = e.preventDefault && e.preventDefault.bind(e);
callback(e.changedTouches[i]);
}
e.preventMouseFallback = true;
}, useCapture);
el.addEventListener("mouseup", function (e) {
callback(e);
}, useCapture);
// el.addEventListener("touchmove", function (e) {
// var i;
// for (i = 0; i < e.changedTouches.length; i += 1) {
// callback(e.changedTouches[i]);
// }
// });
break;
case "slide":
el.addEventListener("mousedown", callback, useCapture);
el.addEventListener("mouseup", callback, useCapture);
el.addEventListener("mousemove", function (e) {
if (e.buttons || (e.buttons === undefined && e.which)) {
callback(e);
}
}, useCapture);
el.addEventListener("touchstart", touch2mouse, useCapture);
el.addEventListener("touchend", touch2mouse, useCapture);
el.addEventListener("touchmove", touch2mouse, useCapture);
break;
default:
el.addEventListener(event, callback, useCapture);
}
}
window.addEvent = addEvent;
\ No newline at end of file
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3 https://www.gnu.org/licenses/agpl-3.0-standalone.html
/**
* @source: http://developers.sketchpad.pro/dist/src/fontpalette.js
*
* Sketchpad.pro
* {@link http://sketchpad.pro|Sketchpad.pro - drawing board sketch free}
*
* @copyright 2016-2017 positivestudio.co
* @version 0.5.1
* @author positivestudio.co
*
* @licstart The following is the entire license notice for the
* JavaScript code in this page.
*
* Copyright (C) 2016 positivestudio.co
*
* Sketchpad.pro - drawing board sketch pad
* Copyright (C) 2016 positivestudio.co
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this page.
*
* @file Inputs stack
*
* Date: 2016-08-11T14:00Z
*/
/*global window, addEvent*/
/*global Eventsmanager, Keyshortcuts*/
/**
* Palette of colors
* @class
* @param {DOMElement} el
* @param {function} pickCb
*/
function Fontpalette(config) {
"use strict";
// Call parent constructor
Eventsmanager.call(this, config);
if (typeof config.keyModifiers !== "object") {
config.keyModifiers = {};
}
// var pickCb = config.pick;
var el = config.containerEl;
this.containerEl = el;
el.classList.add("fontpalette");
var width = 200,
height = 220;
this.width = width;
this.height = height;
this.font = "Georgia";
this.size = 60;
this.color = {
hue: 0,
saturation: 0,
value: 0,
red: 0,
green: 0,
blue: 0,
alpha: 1
};
this.minSize = config.minSize || 10;
this.maxSize = config.maxSize || 100;
this.fontList = config.fontList || ["Arial", "Arial Black", "Arial Narrow", "Courier New", "Lucida Sans Typewriter", "Papyrus", "Tahoma", "Times New Roman", "Trebuchet MS", "Comic Sans MS", "Verdana"];
el.style.position = "relative";
el.style.width = width + "px";
el.style.height = height + "px";//yeap
el.style.display = "inline-block";
var that = this;
this.fontListSelectEl = document.createElement("select");
this.fontListSelectEl.style.height = Math.floor(height * (1 / 11)) + "px";
this.fontListSelectEl.style.width = "100%";
this.fontListSelectEl.style.lineHeight = Math.floor(height * (1 / 11)) + "px";
this.fontList.forEach(function (font) {
var optionEl = document.createElement("option");
optionEl.value = font;
optionEl.textContent = font;
that.fontListSelectEl.appendChild(optionEl);
});
function onFontChange(e) {
that.setFont(e.target.value);
}
this.fontListSelectEl.addEventListener("change", onFontChange);
this.previewCanvas = document.createElement("canvas");
this.previewCanvas.className = "palette";
this.previewCanvas.width = width;
this.previewCanvas.height = Math.floor(height * (9 / 11));
this.previewContext2d = this.previewCanvas.getContext("2d");
this.paletteIData = this.previewContext2d.createImageData(this.previewCanvas.width, this.previewCanvas.height);
this.previewCanvas.style.cursor = "crosshair";
this.previewCanvas.style.position = "absolute";
this.previewCanvas.style.left = "0px";
this.previewCanvas.style.top = "20px";
this.sizeEl = document.createElement("div");
this.sizeEl.style.backgroundColor = "transparent";
this.sizeEl.style.position = "absolute";
this.sizeEl.className = "bg";
this.sizeEl.style.top = (height * (10 / 11)) + "px";
this.sizeEl.style.left = "0px";
this.sizeEl.style.cursor = "col-resize";
this.sizeEl.style.borderLeft = width + "px solid transparent";
this.sizeEl.style.borderBottom = (height * (1 / 11)) + "px solid black";
this.sizeSliderEl = document.createElement("div");
this.sizeSliderEl.className = "slider hue-slider";
this.sizeSliderEl.style.marginLeft = -width * 0.015 + "px";
this.sizeSliderEl.style.marginTop = -height * 0.01 + "px";
this.sizeSliderEl.style.width = width * 0.03 + "px";
this.sizeSliderEl.style.height = height * (0.11) + "px";
this.sizeSliderEl.style.position = "absolute";
this.sizeSliderEl.style.top = (10 / 11) * height + "px";
this.sizeSliderEl.style.left = "0px";
this.sizeSliderEl.style.backgroundColor = "white";
this.sizeSliderEl.style.cursor = "col-resize";
this.sizeSliderEl.style.boxShadow = "inset 0 0 2px #000";
this.sizeSliderEl.style.zIndex = "100";
function changeSize(e) {
var x = that.normalize(that.width + e.offsetX, 0, that.width);
var size = that.minSize + (x / that.width) * (that.maxSize - that.minSize);
that.setSize(size);
}
function vWheel(e) {
e.preventDefault();
that.setSize(that.size - e.deltaY / 2);
}
this.previewCanvas.addEventListener("wheel", vWheel);