70 Commits

Author SHA1 Message Date
Lukas F. Hartmann
dfad9269ff add empty database folder, or first bringup fails 2021-06-28 14:30:35 +02:00
Lukas F. Hartmann
0a1bd2a107 WIP chargebee integration sketch 2021-05-23 23:53:41 +02:00
Lukas F. Hartmann
6ef1f8fba6 packages: update the rest of the packages manually 2021-05-15 20:42:10 +02:00
Lukas F. Hartmann
a1fb09709a packages: automatic npm audit updates 2021-05-15 20:03:32 +02:00
Romuald
3dd0fce139 change configuration for docker compliance (#189) 2021-05-15 19:52:24 +02:00
dm
50e421d64d fix api-token check 2021-04-20 15:14:41 +02:00
dm
c88433fcff Merge branch 'mnt' of https://github.com/spacedeck/spacedeck-open into mnt 2021-04-07 02:46:09 +02:00
banglashi
ff403a9c23 Merge pull request #179 from arillo/feature/paste-in-place
check for isShift to paste in place
2021-04-07 00:03:51 +02:00
banglashi
79384a2fb2 Merge pull request #178 from arillo/mnt
set language order
2021-04-07 00:03:14 +02:00
dm
7fba188661 check for isShift to paste in place 2021-04-06 23:59:20 +02:00
dm
1242cb84ee language order 2021-04-01 20:31:19 +02:00
dm
8cc027ed95 add dev conf 2021-04-01 19:14:49 +02:00
dm
aa3deb9d72 pass on config in account 2021-04-01 17:46:26 +02:00
banglashi
0cb5ecb08c Merge pull request #169 from bckmnn/dev/puppeteer
replace phantom js with puppeteer
2021-03-24 21:12:47 +01:00
dm
364578b0aa add relative paths to docker 2021-03-24 00:19:37 +01:00
dm
08b9bb4118 empty second page ->
added small margin
2021-03-24 00:13:01 +01:00
dm
63d70c498a Upgrade puppeteer to version 8.0.0
space thumbnails background-size: cover
2021-03-24 00:03:07 +01:00
banglashi
38fffa5644 Merge pull request #154 from eneiluj/fix/space-auth-guest-nickname-issue
Fix space_auth being overriden by unused parameter, add 'nickname' GET param
2021-03-23 23:19:04 +01:00
banglashi
b626a21b83 Merge pull request #155 from eneiluj/enh/default-stroke
Set default stroke to 2 instead of 0
2021-03-23 23:03:33 +01:00
banglashi
6cdb804a8b Merge pull request #119 from juliandavidmr/patch-1
add basic SEO tags
2021-03-23 22:41:40 +01:00
banglashi
89d5a7fd9f Merge pull request #172 from Thorgrimson/mnt
improved README section Installion
2021-03-23 22:36:09 +01:00
banglashi
8ea0fd190f Merge pull request #168 from Mejans/patch-2
i18n update to correct grammar
2021-03-23 22:35:12 +01:00
dm
9018a233a1 add Hungarian 2021-03-23 22:30:37 +01:00
ovari
d196af739c Create hu.js 2021-03-19 20:12:24 +11:00
Thorgrimson
0c877bc20b Update nginx_setup.md 2021-03-07 22:16:34 +01:00
Thorgrimson
803aad6219 Update README.md 2021-03-07 21:30:00 +01:00
Thorgrimson
79cd848cbb Update README.md 2021-03-07 21:02:58 +01:00
bckmnn
6bd7130597 fixes pdf export of images 2021-02-20 17:04:58 +01:00
bckmnn
5c753cb82a removed phantom js 2021-02-20 16:43:42 +01:00
Mejans
7a42865c84 Grammatical error
Just a small change
2021-02-17 09:22:24 +01:00
bckmnn
ea40f1cc7a added line drawings to pdf 2021-02-14 17:25:41 +01:00
bckmnn
26329b000b replaced phantomjs with puppeteer
started to replace phantomjs with puppeteer
2021-02-14 17:18:50 +01:00
dm
7c8ef82bd1 Merge branch 'mnt' of https://github.com/spacedeck/spacedeck-open into mnt 2021-02-14 12:55:58 +01:00
banglashi
20bbb5aa08 Merge pull request #167 from arillo/fix/upload-conversion-progress
fix double %
2021-02-14 12:54:48 +01:00
dm
176b383cbd fix double % 2021-02-14 12:54:14 +01:00
dm
0e2c6401e0 build first 2021-02-12 20:15:27 +01:00
dm
28c31e9331 add mnt branch to deploy to dev 2021-02-12 20:12:00 +01:00
dm
56988fc1c4 set video to left: 0
(so the possible alignment settings doesn't apply)
2021-02-06 01:25:07 +01:00
banglashi
f23b44e6c2 Merge pull request #144 from Mejans/patch-1
Update + correction
2021-02-06 01:14:43 +01:00
banglashi
927b0ddac1 Merge pull request #162 from arillo/feature/convert-percentage
add video conversion progress
2021-02-06 01:13:22 +01:00
dm
1cd69e6538 add gulp clean css to minify css 2021-02-06 01:11:54 +01:00
dm
ec797709fd style convertion progress 2021-02-06 01:04:41 +01:00
dm
8d73918d0d Merge branch 'mnt' of github.com:spacedeck/spacedeck-open into feature/convert-percentage 2021-02-06 00:27:30 +01:00
banglashi
7e5b988606 Merge pull request #159 from arillo/feature/nodemon
use nodemon for development
2021-02-06 00:23:52 +01:00
dm
35c747eabb add storage & .DS_Store to .gitignore 2021-02-06 00:23:01 +01:00
dm
fab692787c add .tool-versions file 2021-02-06 00:22:44 +01:00
dm
c1a3700bae add vscode launch configuration 2021-02-06 00:22:36 +01:00
banglashi
c64c41f624 Merge pull request #161 from arillo/config/disable-db-logs
disable db logs via config/default.json
2021-02-06 00:17:59 +01:00
banglashi
3fd00bf755 Merge branch 'mnt' into config/disable-db-logs 2021-02-06 00:17:46 +01:00
banglashi
e135976299 Merge pull request #158 from arillo/feature/default-color-via-config
allow for custom color swatches via config/default.json
2021-02-06 00:14:22 +01:00
banglashi
170ed5471d Merge pull request #157 from arillo/feature/postgres
Feature/postgres
2021-02-06 00:13:21 +01:00
dm
1b05aaeaf8 add video conversion progress 2021-01-26 01:38:46 +01:00
dm
a7704875c5 check existance of config value 2021-01-21 23:43:47 +01:00
dm
60667187f3 disable db logs via config/default.json 2021-01-21 23:26:08 +01:00
dm
058c414ae3 fix 2021-01-21 09:09:17 +01:00
dm
ba72cf7dc8 use configurable default colors on reset 2021-01-21 08:54:29 +01:00
dm
3d391c571c ammend docs 2021-01-20 09:14:49 +01:00
dm
96e9b82fbb add nodemon 2021-01-18 21:06:54 +01:00
dm
5a9a79addb allow for custom color swatches
reset colors to black on deselect
fix fill_color default black
2021-01-18 20:53:54 +01:00
mntmn
fbf18839f9 Fix Space browsing/editing on touch devices such as iPad (#152)
* touch (tablet): fix deselect when tapping space; add edit text butto
* touch: fix media upload on iDevice
* touch: fix vector transforming (points of arrows, scribbles)

Co-authored-by: Lukas F. Hartmann <lukas@mntre.com>
2021-01-11 13:04:07 +01:00
dm
65476a0d09 remove pg dependency, add docs 2021-01-07 15:41:37 +01:00
dm
18d09b49be unify config, add postgres decimal fix 2021-01-07 15:26:13 +01:00
dm
b2cf8cf336 change to unindented config definition 2021-01-07 14:54:24 +01:00
dm
e04eedb2c4 add postgres support 2021-01-07 14:37:54 +01:00
Julien Veyssier
4df480c8c5 set default stroke to 2 instead of 0, fixes first drawn shapes being invisible
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2021-01-04 16:17:34 +01:00
Julien Veyssier
a184225c8b fix space_auth being overriden by unused parameter, add 'nickname' GET param
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2020-12-27 16:57:27 +01:00
Lukas F. Hartmann
72221fcf0b fix copyright in footer 2020-12-18 18:17:15 +01:00
Mejans
531ad2f833 Update oc.js 2020-12-02 19:38:16 +01:00
Mejans
ba10889ef8 Update + correction 2020-12-02 19:35:59 +01:00
Julian David
0fdeef12ed add basic SEO tags 2020-11-09 20:33:00 -05:00
47 changed files with 8747 additions and 16623 deletions

3
.gitignore vendored
View File

@@ -5,4 +5,5 @@ public/stylesheets/*.css
database.sqlite database.sqlite
*.swp *.swp
*~ *~
storage/
.DS_Store

44
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,44 @@
variables:
CONTAINER_IMAGE: git.universe.io:4567/$CI_PROJECT_PATH
DOCKER_HOST: tcp://docker:2376
stages:
- build
- deploy_dev
- deploy
build:
stage: build
image: docker:stable
services:
- docker:19.03.12-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
script:
- docker image build -t $CONTAINER_IMAGE:$CI_BUILD_REF -t $CONTAINER_IMAGE:latest .
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN git.universe.io:4567
- docker image push $CONTAINER_IMAGE:latest
- docker image push $CONTAINER_IMAGE:$CI_BUILD_REF
only:
- mnt
- custom/freiwerkb
deploy_dev:
stage: deploy
image: alpine
script:
- apk add --update curl
- curl -XPOST $WWW_WEBHOOK
only:
- mnt
- custom/freiwerkb
deploy:
when: manual
stage: deploy
image: alpine
script:
- apk add --update curl
- |
curl -XPOST -K - <<URL
$WWW_WEBHOOKS
URL
only:
- master
- custom/freiwerkb

1
.tool-versions Normal file
View File

@@ -0,0 +1 @@
nodejs 10.23.1

29
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,29 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch debug",
"runtimeExecutable": "${workspaceFolder}/node_modules/nodemon/bin/nodemon.js",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/spacedeck.js",
"cwd": "${workspaceFolder}",
"env": {
"NODE_APP_INSTANCE" :"dev"
}
},
{
"type": "node",
"request": "launch",
"name": "Launch ldap",
"runtimeExecutable": "${workspaceFolder}/node_modules/nodemon/bin/nodemon.js",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/spacedeck.js",
"cwd": "${workspaceFolder}",
"env": {
"NODE_APP_INSTANCE" :"ldap"
}
}
]
}

8
CHANGELOG.md Normal file
View File

@@ -0,0 +1,8 @@
# Changelog
## [X.X.X] - 2021-03-23
### Changes
- Replaced phantomjs with puppeteer #169
- Create Hungarian language pack #174

View File

@@ -21,6 +21,20 @@ RUN cd audiowaveform/build/ && cmake -D ENABLE_TESTS=0 -D BUILD_STATIC=1 ..
RUN cd audiowaveform/build/ && make RUN cd audiowaveform/build/ && make
RUN cd audiowaveform/build/ && make install RUN cd audiowaveform/build/ && make install
# install chromium
RUN apk add --no-cache \
chromium \
nss \
freetype \
freetype-dev \
harfbuzz \
ca-certificates \
ttf-freefont
# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# install other requirements # install other requirements
RUN apk add graphicsmagick ffmpeg ffmpeg-dev ghostscript RUN apk add graphicsmagick ffmpeg ffmpeg-dev ghostscript

View File

@@ -1,13 +1,15 @@
const gulp = require('gulp') const gulp = require('gulp');
const sass = require('gulp-sass') const sass = require('gulp-sass');
const concat = require('gulp-concat') const concat = require('gulp-concat');
const cleanCSS = require('gulp-clean-css');
gulp.task('styles', function(done) { gulp.task('styles', function(done) {
gulp.src('styles/**/*.scss') gulp.src('styles/**/*.scss')
.pipe(sass({ .pipe(sass({
errLogToConsole: true errLogToConsole: true
})) }))
.pipe(cleanCSS())
.pipe(gulp.dest('./public/stylesheets/')) .pipe(gulp.dest('./public/stylesheets/'))
.pipe(concat('style.css')) .pipe(concat('style.css'))
done() done()
}) });

View File

@@ -12,7 +12,7 @@ We appreciate filed issues, pull requests and general discussion.
# Features # Features
- Create virtual whiteboards called *Spaces* with virtually unlimited size - Create virtual whiteboards called _Spaces_ with virtually unlimited size
- Drag & drop images, videos and audio from your computer or the web - Drag & drop images, videos and audio from your computer or the web
- Write and format text with full control over fonts, colors and style - Write and format text with full control over fonts, colors and style
- Draw, annotate and highlight with included graphical shapes - Draw, annotate and highlight with included graphical shapes
@@ -37,7 +37,7 @@ Spacedeck requires:
To run Spacedeck, you only need Node.JS 10.x. To run Spacedeck, you only need Node.JS 10.x.
To install all node dependencies, run (do this once): To install all node dependencies, run (do this once) after cloning the repository:
npm install npm install
@@ -45,6 +45,52 @@ To install all node dependencies, run (do this once):
See [config/default.json](config/default.json). Set `storage_local_path` for a local sqlite database or `storage_region`, `storage_bucket`, `storage_cdn` and `storage_endpoint` for AWS S3. `mail_provider` may be one of `console` or `smtp`. Also, omit a trailing `/` for the `endpoint`. See [config/default.json](config/default.json). Set `storage_local_path` for a local sqlite database or `storage_region`, `storage_bucket`, `storage_cdn` and `storage_endpoint` for AWS S3. `mail_provider` may be one of `console` or `smtp`. Also, omit a trailing `/` for the `endpoint`.
## Disable DB logs
```json
...
"db_logs_disabled": true
...
```
## Configure color swatches
Add a custom array of swatches to your config/default.json.
**You should include the swatch transparent (rgba(0,0,0,0)) so users can remove the color applied.**
## Configure default colors
You can define text, stroke and fill color in your config/default.json.
**You also should include the default colors in your custom swatches palette.**
```json
...
"spacedeck": {
"default_text_color": "#E11F26",
"default_stroke_color": "#9E0F13",
"default_fill_color": "#64BCCA",
"swatches": [
{"id":8, "hex":"#000000"},
{"id":30, "hex":"rgba(0,0,0,0)"},
{"id":31, "hex": "#E11F26"},
{"id":32, "hex": "#9E0F13"},
{"id":33, "hex": "#64BCCA"},
{"id":34, "hex": "#40808A"},
{"id":35, "hex": "#036492"},
{"id":36, "hex": "#005179"},
{"id":37, "hex": "#84427E"},
{"id":38, "hex": "#6C3468"},
{"id":39, "hex": "#F79B84"},
{"id":40, "hex": "#B57362"},
{"id":41, "hex": "#E7D45A"},
{"id":42, "hex": "#ACA044"}
]
}
...
```
# Run (web server) # Run (web server)
node spacedeck.js node spacedeck.js
@@ -61,18 +107,34 @@ For advanced media conversion:
# Data Storage # Data Storage
By default, media files are uploaded to the ```storage``` folder. By default, media files are uploaded to the `storage` folder.
The database is stored in ```database.sqlite``` by default. The database is stored in `database.sqlite` by default.
# Other databases (Not officially supported)
## Postgres
Add the [pg](https://www.npmjs.com/package/pg) module and change the config/default.json to
```
"storage_dialect": "postgres",
```
Adapt the other values as needed
```
"storage_host": "localhost",
"storage_database": "spacedeck",
"storage_username": "username",
"storage_password": "password",
```
# Run with Docker # Run with Docker
- configure `config/default.json` - configure `config/default.json`
- configure `volumes` section inside `docker-compose.yml` - adapt your `docker-compose.yml` if needed.
- point to `database.sqlite` on the host system - start the container with `docker-compose up`
- `touch database.sqlite` if it not exists (use `-d` for background process and `--build` for rebuilding the image)
- point to `storage/` on the host system
- `mkdir storage/` if it not exists
- start the container with `sudo docker-compose up -f docker-compose.yml -d --build`
# Hacking # Hacking
@@ -89,7 +151,7 @@ Spacedeck Open source code is released under the GNU Affero General Public Licen
Spacedeck Open - Web-based Collaborative Whiteboard For Rich Media Spacedeck Open - Web-based Collaborative Whiteboard For Rich Media
Copyright (C) 2011-2018 Lukas F. Hartmann, Martin Güther Copyright (C) 2011-2018 Lukas F. Hartmann, Martin Güther
Icons and original CSS design copyright by Thomas Helbig Icons and original CSS design copyright by Thomas Helbig
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the published by the Free Software Foundation, either version 3 of the

View File

@@ -7,8 +7,15 @@
"endpoint": "http://localhost:9666", "endpoint": "http://localhost:9666",
"invite_code": "top-sekrit", "invite_code": "top-sekrit",
"storage_dialect": "sqlite",
"storage_host": "localhost",
"storage_database": "spacedeck",
"storage_username": "username",
"storage_password": "password",
"storage_local_path": "./storage", "storage_local_path": "./storage",
"storage_local_db": "./database.sqlite", "storage_local_db": "./database/database.sqlite",
"storage_region": "eu-central-1", "storage_region": "eu-central-1",
"storage_endpoint": "http://localhost:4572", "storage_endpoint": "http://localhost:4572",
"storage_bucket": "my_spacedeck_bucket", "storage_bucket": "my_spacedeck_bucket",
@@ -18,7 +25,7 @@
"redis_mock": true, "redis_mock": true,
"redis_host": "localhost", "redis_host": "localhost",
"phantom_api_secret": "very_secret_phantom_password", "export_api_secret": "very_secret_export_password",
"mail_provider": "smtp", "mail_provider": "smtp",
"mail_smtp_host": "your.smtp.host", "mail_smtp_host": "your.smtp.host",
@@ -26,5 +33,6 @@
"mail_smtp_secure": true, "mail_smtp_secure": true,
"mail_smtp_require_tls": true, "mail_smtp_require_tls": true,
"mail_smtp_user": "your.smtp.user", "mail_smtp_user": "your.smtp.user",
"mail_smtp_pass": "your.secret.smtp.password" "mail_smtp_pass": "your.secret.smtp.password",
"spacedeck": {}
} }

0
database/.gitkeep Normal file
View File

View File

@@ -1,5 +1,4 @@
version: "2.0" version: "2.0"
services: services:
spacedeck: spacedeck:
build: . build: .
@@ -7,6 +6,5 @@ services:
ports: ports:
- "9666:9666" - "9666:9666"
volumes: volumes:
- /absolute/path/to/storage:/app/storage - ./storage:/app/storage
- /absolute/path/to/database.sqlite:/app/database.sqlite - ./database:/app/database

View File

@@ -12,7 +12,7 @@ server {
listen 80; listen 80;
listen [::]:80; listen [::]:80;
servername spacedeck.domain.de server_name spacedeck.domain.de
return 301 https://$server_name$request_uri; return 301 https://$server_name$request_uri;
} }

View File

@@ -50,6 +50,8 @@ const convertableAudioTypes = [
"audio/x-hx-aac-adts", "audio/x-hx-aac-adts",
"audio/aac"]; "audio/aac"];
// ffmpeg progress
var duration = 0, time = 0, progress = 0;
function getDuration(localFilePath, callback){ function getDuration(localFilePath, callback){
exec.execFile("ffprobe", ["-show_format", "-of", "json", localFilePath], function(error, stdout, stderr) { exec.execFile("ffprobe", ["-show_format", "-of", "json", localFilePath], function(error, stdout, stderr) {
@@ -58,6 +60,40 @@ function getDuration(localFilePath, callback){
}); });
} }
function getConversionProgress(content){
// get duration of source
var matches = (content) ? content.match(/Duration: (.*?), start:/) : [];
if( matches && matches.length>0 ){
var rawDuration = matches[1];
// convert rawDuration from 00:00:00.00 to seconds.
var ar = rawDuration.split(":").reverse();
duration = parseFloat(ar[0]);
if (ar[1]) duration += parseInt(ar[1]) * 60;
if (ar[2]) duration += parseInt(ar[2]) * 60 * 60;
}
// get the time
matches = content.match(/time=(.*?) bitrate/g);
if( matches && matches.length>0 ){
var rawTime = matches.pop();
// needed if there is more than one match
if (Array.isArray(rawTime)){
rawTime = rawTime.pop().replace('time=','').replace(' bitrate','');
} else {
rawTime = rawTime.replace('time=','').replace(' bitrate','');
}
// convert rawTime from 00:00:00.00 to seconds.
ar = rawTime.split(":").reverse();
time = parseFloat(ar[0]);
if (ar[1]) time += parseInt(ar[1]) * 60;
if (ar[2]) time += parseInt(ar[2]) * 60 * 60;
//calculate the progress
progress = Math.round((time/duration) * 100);
}
return progress;
}
function createWaveform(fileName, localFilePath, callback){ function createWaveform(fileName, localFilePath, callback){
var filePathImage = localFilePath + "-" + (new Date().getTime()) + ".png"; var filePathImage = localFilePath + "-" + (new Date().getTime()) + ".png";
@@ -135,7 +171,7 @@ function convertVideo(fileName, filePath, codec, callback, progressCallback) {
ff.stderr.on('data', function (data) { ff.stderr.on('data', function (data) {
console.log('[ffmpeg-video] stderr: ' + data); console.log('[ffmpeg-video] stderr: ' + data);
if (progressCallback) { if (progressCallback) {
progressCallback(data); progressCallback(getConversionProgress(""+data)+"%");
} }
}); });

58
helpers/exporter.js Normal file
View File

@@ -0,0 +1,58 @@
'use strict';
const db = require('../models/db');
const config = require('config');
const puppeteer = require('puppeteer');
const os = require('os');
module.exports = {
// type = "pdf" or "png"
takeScreenshot: function(space,type,on_success,on_error) {
var spaceId = space._id;
var space_url = config.get("endpoint")+"/api/spaces/"+spaceId+"/html";
var export_path = os.tmpdir()+"/"+spaceId+"."+type;
var timeout = 5000;
if (type=="pdf") timeout = 30000;
space_url += "?api_token="+config.get("export_api_secret");
console.log("[space-screenshot] url: "+space_url);
console.log("[space-screenshot] export_path: "+export_path);
(async () => {
let browser;
let page;
try {
browser = await puppeteer.launch(
{
headless: true,
args: ['--disable-dev-shm-usage', '--no-sandbox']
}
);
page = await browser.newPage();
page.setDefaultTimeout(timeout);
await page.setJavaScriptEnabled(false);
await page.goto(space_url, {waitUntil: 'networkidle2'});
await page.emulateMediaType('screen');
if (type=="pdf") {
let margin = 2;
await page.pdf({path: export_path, printBackground: true, width: space.width+margin+'px', height: space.height+margin+'px' });
}else{
await page.screenshot({path: export_path, printBackground: true});
}
await browser.close();
on_success(export_path);
} catch (error) {
console.error(error);
console.error("[space-screenshot] puppeteer abnormal exit for url "+space_url);
on_error();
}
})();
}
};

View File

@@ -1,70 +0,0 @@
'use strict';
const db = require('../models/db');
const config = require('config');
const phantom = require('node-phantom-simple');
const os = require('os');
module.exports = {
// type = "pdf" or "png"
takeScreenshot: function(space,type,on_success,on_error) {
var spaceId = space._id;
var space_url = config.get("endpoint")+"/api/spaces/"+spaceId+"/html";
var export_path = os.tmpdir()+"/"+spaceId+"."+type;
var timeout = 5000;
if (type=="pdf") timeout = 30000;
space_url += "?api_token="+config.get("phantom_api_secret");
console.log("[space-screenshot] url: "+space_url);
console.log("[space-screenshot] export_path: "+export_path);
var on_success_called = false;
var on_exit = function(exit_code) {
if (exit_code>0) {
console.error("phantom abnormal exit for url "+space_url);
if (!on_success_called && on_error) {
on_error();
}
}
};
phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) {
if (err) {
console.error(err);
} else {
return browser.createPage(function (err, page) {
console.log("page created, opening ",space_url);
if (type=="pdf") {
var psz = {
width: space.width+"px",
height: space.height+"px"
};
page.set('paperSize', psz);
}
page.set('settings.resourceTimeout',timeout);
page.set('settings.javascriptEnabled',false);
return page.open(space_url, function (err,status) {
page.render(export_path, function() {
on_success_called = true;
if (on_success) {
on_success(export_path);
}
page.close();
browser.exit();
});
});
});
}
}, {
onExit: on_exit
});
}
};

View File

@@ -320,5 +320,6 @@
"follow_present": "Folgen", "follow_present": "Folgen",
"mute_present": "Entfolgen", "mute_present": "Entfolgen",
"follow_present_help": "Wenn jemand den Space präsentiert, folgen die anderen Mitglieder automatisch der Präsentation. Mit diesem Knopf lässt sich das an- oder ausschalten.", "follow_present_help": "Wenn jemand den Space präsentiert, folgen die anderen Mitglieder automatisch der Präsentation. Mit diesem Knopf lässt sich das an- oder ausschalten.",
"media": "Media" "media": "Media",
} "tool_edit_text": "Text bearbeiten"
}

View File

@@ -322,6 +322,7 @@
"follow_present": "Follow", "follow_present": "Follow",
"mute_present": "Unfollow", "mute_present": "Unfollow",
"follow_present_help": "If someone else is presenting this Space, the other members automatically follow the presentation. Switch following on or off with this button.", "follow_present_help": "If someone else is presenting this Space, the other members automatically follow the presentation. Switch following on or off with this button.",
"export": "export", "export": "Export",
"media": "Media" "media": "Media",
} "tool_edit_text": "Edit Text"
}

328
locales/hu.js Normal file
View File

@@ -0,0 +1,328 @@
{
"ok": "Rendben",
"cancel": "Mégse",
"close": "Bezárás",
"open": "Megnyitás",
"folder": "Mappa",
"save": "Mentés",
"saved": "Mentve",
"created": "létrehozva",
"duplicate": "Másolás",
"delete": "Törlés",
"remove": "Eltávolítás",
"set": "készlet",
"reset": "alaphelyzet",
"thanks": "Köszönöm",
"share": "Megosztás",
"signup": "Feliratkozás",
"login": "Bejelentkezés",
"logout": "Kijelentkezés",
"email": "E-mail-cím",
"password": "Jelszó",
"width": "Szélesség",
"height": "Magasság",
"nick": "Név",
"role": "Szerepkör",
"members": "Tagok",
"actions": "Műveletek",
"or": "vagy",
"you": "Ön",
"via": "ezen keresztül:",
"by": "szerző:",
"zero": "Nulla",
"page": "Oldal:",
"new": "Új",
"copy": "Másolás",
"home": "Kezdőoldal",
"owner": "Tulajdonos",
"space": "Hely",
"second": "másodperc",
"not_found": "Nem található.",
"untitled_space": "Névtelen hely",
"untitled_folder": "Névtelen mappa",
"untitled": "névtelen",
"sure": "Biztos vagy benne?",
"specify": "Kérjük, részletezze",
"confirm": "Erősítse meg a műveletet",
"error_unknown_email": "Ez az e-mail/jelszó kombináció ismeretlen.",
"error_password_confirmation": "A megadott jelszavak nem egyeznek.",
"error_domain_blocked": "Tartománya letiltva.",
"error_user_email_already_used": "Ez az e-mail cím már használatban van.",
"support": "Spacedeck támogatás",
"offline": "Kapcsolat nélküli üzemmód. Kattintson további információkért.",
"error": "Sajnálom, de valami rosszul ment. Kérjük, lépjen kapcsolatba a support@spacedeck.com-al",
"welcome": "Üdvözöljük",
"claim": "A digitális rajztáblája.",
"trynow": "Próbálja most.",
"about": "Rólunk",
"terms": "Feltételek",
"contact": "Kapcsolat",
"privacy": "Adatvédelem",
"business_adress": "Üzleti cím",
"post_adress": "Posta cím",
"phone": "Telefon",
"ceo": "Ügyvezető igazgató",
"name": "Név",
"confirm_subject": "Spacedeck e-mail megerősítés",
"confirm_body": "Köszönjük, hogy feliratkozott a Spacedeck-re.\nKérjük, hogy kattintson az alábbi linkre az e-mail cím megerősítéséhez.\n",
"confirm_action": "Erősítse meg most",
"team_invite_membership_subject": "Csapat meghívó %-s számára",
"team_invite_membership_body": "Meghívták a(z) %s oldalra a Spacedeck-en. Kattintson a következő hivatkozásra a meghívás elfogadásához.",
"team_invite_user_body": "Meghívták a(z)%s oldalra a Spacedeck-en.\nAz ideiglenes jelszó \"%s\".\n Kérjük, kattintson a következő hivatkozásra a meghívás elfogadásához.",
"team_invite_admin_body": "%s meghívást kapott a csapatodba: %s. Az ideiglenes jelszó \"%s\".",
"team_invite_membership_acction": "Elfogad",
"team_new_member_subject": "A(z) %s új csapattagja feliratkozott",
"team_new_member_body": "%s most csatlakozott a(z) %s csoporthoz a Spacedeck-en.",
"space_invite_membership_subject": "%s meghívott %s helyre",
"space_invite_membership_body": "%s meghívta, hogy csatlakozzon a(z) %s helyre a Spacedecken. Kattintson a következő linkre a meghívás elfogadásához.",
"space_invite_membership_action": "Elfogad",
"folder_invite_membership_subject": "Hely",
"folder_invite_membership_body": "Meghívták a Spacedeck csoportjába. Kérjük, hogy kattintson a következő linkre a meghívás elfogadásához.",
"folder_invite_membership_acction": "Elfogad",
"login_google": "Bejelentkezés a Google-al",
"save_changes": "Változtatások mentése",
"upgrade": "Frissítés",
"upgrade_now": "Frissítés most",
"create_space": "Hely létrehozása",
"create_folder": "Mappa létrehozása",
"email_unconfirmed": "E-mail nincs megerősítve",
"confirmation_sent": "E-mail elküldve",
"folder_filter": "Szűrő",
"sort_by": "Rendezés",
"last_modified": "Utoljára módosítva",
"last_opened": "Utoljára nyitva",
"title": "Cím",
"edit_team": "Csapat szerkesztése",
"edit_account": "Fiók szerkesztése",
"log_out": "Kijelentkezés",
"no_spaces_yet": "Isten hozta! Itt létrehozhat helyeket és mappákat a bal felső sarokban található gombokkal.",
"new_folder_title": "Új cím a mappához",
"folder_settings": "Mappa beállításai",
"upload_cover_image": "Borító kép feltöltése",
"spacedeck_pro_ad_folders": "A Spacedeck Pro segítségével korlátlan mennyiségű helyet foglalhat el a mappákban és kezelheti az egyes mappák hozzáférési vezérléseit. Szeretne többet megtudni a Pro funkcióiról?",
"spacedeck_pro_ad_versions": "A Spacedeck Pro segítségével korlátlan verziókat menthet az egyes helyekről, hogy nyomon kövesse az előrehaladást vagy biztonságban tartsa a pillanatképeket. Szeretne többet megtudni a Pro funkcióiról?",
"spacedeck_pro_ad_pdf": "A Spacedeck Pro segítségével a helyeket éles PDF-fájlokként exportálhatja archiváláshoz, postázáshoz vagy nyomtatáshoz. Szeretne többet megtudni a Pro funkcióiról?",
"spacedeck_pro_ad_zip": "A Spacedeck Pro segítségével exportálhatja a hely tartalmát ZIP-csomagként. Szeretne többet megtudni a Pro funkcióiról?",
"spacedeck_pro_ad_colors": "A Spacedeck Pro segítségével professzionális színválasztóval keverheti össze saját színeit.",
"profile_caption": "Profil",
"upload_avatar": "Avatar feltöltése",
"uploading_avatar": "Avatar feltöltése…",
"avatar_dimensions": "Ajánlott méretek: 200 × 200 képpont.",
"profile_name": "Név",
"profile_email": "E-mail cím",
"send_again": "Küldje újra",
"confirmation_sent_long": "E-mail megerősítő hivatkozáds elküldve.Kérjük, ellenőrizze a beérkező leveleket.",
"confirmation_sent_another": "Egy másik megerősítő link elküldve.",
"confirmation_sent_dialog_text": "E-mailt küldtünk Önnek, amelyben elmagyaráztuk, hogyan erősítse meg e-mail címét.",
"payment_caption": "Fizetés",
"language_caption": "Nyelv",
"notifications_caption": "Értesítések",
"notifications_option_chat": "E-mailben értesítsen az új megjegyzésekről",
"notifications_option_spaces": "Küldjön napi összefoglalót arról, hogy mi történt a helyeim és mappáim között",
"password_caption": "Jelszó",
"current_password": "Jelenlegi jelszó",
"new_password": "Új jelszó",
"verify_password": "Jelszó megerősítése",
"change_password": "Jelszó módosítása",
"reset_password": "Jelszó visszaállítása",
"terminate_caption": "Fiók törlése",
"terminate_warning": "Ha törli a fiókját, akkor az összes hely, mappa és üzenet, beleértve az Ön összes tartalmát és a helyeiben létrehozott más embereket megsemmisül.",
"terminate_warning2": "Ezt nem lehet visszavonni.",
"terminate_reason": "Üzenet",
"terminate_reason_caption": "Segítsen nekünk a fejlesztésben, ha megosztja a lemondás okait.",
"terminate_terminate": "Leállít",
"space_blank1": "Üdvözöljük egy friss, új helyen!",
"space_blank2": "Húzza ide a fájlokat, hivatkozások beillesztése",
"space_blank3": "vagy használja az alábbi eszközöket",
"space_blank4": "hogy ezt a helyet tartalommal töltse meg.",
"draft": "Piszkozat",
"publish": "Közzététel",
"published": "Közzétett",
"save_version": "Verzió mentése",
"version_saved": "Verzió mentve",
"post": "Üzenet küldése",
"chat_invite_cta1": "Az együttműködés szórakoztató!",
"chat_invite_cta2": "Miért ne",
"chat_invite_cta3": "hívjon meg néhány embert",
"chat_invite_cta4": "Önnel dolgozni?",
"chat_message_placeholder": "Írja meg az üzenetét…",
"view": "Nézet",
"edit": "Szerkesztés",
"present": "Jelen van",
"chat": "Csevegés",
"meta": "Meta",
"tool_search": "Keresés",
"tool_upload": "Feltöltés",
"tool_text": "Szöveg",
"tool_shape": "Forma",
"tool_zones": "Zónák",
"tool_canvas": "Vászon",
"search_media": "Keresés a médiában…",
"type_here": "Írja ide",
"text_formats": "Formátumok",
"format_p": "Bekezdés",
"format_bullets": "Listajeles lista",
"format_numbers": "Számozott lista",
"format_h1": "1. címsor",
"format_h2": "2. címsor",
"format_h3": "3. címsor",
"font_size": "Betűméret",
"line_height": "Sormagasság",
"tool_align": "Igazítás",
"tool_styles": "Stílusok",
"tool_bullets": "Felsorolás",
"tool_numbers": "Számok",
"tool_font": "Betűtípus",
"color_fill": "Kitöltés",
"color_stroke": "Vonás",
"color_text": "Szöveg",
"tool_type": "Típus",
"tool_box": "Doboz",
"tool_link": "Hivatkozás",
"tool_layout": "Elrendezés",
"tool_options": "Lehetőségek",
"tool_stroke": "Vonás ",
"tool_delete": "Törlés",
"tool_lock": "Lezár",
"tool_copy": "Másol",
"stack": "Halom",
"tool_circle": "Kör",
"tool_hexagon": "Hatszög",
"tool_square": "Négyzet",
"tool_diamond": "Gyémánt",
"tool_bubble": "Buborék",
"tool_cloud": "Felhő",
"tool_burst": "Sorozatfelvétel",
"tool_star": "Csillag",
"tool_heart": "Szív",
"tool_scribble": "Irkál",
"tool_line": "Vonal",
"tool_arrow": "Nyíl",
"search_media_placeholder": "Keresés az internetes médiában…",
"add_zone": "Új zóna",
"palette": "Paletta",
"picker": "Választó",
"background_image_caption": "Kép",
"background_color_caption": "Szín",
"upload_background_caption": "Kattintson egy háttérkép feltöltéséhez",
"upload_background": "Háttér feltöltése",
"access_caption": "Hozzáférés",
"versions_caption": "Verziók",
"info_caption": "Információ",
"mode_private": "Magán: Csak a tagok tekinthetik meg és szerkeszthetik",
"mode_public": "Nyilvános: A hivatkozás birtokában bárki megtekintheti",
"invite_collaborators": "Hívjon meg együttműködőket",
"revoke_access": "Hozzáférés visszavonása",
"invite": "Meghívók küldése",
"invitee_email_address": "Az új tag e-mail címe",
"optional_message": "Választható üzenet",
"role_viewer": "Néző",
"role_editor": "Szerkesztő",
"role_admin": "Adminisztrátor",
"new_space_title": "Új cím a helynek",
"team": "Csoport",
"search": "Keresés",
"search_no_results": "keresés_nincsenek_eredmények",
"search_clear": "keresés_törlés",
"rename": "Átnevezés",
"mobile": "mobil",
"image": "kép",
"tool_filter": "szűrő",
"canel": "mégse",
"invite_membership_action": "meghívni_tagságot",
"viewer": "néző",
"editor": "szerkesztő",
"admin": "adminisztrátor ",
"logging_in": "bejelentkezés",
"password_confirmation": "Jelszó megerősítése",
"confirm_again": "E-mailt küldtünk Önnek, amelyben elmagyaráztuk, hogyan erősítse meg e-mail címét.",
"confirmed": "Fiókját sikeresen megerősítettük. Köszönjük.",
"signing_up": "Feliratkozás",
"password_check_inbox": "Kérjük, ellenőrizze a beérkező leveleket",
"new_space": "Új hely",
"tool_more": "Több",
"what_is_your_name": "Üdvözöljük a %s oldalon! Kérjük, hogy válasszon egy felhasználónevet.",
"lang": "hu",
"landing_title": "Rajztáblája az interneten.",
"landing_claim": "A Spacedeck segítségével könnyedén kombinálhat mindenféle típusú médiát a virtuális rajztáblákon: szöveges jegyzeteket, fotókat, internetes linkeket, sőt videókat és hangfelvételeket.",
"landing_example": "Az emberek a Spacedeck segítségével szervezik ötleteiket, csoportokban, hogy egy pillantással teljes projekteket láthassanak vagy iskolákban és egyetemeken gazdagabb, összekapcsolt tanulási tapasztalatokkal.",
"spaces": "Saját helyek",
"access_editor_link": "Azonnali hivatkozás szerkesztése",
"access_editor_link_desc": "Adja meg ezt a hivatkozást mindenkinek, akinek képesnek kell lennie a hely azonnali szerkesztésére, nincs szükség fiókra:",
"access_editor_link_desc_slug": "Ez a hivatkozás tartalmazza a hely nevét is.",
"access_anonymous_edit_blocking": "Névtelen szerkesztők csak a saját tételeiket változtathatják meg",
"access_current_members": "Current Members",
"access_new_members": "Új tagok meghívása",
"access_no_members": "A Hely tagjai itt jelennek meg.",
"comments": "hozzászólások",
"landing_customers": "Ezrek bíznak benne.",
"landing_features_title": "Gyerekjáték használni.",
"landing_features_text": "Az új Spacedeck 5 korszerű, gyönyörű felhasználói felülettel rendelkezik, amely minden eddiginél könnyebbé és szórakoztatóbbá teszi munkáját - miközben még erőteljesebb funkciókat kínál:",
"landing_features_1": "<b>Fogd és vidd</b> képeket, videókat és hangokat a számítógépről vagy az internetről",
"landing_features_2": "<b>Szöveg írása és formázása</b> de a betűtípusok, a színek és a stílus teljes ellenőrzésével",
"landing_features_3": "<b>Rajzoljon, kommentáljon és jelöljön ki</b> a mellékelt grafikus alakzatokkal",
"landing_features_4": "</b>Kapcsolja tábláját <b>nagyító prezentációvá</b>",
"landing_features_5": "<b>Együttműködés és csevegés</b> valós időben csoport társaival, diákjaival vagy barátaival. ",
"landing_features_6": "<b>Helyek megosztása</b> az interneten vagy e-mailben",
"landing_features_7": "<b>Exportálja munkáját</b> nyomtatható PDF vagy ZIP formátumban",
"landing_pricing": "Hihetetlenül megfizethető.",
"landing_pricing_lite": "Ingyenes/személyes használat",
"landing_pricing_lite_text": "Az alap, átfogó verzió a képek gyűjtésére és a jegyzetek vezetésére.",
"landing_pricing_pro_features_list": "<ul><li>Korlátlan helyek</li><li>Mappa struktúrák</li><li>PDF és ZIP exportálás</li><li>Nincs vízjel</li><li>Egyéni hátterek</li><li>Tevékenység előzmények</li><li>20 GB tárhely</li><ul>",
"landing_pricing_pro": "€4,90/Felhasználó/hónap. <br><small>or 49,90/Felhasználó /év</small>",
"landing_pricing_pro_text": "Turbófeltöltve minden elvárt erővel.",
"landing_pricing_pro_features": "Turbófeltöltve minden elvárt erővel.",
"welcome_subject": "Üdvözöljük a Spacedeck-en",
"welcome_body": "Üdvözlet!\nKöszönjük, hogy feliratkozott a Spacedeck webhelyre.<br>Reméljük, hogy élvezni fogja a Helyek szolgáltatásban való munkát.<br>Ne feledje, hogy fiókjában korlátlan számú munkatárs található. Nyugodtan ossza meg helyeit barátaival és kollégáival a világ minden tájáról.",
"invite_emails": "E-mail címek (vesszővel elválasztva)",
"history_recently_updated": "Mostanában frissítve",
"history_recently_empty": "Még nem történt semmi.",
"parent_folder": "szülő_mappa",
"created_by": "Készítette",
"last_updated": "Utolsó frissítés",
"feedback_sent": "Köszönjük a visszajelzését!",
"role_member": "Tag",
"team_invite_membership_action": "Meghívás elfogadása",
"space_message_subject": "Új üzenet a következő helyen % s",
"space_message_body": "%s ezt írta %s-ban: \n",
"pro_ad_history_headline": "A Spacedeck Pro verzióra való frissítéskor itt láthatja az összes (megosztott) hely legújabb frissítésének előzményeit.",
"password_reset_subject": "Spacedeck jelszavának visszaállítása",
"password_reset_body": "Ön kérte a Spacedeck jelszavának visszaállítását.\n Kérjük, hogy kattintson a következő hivatkozásra az új jelszó beállításához.",
"password_reset_action": "Visszaállítás most",
"was_offline": "A Spacedeck-hez való kapcsolat megszakadt. Ha nem mentett munkája van, kérjük, tartsa nyitva ezt a böngészőfület a kapcsolat helyreállításáig, majd érintse meg újra a nem mentett objektumokat.",
"subscription_failed_user_subject": "Probléma a Spacedeck fizetésével",
"subscription_failed_user_body": "Sajnos nem tudtuk feldolgozni a fizetési módját. A fiók beállításokban könnyedén létrehozhat egy új fizetési módot, beleértve a PayPalt is.",
"subscription_failed_team_subject": "Probléma a Spacedeck fizetésével",
"subscription_failed_team_body": "Sajnos nem tudtuk feldolgozni a fizetési módját a Team-fiókjához. Kérjük, javítsa ki fizetési módját a lehető leghamarabb.",
"team_name": "Csoport név",
"subdomain": "altartomány",
"team_adresses": "E-mail címek",
"add": "Hozzáadás",
"invited": "meghívott",
"duplicate_destination": "Melyik mappába kívánja másolni ezt a helyet?",
"duplicate_confirm": "A(z)% s másolat% s-ra?",
"duplicate_success": "% s másolva lett% s-ra.",
"goto_space": "Lépjen a(z)% s helyre",
"goto_folder": "Lépjen a(z)% s mappába",
"stay_here": "Maradjon itt",
"sharing": "Megosztás",
"list": "Lista exportálása",
"link": "Hivatkozás",
"download_space": "Hely letöltése ",
"download_as_pdf": "Hely letöltése PDF formátumban",
"type": "Típus",
"download": "Letöltés",
"Previous Zone": "Előző zóna",
"Next Zone": "Következő zóna",
"promote": "Népszerűsít",
"demote": "Lefokoz",
"more": "Több",
"lock": "Lezár",
"unlock": "Felold",
"follow_present": "Követ",
"mute_present": "Követés megszüntetése",
"follow_present_help": "Ha valaki más mutatja be ezt a helyet, a többi tag automatikusan követi az előadást. Ezzel a gombbal kapcsolhatja be vagy ki a követést.",
"export": "Exportál",
"media": "Média",
"tool_edit_text": "Szöveg szerkesztése"
}

View File

@@ -173,7 +173,7 @@
"tool_bullets": "Bullets", "tool_bullets": "Bullets",
"tool_numbers": "Nombres", "tool_numbers": "Nombres",
"color_fill": "Fill", "color_fill": "Fill",
"tool_font": "Font", "tool_font": "Poliça",
"color_stroke": "Traçat", "color_stroke": "Traçat",
"color_text": "Tèxte", "color_text": "Tèxte",
"tool_type": "Tipe", "tool_type": "Tipe",
@@ -244,7 +244,7 @@
"what_is_your_name": "La benvenguda a %s! Mercés de causir un escais-nom.", "what_is_your_name": "La benvenguda a %s! Mercés de causir un escais-nom.",
"lang": "en", "lang": "en",
"landing_title": "Vòstre tablèu blanc sul Web.", "landing_title": "Vòstre tablèu blanc sul Web.",
"landing_claim": "Spacedeck vos permet de facilament combinar quin que siá tipe de mèdias sus un tablèu virtual: tèxte, nòtas, ligams web, amai vidèos e enregistraments àudio. ", "landing_claim": "Spacedeck vos permet de facilament combinar quin tipe que siá de mèdias sus un tablèu virtual: tèxte, nòtas, ligams web, amai vidèos e enregistraments àudio. ",
"landing_example": "Lo monde utiliza Spacedeck per organizar lors idèas, en equipa per veire totes los projèctes en una ulhada, a lescòla e a luniversitat pels mai rics, experiéncia daprendissatge connectat.", "landing_example": "Lo monde utiliza Spacedeck per organizar lors idèas, en equipa per veire totes los projèctes en una ulhada, a lescòla e a luniversitat pels mai rics, experiéncia daprendissatge connectat.",
"spaces": "Mos espacis", "spaces": "Mos espacis",
"access_editor_link": "Ligam de modificacion dirècta", "access_editor_link": "Ligam de modificacion dirècta",
@@ -253,7 +253,7 @@
"access_anonymous_edit_blocking": "Los convidats pòdon pas modificar los elements quan creats.", "access_anonymous_edit_blocking": "Los convidats pòdon pas modificar los elements quan creats.",
"access_current_members": "Membres actuals", "access_current_members": "Membres actuals",
"access_new_members": "Convidar de novèls membres", "access_new_members": "Convidar de novèls membres",
"access_no_members": "Los membres daqueste Espacii apreissaràn aquí.", "access_no_members": "Los membres daqueste Espaci apareisseràn aquí.",
"comments": "comentaris", "comments": "comentaris",
"landing_customers": "La fisança de milièr de personas.", "landing_customers": "La fisança de milièr de personas.",
"landing_features_title": "Un jòc d'enfants dutilizar.", "landing_features_title": "Un jòc d'enfants dutilizar.",
@@ -309,7 +309,7 @@
"list": "lista", "list": "lista",
"link": "Ligam", "link": "Ligam",
"download_space": "Telecargar espaci", "download_space": "Telecargar espaci",
"download_space_as_pdf": "Telecargar espaci PDF", "download_space_as_pdf": "Telecargar espaci coma PDF",
"type": "Tipe", "type": "Tipe",
"download": "Telecargar", "download": "Telecargar",
"Previous Zone": "Zòna precedenta", "Previous Zone": "Zòna precedenta",
@@ -321,7 +321,7 @@
"unlock": "Desverrolhar", "unlock": "Desverrolhar",
"follow_present": "Seguir", "follow_present": "Seguir",
"mute_present": "Quitar de seguir", "mute_present": "Quitar de seguir",
"follow_present_help": "follow_present_help", "follow_present_help": "Se qualquun mai presenta aqueste espaci, los demai membres seguiràn automaticament la presentacion. Basculatz labonament a la presentacion amb aqueste boton.",
"export": "exportar", "export": "exportar",
"media": "Media" "media": "Mèdia"
} }

View File

@@ -1,22 +1,22 @@
'use strict'; "use strict";
const db = require('../models/db'); const db = require("../models/db");
var config = require('config'); var config = require("config");
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
// authentication via API token // authentication via API token
const api_token = req.headers["x-spacedeck-api-token"]; const api_token = req.headers["x-spacedeck-api-token"];
if (api_token && api_token.length>7) { if (api_token && api_token.length > 7) {
db.User.findOne({where: {api_token: api_token}}).then(user => { db.User.findOne({ where: { api_token: api_token } }).then((user) => {
req.user = user; if (user) {
next(); req.user = user;
}).error(err => { next();
res.status(403).json({ } else {
"error": "invalid_api-token" res.status(403).json({
}); error: "invalid_api-token",
next(); });
}
}); });
return; return;
@@ -26,28 +26,29 @@ module.exports = (req, res, next) => {
const token = req.cookies["sdsession"]; const token = req.cookies["sdsession"];
if (token && token != "null" && token != null) { if (token && token != "null" && token != null) {
db.Session.findOne({where: {token: token}}) db.Session.findOne({ where: { token: token } })
.then(session => { .then((session) => {
if (!session) { if (!session) {
// session not found // session not found
next(); next();
} } else
else db.User.findOne({where: {_id: session.user_id}}) db.User.findOne({ where: { _id: session.user_id } }).then((user) => {
.then(user => {
if (!user) { if (!user) {
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname; var domain =
res.clearCookie('sdsession', { domain: domain }); process.env.NODE_ENV == "production"
? new URL(config.get("endpoint")).hostname
: req.headers.hostname;
res.clearCookie("sdsession", { domain: domain });
if (req.accepts("text/html")) { if (req.accepts("text/html")) {
res.send("Please clear your cookies and try again."); res.send("Please clear your cookies and try again.");
} else if (req.accepts('application/json')) { } else if (req.accepts("application/json")) {
res.status(403).json({ res.status(403).json({
"error": "token_not_found" error: "token_not_found",
}); });
} else { } else {
res.send("Please clear your cookies and try again."); res.send("Please clear your cookies and try again.");
} }
} else { } else {
req["token"] = token; req["token"] = token;
req["user"] = user; req["user"] = user;
@@ -55,11 +56,11 @@ module.exports = (req, res, next) => {
} }
}); });
}) })
.error(err => { .error((err) => {
console.error("Session resolve error",err); console.error("Session resolve error", err);
next(); next();
}); });
} else { } else {
next(); next();
} }
} };

View File

@@ -86,7 +86,7 @@ module.exports = (req, res, next) => {
// space is private // space is private
// special permission for screenshot/pdf export from backend // special permission for screenshot/pdf export from backend
if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) { if (req.query['api_token'] && req.query['api_token'] == config.get('export_api_secret')) {
finalizeReq(space, "viewer"); finalizeReq(space, "viewer");
return; return;
} }

View File

@@ -6,24 +6,28 @@ function sequel_log(a,b,c) {
} }
const Sequelize = require('sequelize'); const Sequelize = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password', { const sequelize = new Sequelize(
host: 'localhost', config.get('storage_database'),
dialect: 'sqlite', config.get('storage_username'),
config.get('storage_password'),
pool: { {
max: 5, host: config.get('storage_host'),
min: 0, dialect: config.get('storage_dialect'),
acquire: 30000, pool: {
idle: 10000 max: 5,
}, min: 0,
acquire: 30000,
// SQLite only idle: 10000
storage: config.get('storage_local_db'), },
logging: sequel_log, logging: config.has('db_logs_disabled') ? false : sequel_log,
// http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
// http://docs.sequelizejs.com/manual/tutorial/querying.html#operators operatorsAliases: false,
operatorsAliases: false // SQLite only
}); storage: config.get('storage_local_db')
}
);
// https://github.com/sequelize/sequelize/issues/8019#issuecomment-384316346
Sequelize.postgres.DECIMAL.parse = function (value) { return parseFloat(value); };
var User; var User;
var Session; var Session;

7541
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,9 @@
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node spacedeck.js" "start": "node spacedeck.js",
"dev": "nodemon spacedeck.js",
"styles": "gulp styles"
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
@@ -13,6 +15,7 @@
"async": "2.3.0", "async": "2.3.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"chargebee": "2.6.5",
"cheerio": "0.22.0", "cheerio": "0.22.0",
"config": "1.25.1", "config": "1.25.1",
"cookie-parser": "~1.4.3", "cookie-parser": "~1.4.3",
@@ -22,33 +25,35 @@
"file-type": "^7.6.0", "file-type": "^7.6.0",
"glob": "7.1.1", "glob": "7.1.1",
"gm": "^1.23.1", "gm": "^1.23.1",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-sass": "^4.0.2",
"helmet": "^3.5.0", "helmet": "^3.5.0",
"i18n-2": "0.6.3", "i18n-2": "0.6.3",
"log-timestamp": "latest", "log-timestamp": "latest",
"mock-aws-s3": "^2.6.0", "mock-aws-s3": "^4.0.2",
"moment": "^2.19.3", "moment": "^2.19.3",
"morgan": "^1.9.1", "morgan": "^1.9.1",
"node-phantom-simple": "2.2.4", "nodemailer": "^6.6.0",
"node-server-screenshot": "^0.2.1", "puppeteer": "8.0.0",
"nodemailer": "^4.6.7",
"phantomjs-prebuilt": "^2.1.16",
"read-chunk": "^2.1.0", "read-chunk": "^2.1.0",
"request": "^2.88.0", "request": "^2.88.0",
"sanitize-html": "^1.11.1", "sanitize-html": "^2.3.3",
"sequelize": "^4.37.6", "sequelize": "^4.37.6",
"serve-favicon": "~2.4.2", "serve-favicon": "~2.4.2",
"serve-static": "^1.13.1", "serve-static": "^1.13.1",
"slug": "^1.1.0", "slug": "^1.1.0",
"sqlite3": "^4.0.0", "sqlite3": "^4.2.0",
"umzug": "^2.1.0", "umzug": "^2.1.0",
"underscore": "1.8.3", "underscore": "^1.13.1",
"uuid": "^3.2.1", "uuid": "^3.2.1",
"validator": "7.0.0", "validator": "7.0.0",
"ws": "3.3.1" "ws": "3.3.1"
}, },
"devDependencies": {
"gulp": "^4.0.2",
"gulp-clean-css": "^4.3.0",
"gulp-concat": "^2.6.1",
"gulp-sass": "^4.0.2",
"nodemon": "^2.0.6"
},
"main": "app.js", "main": "app.js",
"description": "", "description": "",
"directories": {}, "directories": {},

View File

@@ -8,6 +8,8 @@ var SpacedeckSections = {
data: { data: {
MAX_COLUMNS: 20, MAX_COLUMNS: 20,
isShift: false,
redo_stack: [], redo_stack: [],
undo_stack: [], undo_stack: [],
@@ -62,15 +64,15 @@ var SpacedeckSections = {
active_style: { active_style: {
border_radius: 0, border_radius: 0,
stroke: 0, stroke: 2,
font_family: "Inter", font_family: "Inter",
font_size: 36, font_size: 36,
line_height: 1.5, line_height: 1.5,
letter_spacing: 0, letter_spacing: 0,
stroke_color: "#000000", stroke_color: ENV.options.default_stroke_color ? ENV.options.default_stroke_color : "#000000",
fill_color: "#00000000", fill_color: ENV.options.default_fill_color ? ENV.options.default_fill_color : "#000000",
text_color: "#000000", text_color: ENV.options.default_text_color ? ENV.options.default_text_color : "#000000",
background_color: "#ffffff", background_color: "#ffffff",
padding: 0, padding: 0,
@@ -109,7 +111,7 @@ var SpacedeckSections = {
color_picker_hue: 127, color_picker_hue: 127,
color_picker_opacity: 255, color_picker_opacity: 255,
swatches: [ swatches: ENV.options.swatches ? ENV.options.swatches : [
{id:1, hex:"#ff00ff"}, {id:1, hex:"#ff00ff"},
{id:2, hex:"#ffff00"}, {id:2, hex:"#ffff00"},
{id:3, hex:"#00ffff"}, {id:3, hex:"#00ffff"},
@@ -133,18 +135,7 @@ var SpacedeckSections = {
{id:26, hex:"#d55c4b"}, {id:26, hex:"#d55c4b"},
{id:27, hex:"#6f4021"}, {id:27, hex:"#6f4021"},
{id:29, hex:"#95a5a6"}, {id:29, hex:"#95a5a6"},
{id:30, hex:"rgba(0,0,0,0)"}, {id:30, hex:"rgba(0,0,0,0)"}
],
swatches_text: [
{id:1, hex:"#9b59b6"},
{id:2, hex:"#3498db"},
{id:3, hex:"#2ecc71"},
{id:4, hex:"#f1c40f"},
{id:5, hex:"#e67e22"},
{id:6, hex:"#d55c4b"},
{id:8, hex:"#ffffff"},
{id:10, hex:"#252525"},
], ],
fonts: [ fonts: [
@@ -217,7 +208,9 @@ var SpacedeckSections = {
Mousetrap.bind('shift+left', function(evt) { this.if_editable(function() {this.nudge_selected_artifacts(-10,0,evt);}) }.bind(this)); Mousetrap.bind('shift+left', function(evt) { this.if_editable(function() {this.nudge_selected_artifacts(-10,0,evt);}) }.bind(this));
Mousetrap.bind('shift+right', function(evt) { this.if_editable(function() {this.nudge_selected_artifacts(10,0,evt);}) }.bind(this)); Mousetrap.bind('shift+right', function(evt) { this.if_editable(function() {this.nudge_selected_artifacts(10,0,evt);}) }.bind(this));
Mousetrap.bind('space', function(evt) { this.activate_pan_tool(evt); }.bind(this)); Mousetrap.bind('space', function(evt) { this.activate_pan_tool(evt); }.bind(this));
Mousetrap.bind(['shift'], function(evt) { this.isShift = true; }.bind(this), 'keydown');
Mousetrap.bind(['shift'], function(evt) { this.isShift = false; }.bind(this), 'keyup');
Mousetrap.bind('shift+up', function(evt) { this.if_editable(function() {this.nudge_selected_artifacts(0,-10,evt);}) }.bind(this));
$(document).bind("beforecopy", this.handle_onbeforecopy.bind(this)); $(document).bind("beforecopy", this.handle_onbeforecopy.bind(this));
$(window).bind("beforeunload", this.handle_onunload.bind(this)); $(window).bind("beforeunload", this.handle_onunload.bind(this));
$(window).bind("resize", this.handle_window_resize.bind(this)); $(window).bind("resize", this.handle_window_resize.bind(this));
@@ -1019,8 +1012,7 @@ var SpacedeckSections = {
}; };
}, },
update_selection_metrics: function(arts) { update_selection_metrics: function(arts, temporary) {
if (this.active_tool == "scribble") { if (this.active_tool == "scribble") {
this.selection_metrics.count = 1; this.selection_metrics.count = 1;
return; return;
@@ -1053,8 +1045,6 @@ var SpacedeckSections = {
// FIXME make sure that menus fit in window // FIXME make sure that menus fit in window
this.toolbar_props_x = pp.x+"px"; this.toolbar_props_x = pp.x+"px";
this.toolbar_props_y = pp.y+"px"; this.toolbar_props_y = pp.y+"px";
//this.hide_toolbar_artifacts();
} }
this.selection_metrics.x1 = sr.x1; this.selection_metrics.x1 = sr.x1;
@@ -1069,24 +1059,26 @@ var SpacedeckSections = {
if (!arts) arts = this.selected_artifacts(); if (!arts) arts = this.selected_artifacts();
this.first_selected_artifact = arts[0]; if (!temporary) {
this.selection_metrics.count=arts.length; this.first_selected_artifact = arts[0];
this.selection_metrics.scribble_selection = false; this.selection_metrics.count=arts.length;
if (arts.length == 1 && arts[0].mime == "x-spacedeck/vector") { this.selection_metrics.scribble_selection = false;
if (arts[0].shape == "scribble") { if (arts.length == 1 && arts[0].mime == "x-spacedeck/vector") {
this.selection_metrics.scribble_selection = true; if (arts[0].shape == "scribble") {
this.selection_metrics.scribble_selection = true;
}
this.selection_metrics.vector_points = arts[0].control_points;
this.selection_metrics.vector_selection = true;
} else {
this.selection_metrics.vector_points = [{},{}];
this.selection_metrics.vector_selection = false;
}
this.selection_metrics.has_link=false;
this.insert_link_url="";
if (arts.length == 1 && arts[0].meta && arts[0].meta.link_uri && arts[0].meta.link_uri.length>0) {
this.selection_metrics.has_link=true;
this.insert_link_url = arts[0].meta.link_uri;
} }
this.selection_metrics.vector_points = arts[0].control_points;
this.selection_metrics.vector_selection = true;
} else {
this.selection_metrics.vector_points = [{},{}];
this.selection_metrics.vector_selection = false;
}
this.selection_metrics.has_link=false;
this.insert_link_url="";
if (arts.length == 1 && arts[0].meta && arts[0].meta.link_uri && arts[0].meta.link_uri.length>0) {
this.selection_metrics.has_link=true;
this.insert_link_url = arts[0].meta.link_uri;
} }
}, },
@@ -1368,7 +1360,7 @@ var SpacedeckSections = {
}, },
reset_stroke: function() { reset_stroke: function() {
this.active_style.stroke = 0; this.active_style.stroke = 2;
this.active_style.border_radius = 0; this.active_style.border_radius = 0;
this.active_style.stroke_style = "solid"; this.active_style.stroke_style = "solid";
}, },
@@ -1436,13 +1428,13 @@ var SpacedeckSections = {
this.color_picker_rgb = rgb_to_hex(rgba.r,rgba.g,rgba.b); this.color_picker_rgb = rgb_to_hex(rgba.r,rgba.g,rgba.b);
}, },
update_selected_artifacts: function(change_func, override_locked) { update_selected_artifacts: function(change_func, override_locked, temporary) {
var artifacts = this.selected_artifacts(!override_locked); var artifacts = this.selected_artifacts(!override_locked);
if (!artifacts.length) return; if (!artifacts.length) return;
this.update_artifacts(artifacts, change_func); this.update_artifacts(artifacts, change_func);
this.update_selection_metrics(); this.update_selection_metrics(null, temporary||false);
}, },
nudge_selected_artifacts: function(dx, dy, event) { nudge_selected_artifacts: function(dx, dy, event) {
@@ -1726,7 +1718,7 @@ var SpacedeckSections = {
h: h, h: h,
stroke_color: this.active_style.stroke_color, stroke_color: this.active_style.stroke_color,
text_color: this.active_style.text_color, text_color: this.active_style.text_color,
stroke: 0, stroke: this.active_style.stroke,
fill_color: this.active_style.fill_color, fill_color: this.active_style.fill_color,
shape: shape_type, shape: shape_type,
valign: "middle", valign: "middle",
@@ -1923,10 +1915,7 @@ var SpacedeckSections = {
}.bind(this)); }.bind(this));
}, },
delayed_edit_artifact: function(evt) { delayed_edit_artifact: function() {
evt.stopPropagation();
evt.preventDefault();
var a = this.selected_artifacts()[0]; var a = this.selected_artifacts()[0];
var el = $("#ios-focuser-"+a._id); var el = $("#ios-focuser-"+a._id);
@@ -2087,8 +2076,6 @@ var SpacedeckSections = {
if (a.description!=dom.innerHTML) { if (a.description!=dom.innerHTML) {
a.description = dom.innerHTML; a.description = dom.innerHTML;
console.log("new DOM:",dom.innerHTML);
this.update_board_artifact_viewmodel(a); this.update_board_artifact_viewmodel(a);
this.queue_artifact_for_save(a); this.queue_artifact_for_save(a);
@@ -2309,17 +2296,19 @@ var SpacedeckSections = {
for (var i=0; i<parsed.length; i++) { for (var i=0; i<parsed.length; i++) {
if (parsed[i].mime) { if (parsed[i].mime) {
var z = this.highest_z()+1; var z = this.highest_z()+1;
if (parsed.length==1) { if(!this.isShift) {
var w = parsed[i].w; if (parsed.length==1) {
var h = parsed[i].h; var w = parsed[i].w;
var point = this.find_place_for_item(w,h); var h = parsed[i].h;
parsed[i].x = point.x; var point = this.find_place_for_item(w,h);
parsed[i].y = point.y; parsed[i].x = point.x;
parsed[i].z = point.z; parsed[i].y = point.y;
} else { parsed[i].z = point.z;
parsed[i].x = parsed[i].x+50; } else {
parsed[i].y = parsed[i].y+50; parsed[i].x = parsed[i].x+100;
parsed[i].y = parsed[i].z+z; parsed[i].y = parsed[i].y+100;
parsed[i].y = parsed[i].z+z;
}
} }
this.clone_artifact(parsed[i], 0,0, function(a) { this.clone_artifact(parsed[i], 0,0, function(a) {
this.multi_select([a]); this.multi_select([a]);

View File

@@ -99,7 +99,7 @@ var SpacedeckSpaces = {
}.bind(this), {value: dft || "Guest "+parseInt(10000*Math.random()), ok: __("ok"), cancel: __("cancel")}); }.bind(this), {value: dft || "Guest "+parseInt(10000*Math.random()), ok: __("ok"), cancel: __("cancel")});
}, },
load_space: function(space_id, on_success, on_error, space_auth) { load_space: function(space_id, on_success, on_error) {
this.folder_spaces_filter=""; this.folder_spaces_filter="";
this.folder_spaces_search=""; this.folder_spaces_search="";
@@ -308,7 +308,8 @@ var SpacedeckSpaces = {
userReady(); userReady();
} }
if (!this.user && space_auth) { if (!this.user.nickname && space_auth) {
this.guest_nickname = get_query_param("nickname") || this.guest_nickname;
if (this.guest_nickname) { if (this.guest_nickname) {
userReady(); userReady();
} else { } else {

View File

@@ -130,9 +130,27 @@ SpacedeckUsers = {
$event.stopPropagation(); $event.stopPropagation();
} }
create_user(name, email, password, password_confirmation, invite_code, function(session) { create_user(name, email, password, password_confirmation, invite_code, function(res) {
this.creating_user = false; this.creating_user = false;
this.login_submit(email, password, null, on_success);
if (res.chargebee_checkout) {
var chargebeeInstance = Chargebee.init({
site: "spacedeck-test",
enableRedirectMode: true,
enableFriendbuyTracking: false
});
chargebeeInstance.openCheckout({
hostedPage: function() {
return new Promise(function(resolve, reject) {
resolve(res.chargebee_checkout);
})
}
});
} else {
// user created, login
this.login_submit(email, password, null, on_success);
}
}.bind(this), function(req) { }.bind(this), function(req) {
this.creating_user = false; this.creating_user = false;
try { try {

View File

@@ -6,7 +6,7 @@
function setup_whiteboard_directives() { function setup_whiteboard_directives() {
var mode_touch = false; var mode_touch = false;
if ('ontouchstart' in window) { if ('ontouchstart' in window) {
mode_touch = true; mode_touch = true;
var edown = "touchstart"; var edown = "touchstart";
@@ -38,7 +38,7 @@ function setup_whiteboard_directives() {
$(el).bind("mousedown", this.handle_mouse_down_space.bind(this)); $(el).bind("mousedown", this.handle_mouse_down_space.bind(this));
$(el).bind("mousemove", this.handle_mouse_move.bind(this)); $(el).bind("mousemove", this.handle_mouse_move.bind(this));
$(el).bind("mouseup", this.handle_mouse_up_space.bind(this)); $(el).bind("mouseup", this.handle_mouse_up_space.bind(this));
$(el).bind("wheel", this.handle_wheel_space.bind(this)); $(el).bind("wheel", this.handle_wheel_space.bind(this));
$(document.body).bind("mouseleave", this.handle_mouse_leave.bind(this)); $(document.body).bind("mouseleave", this.handle_mouse_leave.bind(this));
@@ -99,7 +99,7 @@ function setup_whiteboard_directives() {
this.handle_mouse_down_space(evt); this.handle_mouse_down_space(evt);
return; return;
} }
if ($scope.active_tool == "note") { if ($scope.active_tool == "note") {
this.handle_mouse_down_space(evt, true); this.handle_mouse_down_space(evt, true);
return; return;
@@ -154,7 +154,7 @@ function setup_whiteboard_directives() {
var a = $scope.find_artifact_by_id(evt.currentTarget.id.replace("artifact-","")); var a = $scope.find_artifact_by_id(evt.currentTarget.id.replace("artifact-",""));
if (!a) return; if (!a) return;
if (a.payload_uri) { if (a.payload_uri) {
$scope.download_selected_artifacts(); $scope.download_selected_artifacts();
} }
@@ -200,10 +200,10 @@ function setup_whiteboard_directives() {
var $scope = this.vm.$root; var $scope = this.vm.$root;
if (!evt.ctrlKey && !evt.shiftKey) return; if (!evt.ctrlKey && !evt.shiftKey) return;
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
var amount = 1; var amount = 1;
var dy = evt.originalEvent.deltaY; var dy = evt.originalEvent.deltaY;
if (dy>0) { if (dy>0) {
@@ -222,7 +222,7 @@ function setup_whiteboard_directives() {
if (!force && evt.which != 2) { if (!force && evt.which != 2) {
if (evt.target != evt.currentTarget && !_.include(["wrapper"],evt.target.className)) return; if (evt.target != evt.currentTarget && !_.include(["wrapper"],evt.target.className)) return;
} }
var $scope = this.vm.$root; var $scope = this.vm.$root;
$scope.opened_dialog="none"; $scope.opened_dialog="none";
@@ -234,7 +234,7 @@ function setup_whiteboard_directives() {
if ((mode_touch && $scope.active_tool=="pointer") || evt.which == 2 || evt.buttons == 4) { if ((mode_touch && $scope.active_tool=="pointer") || evt.which == 2 || evt.buttons == 4) {
$scope.active_tool = "pan"; $scope.active_tool = "pan";
} }
if ($scope.active_tool=="note") { if ($scope.active_tool=="note") {
this.deselect(); this.deselect();
this.mouse_state = "transform"; this.mouse_state = "transform";
@@ -285,6 +285,7 @@ function setup_whiteboard_directives() {
$scope.start_adding_placeholder(evt); $scope.start_adding_placeholder(evt);
return; return;
} else if ($scope.active_tool=="pan") { } else if ($scope.active_tool=="pan") {
this.deselect();
this.start_pan(evt); this.start_pan(evt);
return; return;
} }
@@ -305,7 +306,7 @@ function setup_whiteboard_directives() {
this.old_panx = el.scrollLeft; this.old_panx = el.scrollLeft;
this.old_pany = el.scrollTop; this.old_pany = el.scrollTop;
} }
var cursor = this.cursor_point_to_space(evt); var cursor = this.cursor_point_to_space(evt);
$scope.mouse_ox = cursor.x; $scope.mouse_ox = cursor.x;
$scope.mouse_oy = cursor.y; $scope.mouse_oy = cursor.y;
@@ -314,7 +315,7 @@ function setup_whiteboard_directives() {
deselect: function() { deselect: function() {
var $scope = this.vm.$root; var $scope = this.vm.$root;
$scope.deselect(); $scope.deselect();
}, },
@@ -342,7 +343,7 @@ function setup_whiteboard_directives() {
rects_intersecting: function(r1,r2) { rects_intersecting: function(r1,r2) {
if (!r1 || !r2) return false; if (!r1 || !r2) return false;
if ( (r1.x+r1.w < r2.x) if ( (r1.x+r1.w < r2.x)
|| (r1.x > r2.x+r2.w) || (r1.x > r2.x+r2.w)
|| (r1.y+r1.h < r2.y) || (r1.y+r1.h < r2.y)
@@ -352,7 +353,7 @@ function setup_whiteboard_directives() {
artifacts_in_rect: function(rect) { artifacts_in_rect: function(rect) {
if (!rect) return []; if (!rect) return [];
var $scope = this.vm.$root; var $scope = this.vm.$root;
return _.filter($scope.active_space_artifacts, function(a) { return _.filter($scope.active_space_artifacts, function(a) {
@@ -688,7 +689,7 @@ function setup_whiteboard_directives() {
var $scope = this.vm.$root; var $scope = this.vm.$root;
evt.preventDefault(); evt.preventDefault();
if (this.mouse_state == "lasso") { if (this.mouse_state == "lasso") {
var lasso_rect = this.abs_rect(this.lasso); var lasso_rect = this.abs_rect(this.lasso);
@@ -696,7 +697,7 @@ function setup_whiteboard_directives() {
var arts = this.artifacts_in_rect(lasso_rect); var arts = this.artifacts_in_rect(lasso_rect);
this.multi_select(arts); this.multi_select(arts);
} else { } else {
if (this._no_artifact_toolbar_this_round) { if (this._no_artifact_toolbar_this_round) {
this._no_artifact_toolbar_this_round = false; this._no_artifact_toolbar_this_round = false;
} else { } else {
@@ -708,7 +709,7 @@ function setup_whiteboard_directives() {
} }
else if (_.include(["transform","move","vector_transform","scribble"],this.mouse_state)) { else if (_.include(["transform","move","vector_transform","scribble"],this.mouse_state)) {
var ars = $scope.selected_artifacts(); var ars = $scope.selected_artifacts();
for (var i=0; i<ars.length; i++) { for (var i=0; i<ars.length; i++) {
if (_.include(["text","placeholder"],$scope.artifact_major_type(ars[i]))) { if (_.include(["text","placeholder"],$scope.artifact_major_type(ars[i]))) {
@@ -723,6 +724,9 @@ function setup_whiteboard_directives() {
//save_artifact(ars[i], null, $scope.display_saving_error); //save_artifact(ars[i], null, $scope.display_saving_error);
} }
// update vector handles
$scope.update_selection_metrics();
} }
if (this.mouse_state == "text_editor") { if (this.mouse_state == "text_editor") {
@@ -794,7 +798,7 @@ function setup_whiteboard_directives() {
$scope.websocket_send(cursor_msg); $scope.websocket_send(cursor_msg);
} }
// side effects ftw! // side effects ftw!
$scope.snap_ruler_x = -1000; $scope.snap_ruler_x = -1000;
$scope.snap_ruler_y = -1000; $scope.snap_ruler_y = -1000;
@@ -818,7 +822,7 @@ function setup_whiteboard_directives() {
if (this.mouse_state == "move") { if (this.mouse_state == "move") {
$scope.hide_toolbar_props(); $scope.hide_toolbar_props();
var snap_dx = 0; var snap_dx = 0;
var snap_dy = 0; var snap_dy = 0;
@@ -878,7 +882,7 @@ function setup_whiteboard_directives() {
} }
$scope.hide_toolbar_props(); $scope.hide_toolbar_props();
var ew = (edges.x2-edges.x1); var ew = (edges.x2-edges.x1);
var eh = (edges.y2-edges.y1); var eh = (edges.y2-edges.y1);
@@ -924,7 +928,7 @@ function setup_whiteboard_directives() {
} else if (this.mouse_state == "vector_transform") { } else if (this.mouse_state == "vector_transform") {
$scope.hide_toolbar_props(); $scope.hide_toolbar_props();
var _this = this; var _this = this;
$scope.update_selected_artifacts(function(a) { $scope.update_selected_artifacts(function(a) {
var old_a = $scope.find_artifact_before_transaction(a); var old_a = $scope.find_artifact_before_transaction(a);
@@ -941,18 +945,14 @@ function setup_whiteboard_directives() {
// special case for arrow's 3rd point // special case for arrow's 3rd point
if (a.shape == "arrow" && $scope.selected_control_point_idx!=2) { if (a.shape == "arrow" && $scope.selected_control_point_idx!=2) {
/*control_points[2].dx += dx/2;
control_points[2].dy += dy/2; */
control_points[2].dx = (control_points[0].dx+control_points[1].dx)/2; control_points[2].dx = (control_points[0].dx+control_points[1].dx)/2;
control_points[2].dy = (control_points[0].dy+control_points[1].dy)/2; control_points[2].dy = (control_points[0].dy+control_points[1].dy)/2;
} }
return _this.normalize_control_points(control_points, old_a); return _this.normalize_control_points(control_points, old_a);
}); }, false, true); // override_locked: false, temporary: true
} else if (this.mouse_state == "scribble") { } else if (this.mouse_state == "scribble") {
$scope.update_selected_artifacts(function(a) { $scope.update_selected_artifacts(function(a) {
var old_a = a; var old_a = a;

View File

@@ -23,6 +23,9 @@ function vec2_angle(v) {
function render_vector_drawing(a, padding) { function render_vector_drawing(a, padding) {
var shape = a.shape || ""; var shape = a.shape || "";
var path = []; var path = [];
if(typeof a.control_points == 'string'){
a.control_points = JSON.parse(a.control_points);
}
var p = a.control_points[0]; var p = a.control_points[0];
if (!p) return ""; if (!p) return "";

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@ const db = require('../../models/db');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader'); var uploader = require('../../helpers/uploader');
var space_render = require('../../helpers/space-render'); var space_render = require('../../helpers/space-render');
var phantom = require('../../helpers/phantom'); var exporter = require('../../helpers/exporter');
var async = require('async'); var async = require('async');
var moment = require('moment'); var moment = require('moment');
@@ -51,7 +51,7 @@ router.get('/png', function(req, res, next) {
if (!req.space.thumbnail_updated_at || req.space.thumbnail_updated_at < req.space.updated_at || !req.space.thumbnail_url) { if (!req.space.thumbnail_updated_at || req.space.thumbnail_updated_at < req.space.updated_at || !req.space.thumbnail_url) {
db.Space.update({ thumbnail_updated_at: triggered }, {where : {"_id": req.space._id }}); db.Space.update({ thumbnail_updated_at: triggered }, {where : {"_id": req.space._id }});
phantom.takeScreenshot(req.space, "png", function(local_path) { exporter.takeScreenshot(req.space, "png", function(local_path) {
var localResizedFilePath = local_path + ".thumb.jpg"; var localResizedFilePath = local_path + ".thumb.jpg";
gm(local_path).resize(640, 480).quality(70.0).autoOrient().write(localResizedFilePath, function(err) { gm(local_path).resize(640, 480).quality(70.0).autoOrient().write(localResizedFilePath, function(err) {
@@ -109,7 +109,7 @@ function make_export_filename(space, extension) {
router.get('/pdf', function(req, res, next) { router.get('/pdf', function(req, res, next) {
var s3_filename = make_export_filename(req.space, "pdf"); var s3_filename = make_export_filename(req.space, "pdf");
phantom.takeScreenshot(req.space, "pdf", function(local_path) { exporter.takeScreenshot(req.space, "pdf", function(local_path) {
uploader.uploadFile(s3_filename, "application/pdf", local_path, function(err, url) { uploader.uploadFile(s3_filename, "application/pdf", local_path, function(err, url) {
res.status(201).json({ res.status(201).json({
url: url url: url

View File

@@ -9,7 +9,7 @@ var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader'); var uploader = require('../../helpers/uploader');
var space_render = require('../../helpers/space-render'); var space_render = require('../../helpers/space-render');
var phantom = require('../../helpers/phantom'); var exporter = require('../../helpers/exporter');
var async = require('async'); var async = require('async');
var fs = require('fs'); var fs = require('fs');

View File

@@ -10,7 +10,7 @@ var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader'); var uploader = require('../../helpers/uploader');
var space_render = require('../../helpers/space-render'); var space_render = require('../../helpers/space-render');
var phantom = require('../../helpers/phantom'); var exporter = require('../../helpers/exporter');
var payloadConverter = require('../../helpers/artifact_converter'); var payloadConverter = require('../../helpers/artifact_converter');
var slug = require('slug'); var slug = require('slug');

View File

@@ -23,6 +23,8 @@ var express = require('express');
var router = express.Router(); var router = express.Router();
var glob = require('glob'); var glob = require('glob');
var chargebee = require('chargebee');
router.get('/current', function(req, res, next) { router.get('/current', function(req, res, next) {
if (req.user) { if (req.user) {
var u = _.clone(req.user.dataValues); var u = _.clone(req.user.dataValues);
@@ -39,6 +41,88 @@ router.get('/current', function(req, res, next) {
} }
}); });
function createChargebeeCheckout(req, res, plan_id, email, name, company) {
var nameParts = name.split(" ");
var firstName = nameParts.shift();
var lastName = nameParts.join(" ");
chargebee.hosted_page.checkout_new({
subscription: {
plan_id: plan_id
},
customer: {
first_name: firstName,
last_name: lastName,
email: email,
company: company,
}
}).request(function(error,result) {
if (error) {
console.log(error);
res.status(400).json({"error":error+""});
} else {
res.status(200).json({"chargebee_checkout":result.hosted_page});
}
});
};
function createUser(req, res, email, nickname, password) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
crypto.randomBytes(16, function(ex, buf) {
var token = buf.toString('hex');
var u = {
_id: uuidv4(),
email: email,
account_type: "email",
nickname: nickname,
password_hash: hash,
prefs_language: req.i18n.locale,
confirmation_token: token
};
db.User.create(u)
.error(err => {
res.sendStatus(400);
})
.then(u => {
var homeFolder = {
_id: uuidv4(),
name: req.i18n.__("home"),
space_type: "folder",
creator_id: u._id
};
db.Space.create(homeFolder)
.error(err => {
res.sendStatus(400);
})
.then(homeFolder => {
u.home_folder_id = homeFolder._id;
u.save()
.then(() => {
// home folder created,
// auto accept pending invites
db.Membership.update({
"state": "active"
}, {
where: {
"email_invited": u.email,
"state": "pending"
}
});
res.status(201).json({});
})
.error(err => {
res.status(400).json(err);
});
})
});
});
});
});
};
// create user // create user
router.post('/', function(req, res) { router.post('/', function(req, res) {
if (!req.body["email"] || !req.body["password"]) { if (!req.body["email"] || !req.body["password"]) {
@@ -51,6 +135,7 @@ router.post('/', function(req, res) {
var password = req.body["password"]; var password = req.body["password"];
var password_confirmation = req.body["password_confirmation"]; var password_confirmation = req.body["password_confirmation"];
var invite_code = req.body["invite_code"]; var invite_code = req.body["invite_code"];
var company = req.body["company"] || "";
if (password_confirmation != password) { if (password_confirmation != password) {
res.status(400).json({"error":"password_confirmation"}); res.status(400).json({"error":"password_confirmation"});
@@ -67,67 +152,17 @@ router.post('/', function(req, res) {
return; return;
} }
var createUser = function() {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
crypto.randomBytes(16, function(ex, buf) {
var token = buf.toString('hex');
var u = {
_id: uuidv4(),
email: email,
account_type: "email",
nickname: nickname,
password_hash: hash,
prefs_language: req.i18n.locale,
confirmation_token: token
};
db.User.create(u)
.error(err => {
res.sendStatus(400);
})
.then(u => {
var homeFolder = {
_id: uuidv4(),
name: req.i18n.__("home"),
space_type: "folder",
creator_id: u._id
};
db.Space.create(homeFolder)
.error(err => {
res.sendStatus(400);
})
.then(homeFolder => {
u.home_folder_id = homeFolder._id;
u.save()
.then(() => {
// home folder created,
// auto accept pending invites
db.Membership.update({
"state": "active"
}, {
where: {
"email_invited": u.email,
"state": "pending"
}
});
res.status(201).json({});
})
.error(err => {
res.status(400).json(err);
});
})
});
});
});
});
};
db.User.findAll({where: {email: email}}) db.User.findAll({where: {email: email}})
.then(users => { .then(users => {
if (users.length == 0) { if (users.length == 0) {
createUser(); if (config.get('chargebee_integration')) {
// sign up via paid plan trial
createChargebeeCheckout(req, res, config.get('chargebee_default_plan_id'), email, nickname, company);
// TODO: createUser after chargebee checkout
}
else {
createUser(req, res, email, nickname, password);
}
} else { } else {
res.status(400).json({"error":"user_email_already_used"}); res.status(400).json({"error":"user_email_already_used"});
} }

View File

@@ -1,10 +1,10 @@
"use strict"; "use strict";
const puppeteer = require('puppeteer');
var config = require('config'); var config = require('config');
require('../../models/db'); require('../../models/db');
var fs = require('fs'); var fs = require('fs');
var phantom = require('node-phantom-simple');
var md5 = require('md5'); var md5 = require('md5');
var express = require('express'); var express = require('express');
@@ -19,40 +19,39 @@ function website_to_png(url,on_success,on_error) {
console.log("[webgrabber] url: "+url); console.log("[webgrabber] url: "+url);
console.log("[webgrabber] export_path: "+export_path); console.log("[webgrabber] export_path: "+export_path);
var on_success_called = false;
var on_exit = function(exit_code) {
if (exit_code>0) {
console.log("[phantom-webgrabber] abnormal exit for url "+url);
if (!on_success_called && on_error) {
on_error();
}
}
};
fs.stat(export_path, function(err, stat) { fs.stat(export_path, function(err, stat) {
if (!err) { if (!err) {
// file exists // file exists
console.log("[webgrabber] serving cached snapshot of url: "+url); console.log("[webgrabber] serving cached snapshot of url: "+url);
on_success(export_path); on_success(export_path);
} else { } else {
phantom.create({ path: require('phantomjs-prebuilt').path }, function (err, browser) { (async () => {
return browser.createPage(function (err, page) { let browser;
page.set('settings.resourceTimeout',timeout); let page;
page.set('settings.javascriptEnabled',false); try {
browser = await puppeteer.launch(
return page.open(url, function(err, status) { {
console.log("[webgrabber] status: "+status); headless: true,
page.render(export_path, function() { args: ['--disable-dev-shm-usage', '--no-sandbox']
on_success_called = true; }
on_success(export_path); );
browser.exit(); page = await browser.newPage();
});
}); page.setDefaultTimeout(timeout);
}); await page.setJavaScriptEnabled(false);
}, { await page.goto(url, {waitUntil: 'networkidle2'});
onExit: on_exit await page.emulateMedia('screen');
}); await page.screenshot({path: export_path, printBackground: true});
await browser.close();
on_success(export_path);
} catch (error) {
console.error(error);
console.log("[webgrabber] puppeteer abnormal exit for url "+url);
on_error();
}
})();
} }
}); });
} }

View File

@@ -78,6 +78,22 @@ router.get('/oc', (req, res) => {
res.redirect("/t/oc"); res.redirect("/t/oc");
}); });
router.get('/es/*', (req, res) => {
res.redirect("/t/es");
});
router.get('/es', (req, res) => {
res.redirect("/t/es");
});
router.get('/hu/*', (req, res) => {
res.redirect("/t/hu");
});
router.get('/hu', (req, res) => {
res.redirect("/t/hu");
});
router.get('/en/*', (req, res) => { router.get('/en/*', (req, res) => {
res.redirect("/t/en"); res.redirect("/t/en");
}); });
@@ -87,7 +103,7 @@ router.get('/en', (req, res) => {
}); });
router.get('/account', (req, res) => { router.get('/account', (req, res) => {
res.render('spacedeck'); res.render('spacedeck', { config:config });
}); });
router.get('/login', (req, res) => { router.get('/login', (req, res) => {

View File

@@ -23,6 +23,8 @@ const express = require('express');
const app = express(); const app = express();
const serveStatic = require('serve-static'); const serveStatic = require('serve-static');
const chargebee = require('chargebee');
const isProduction = app.get('env') === 'production'; const isProduction = app.get('env') === 'production';
// workaround for libssl_conf.so error triggered by phantomjs // workaround for libssl_conf.so error triggered by phantomjs
@@ -33,7 +35,7 @@ console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
app.use(logger(isProduction ? 'combined' : 'dev')); app.use(logger(isProduction ? 'combined' : 'dev'));
i18n.expressBind(app, { i18n.expressBind(app, {
locales: ["en", "de", "fr", "oc", "es"], locales: ["de", "en", "es", "fr", "hu", "oc"],
defaultLocale: "en", defaultLocale: "en",
cookieName: "spacedeck_locale", cookieName: "spacedeck_locale",
devMode: (app.get('env') == 'development') devMode: (app.get('env') == 'development')
@@ -110,6 +112,14 @@ if (app.get('env') == 'development') {
module.exports = app; module.exports = app;
// ChargeBee Integration (Optional)
if (config.get('chargebee_integration')) {
chargebee.configure({
site: config.get('chargebee_site'),
api_key: config.get('chargebee_api_key')
});
}
// CONNECT TO DATABASE // CONNECT TO DATABASE
db.init(); db.init();

View File

@@ -11,10 +11,12 @@
max-height: 100%; max-height: 100%;
} }
&.hide-text .text { opacity: 0 } &.hide-text .text {
opacity: 0;
}
&.locked.selected { &.locked.selected {
box-shadow: 0px 0px 0px 3px rgba(0,0,0,0.5) !important; box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.5) !important;
} }
.placeholder { .placeholder {
@@ -26,7 +28,9 @@
} }
} }
&.artifact-text.text-blank [contentEditable=true]:not(.text-editing) p:first-child::after { &.artifact-text.text-blank
[contentEditable="true"]:not(.text-editing)
p:first-child::after {
content: "Double click to edit"; content: "Double click to edit";
opacity: 0.25; opacity: 0.25;
} }
@@ -79,7 +83,6 @@
// padding-right: 20px; margin-right: -20px; // padding-right: 20px; margin-right: -20px;
//> * {pointer-events: auto; } //> * {pointer-events: auto; }
} }
&:not(.artifact-text) { &:not(.artifact-text) {
@@ -142,7 +145,9 @@
li { li {
margin-top: 0; margin-top: 0;
margin-bottom: 0.5em; margin-bottom: 0.5em;
&:last-child {margin-bottom: 0em !important; } &:last-child {
margin-bottom: 0em !important;
}
} }
h1 { h1 {
@@ -179,10 +184,14 @@
p { p {
margin-top: 0; margin-top: 0;
margin-bottom: 0.5em; // compatibility to old system margin-bottom: 0.5em; // compatibility to old system
&:last-child {margin-bottom: 0em !important; } &:last-child {
margin-bottom: 0em !important;
}
} }
a {pointer-events: none; } a {
pointer-events: none;
}
ol { ol {
padding: 0; padding: 0;
@@ -247,7 +256,7 @@
} }
.oembed, .oembed,
div[class*='oembed-'] { div[class*="oembed-"] {
height: 100%; height: 100%;
.play { .play {
position: absolute; position: absolute;
@@ -290,7 +299,9 @@
} }
} }
.title {font-size: 20px; } .title {
font-size: 20px;
}
.image { .image {
height: auto; height: auto;
top: 0; top: 0;
@@ -319,10 +330,11 @@
} }
} }
.video { .video {
width: 100%; width: 100%;
height: 100%; height: 100%;
background-size: cover; background-size: cover;
background-color: black;
&.playing { &.playing {
video { video {
@@ -347,6 +359,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
position: absolute; position: absolute;
left: 0;
} }
.tl-controls { .tl-controls {
@@ -359,10 +372,8 @@
.btn { .btn {
margin-top: 20px; margin-top: 20px;
} }
} }
} }
// FIXME: fix later // FIXME: fix later
@@ -381,8 +392,8 @@
height: 100%; height: 100%;
.timeline { .timeline {
position: absolute; position: absolute;
left:0; left: 0;
top:0; top: 0;
width: 100%; width: 100%;
bottom: 54px; bottom: 54px;
background-size: 100% 100%; background-size: 100% 100%;
@@ -391,7 +402,7 @@
overflow: hidden; overflow: hidden;
.tl-current-time { .tl-current-time {
height:100%; height: 100%;
background: white; background: white;
opacity: 0.5; opacity: 0.5;
border-right: 1px solid #888; border-right: 1px solid #888;
@@ -427,12 +438,11 @@
.btn { .btn {
margin-top: 20px; margin-top: 20px;
} }
} }
.tl-title { .tl-title {
margin-left:10px; margin-left: 10px;
max-width: 55%; max-width: 55%;
overflow: hidden; overflow: hidden;
display: inline-block; display: inline-block;
@@ -454,19 +464,41 @@
height: 100%; height: 100%;
} }
&.bold { font-weight: bold;} &.bold {
&.italic { font-style: italic;} font-weight: bold;
&.underline { text-decoration: underline;} }
&.strike { text-decoration: line-through;} &.italic {
font-style: italic;
}
&.underline {
text-decoration: underline;
}
&.strike {
text-decoration: line-through;
}
&.align-top .text-cell {vertical-align: top;} &.align-top .text-cell {
&.align-middle .text-cell {vertical-align: middle;} vertical-align: top;
&.align-bottom .text-cell {vertical-align: bottom;} }
&.align-middle .text-cell {
vertical-align: middle;
}
&.align-bottom .text-cell {
vertical-align: bottom;
}
&.align-left { text-align: left; } &.align-left {
&.align-center { text-align: center; } text-align: left;
&.align-right { text-align: right; } }
&.align-justify { text-align: justify; } &.align-center {
text-align: center;
}
&.align-right {
text-align: right;
}
&.align-justify {
text-align: justify;
}
audio { audio {
width: 100%; width: 100%;
@@ -481,10 +513,14 @@
font-size: 36px; font-size: 36px;
&.artifact-zone { &.artifact-zone {
background-color: rgba(0,0,0,0.05); background-color: rgba(0, 0, 0, 0.05);
border-radius: 10px; border-radius: 10px;
&:after {display: none; } &:after {
.shape {display: none; } display: none;
}
.shape {
display: none;
}
.zone { .zone {
height: 100%; height: 100%;
} }
@@ -496,32 +532,31 @@ body.present-mode {
.artifact-zone { .artifact-zone {
display: none; display: none;
} }
.artifact { .artifact {
cursor: default !important; cursor: default !important;
.text a { pointer-events: auto !important; } .text a {
pointer-events: auto !important;
}
} }
} }
} }
body:not(.present-mode) { body:not(.present-mode) {
#space { #space {
.Medium { .Medium {
cursor: text; cursor: text;
} }
.artifact { .artifact {
&.selected.text-editing, &.selected.text-editing,
&.text-editing { &.text-editing {
cursor: text; cursor: text;
&:before { &:before {
border: 1px solid rgba(255,255,255,0.25); border: 1px solid rgba(255, 255, 255, 0.25);
} }
&:after { &:after {
border: 1px dotted rgba(40,140,215,0.5); border: 1px dotted rgba(40, 140, 215, 0.5);
background-color: transparent !important; background-color: transparent !important;
} }
} }
@@ -535,24 +570,27 @@ body:not(.present-mode) {
} }
&:not(.artifact-vector):after { &:not(.artifact-vector):after {
border: 1px solid rgba(40,140,215,1); border: 1px solid rgba(40, 140, 215, 1);
// overlay color removed for now (messes with colors) // overlay color removed for now (messes with colors)
//background-color: rgba(40,140,215,0.35); //background-color: rgba(40,140,215,0.35);
} }
} }
} }
} }
} }
.mouse-scribble, .tool-scribble, .tool-line, .tool-arrow { .mouse-scribble,
.tool-scribble,
.tool-line,
.tool-arrow {
cursor: crosshair !important; cursor: crosshair !important;
.artifact { .artifact {
pointer-events: none !important; pointer-events: none !important;
} }
.artifact:after, .artifact:before { .artifact:after,
.artifact:before {
display: none !important; display: none !important;
} }
} }
@@ -566,21 +604,27 @@ body:not(.present-mode) {
} }
.artifact.state-idle { .artifact.state-idle {
.progress, .progress-text { .progress,
.progress-text {
display: none; display: none;
} }
} }
.artifact.state-processing, .artifact.state-uploading { .artifact.state-processing,
.artifact.state-uploading {
.progress { .progress {
height: 100%; height: 100%;
padding: 10px; padding: 4px;
background-color: $blue;
opacity: 0.9; opacity: 0.9;
text-align: center;
font-size: 14px; font-size: 14px;
} }
.progress-container {
background: $white;
width: 100%;
height: 100%;
padding-left: 10px;
padding-top: 10px;
}
.progress-text { .progress-text {
text-align: center; text-align: center;
padding: 8px; padding: 8px;
@@ -593,43 +637,48 @@ body:not(.present-mode) {
color: #888; color: #888;
font-size: 10px; font-size: 10px;
} }
video, audio, .tl-controls, .timeline { video,
audio,
.tl-controls,
.timeline {
display: none; display: none;
} }
background-color: white; background-color: white;
} }
.state-processing .spinner { .state-processing .spinner {
opacity: 1; opacity: 1;
background-image: url('/images/hourglass.gif'); background-image: url("/images/hourglass.gif");
} }
.state-uploading .spinner { .state-uploading .spinner {
opacity: 0.8; opacity: 0.8;
background-image: url('/images/hourglass.gif'); background-image: url("/images/hourglass.gif");
} }
.state-idle .spinner { .state-idle .spinner {
display: none; display: none;
} }
.artifact.image { .artifact.image {
background-color: transparent; background-color: transparent;
&.state-loading { background-color: rgba(40,140,215,0.05);} &.state-loading {
&.state-processing { background-color: rgba(107,195,111,0.05);} background-color: rgba(40, 140, 215, 0.05);
}
&.state-processing {
background-color: rgba(107, 195, 111, 0.05);
}
} }
.spinner { .spinner {
height:44px; height: 44px;
width:44px; width: 44px;
position:absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
margin: -22px; margin: -22px;
border-radius:100%; border-radius: 100%;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
} }

View File

@@ -3,12 +3,17 @@
@import "unicode"; @import "unicode";
@-webkit-keyframes appear { @-webkit-keyframes appear {
0% { opacity: 0;} 0% {
30% { opacity: 0;} opacity: 0;
100% { opacity: 1; } }
30% {
opacity: 0;
}
100% {
opacity: 1;
}
} }
.avatar { .avatar {
background-color: $blue; background-color: $blue;
font-family: $main-font; font-family: $main-font;
@@ -26,7 +31,9 @@
#user-root { #user-root {
margin-left: 5px !important; margin-left: 5px !important;
padding: 0px !important; padding: 0px !important;
.btn {margin-right: 10px; } .btn {
margin-right: 10px;
}
} }
#sidebar.folder-sidebar { #sidebar.folder-sidebar {
@@ -70,7 +77,9 @@
li { li {
display: inline-block; display: inline-block;
line-height: 44px; line-height: 44px;
&.active span {color: $light; } &.active span {
color: $light;
}
span { span {
color: $medium; color: $medium;
display: inline-block; display: inline-block;
@@ -82,8 +91,6 @@
} }
} }
#folder { #folder {
position: absolute; position: absolute;
height: 100%; height: 100%;
@@ -137,7 +144,7 @@
z-index: initial; z-index: initial;
} }
.folder .icon { .folder .icon {
color: $yellow; color: $yellow;
opacity: 1; opacity: 1;
} }
@@ -150,7 +157,7 @@
width: 100%; width: 100%;
} }
> .icon { > .icon {
margin-left: -15px margin-left: -15px;
} }
} }
@@ -176,30 +183,30 @@
// margin-top: -18px; // margin-top: -18px;
margin-right: -60px; margin-right: -60px;
// margin-bottom: -18px; // margin-bottom: -18px;
@include transition( all 0.125s ease-in-out); @include transition(all 0.125s ease-in-out);
.icon { .icon {
left: 0px; left: 0px;
top: 0px; top: 0px;
line-height: 60px; line-height: 60px;
font-size: 13px; font-size: 13px;
position: absolute; position: absolute;
@include scale(0,0); @include scale(0, 0);
@include transition( all 0.125s ease-in-out); @include transition(all 0.125s ease-in-out);
@include opacity(0.25); @include opacity(0.25);
} }
&:hover { &:hover {
margin-right: -12px; margin-right: -12px;
.icon { .icon {
@include scale(1,1); @include scale(1, 1);
} }
} }
} }
} }
#folder-header { #folder-header {
background-color: rgba(41,41,41,0.95); background-color: rgba(41, 41, 41, 0.95);
background-color: rgba(245,245,245,0.95); background-color: rgba(245, 245, 245, 0.95);
} }
#folder-wrapper { #folder-wrapper {
@@ -212,15 +219,22 @@
margin: auto; margin: auto;
} }
#folder-grid .item { #folder-grid .item {
float: left; float: left;
width: 100%; width: 100%;
@media screen and (min-width: 640px) {width: 50%; } @media screen and (min-width: 640px) {
@media screen and (min-width: 800px) {width: 33.333333%; } width: 50%;
@media screen and (min-width: 1000px) {width: 25%; } }
@media screen and (min-width: 1200px) {width: 20%; } @media screen and (min-width: 800px) {
width: 33.333333%;
}
@media screen and (min-width: 1000px) {
width: 25%;
}
@media screen and (min-width: 1200px) {
width: 20%;
}
} }
.compact-hidden { .compact-hidden {
@@ -256,8 +270,8 @@
.item { .item {
display: inline-block; display: inline-block;
text-align: left; text-align: left;
padding-right: $folder-gutter*2; padding-right: $folder-gutter * 2;
padding-bottom: $folder-gutter*2; padding-bottom: $folder-gutter * 2;
margin-bottom: $folder-gutter; margin-bottom: $folder-gutter;
position: relative; position: relative;
@@ -265,36 +279,38 @@
cursor: pointer; cursor: pointer;
.folder-drop {display:none; } .folder-drop {
display: none;
}
&.appear > a, &.appear > a,
&.creating > a { &.creating > a {
opacity: 0; opacity: 0;
-webkit-animation: appear 0.25s ease-out 0.05s; -webkit-animation: appear 0.25s ease-out 0.05s;
-moz-animation: appear 0.25s ease-out 0.05s; -moz-animation: appear 0.25s ease-out 0.05s;
-ms-animation: appear 0.25s ease-out 0.05s; -ms-animation: appear 0.25s ease-out 0.05s;
-o-animation: appear 0.25s ease-out 0.05s; -o-animation: appear 0.25s ease-out 0.05s;
animation: appear 0.25s ease-out 0.05s; animation: appear 0.25s ease-out 0.05s;
-webkit-animation-fill-mode: forwards; -webkit-animation-fill-mode: forwards;
-moz-animation-fill-mode: forwards; -moz-animation-fill-mode: forwards;
-ms-animation-fill-mode: forwards; -ms-animation-fill-mode: forwards;
-o-animation-fill-mode: forwards; -o-animation-fill-mode: forwards;
animation-fill-mode: forwards; animation-fill-mode: forwards;
} }
&.deleting > a { &.deleting > a {
-webkit-animation: disappear 0.25s ease-out 0.05s; -webkit-animation: disappear 0.25s ease-out 0.05s;
-moz-animation: disappear 0.25s ease-out 0.05s; -moz-animation: disappear 0.25s ease-out 0.05s;
-ms-animation: disappear 0.25s ease-out 0.05s; -ms-animation: disappear 0.25s ease-out 0.05s;
-o-animation: disappear 0.25s ease-out 0.05s; -o-animation: disappear 0.25s ease-out 0.05s;
animation: disappear 0.25s ease-out 0.05s; animation: disappear 0.25s ease-out 0.05s;
-webkit-animation-fill-mode: forwards; -webkit-animation-fill-mode: forwards;
-moz-animation-fill-mode: forwards; -moz-animation-fill-mode: forwards;
-ms-animation-fill-mode: forwards; -ms-animation-fill-mode: forwards;
-o-animation-fill-mode: forwards; -o-animation-fill-mode: forwards;
animation-fill-mode: forwards; animation-fill-mode: forwards;
} }
&.tag-important > a { &.tag-important > a {
@@ -308,7 +324,7 @@
&.dropping { &.dropping {
.folder-drop { .folder-drop {
display:block; display: block;
} }
} }
@@ -349,7 +365,13 @@
background-color: $darker; background-color: $darker;
position: absolute; position: absolute;
bottom: 2px; bottom: 2px;
-webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,0)), to(rgba(0,0,0,0.1))); -webkit-mask-image: -webkit-gradient(
linear,
left top,
left bottom,
from(rgba(0, 0, 0, 0)),
to(rgba(0, 0, 0, 0.1))
);
background-color: black; background-color: black;
pointer-events: none; pointer-events: none;
} }
@@ -360,7 +382,7 @@
.fav-toggle { .fav-toggle {
margin-right: -12px; margin-right: -12px;
.icon { .icon {
@include scale(1,1); @include scale(1, 1);
@include opacity(1); @include opacity(1);
} }
} }
@@ -384,7 +406,7 @@
> a { > a {
/* aspect ratio without spacer image */ /* aspect ratio without spacer image */
&:before{ &:before {
content: ""; content: "";
display: block; display: block;
padding-top: 100%; /* initial ratio of 1:1*/ padding-top: 100%; /* initial ratio of 1:1*/
@@ -392,9 +414,11 @@
} }
background-color: white; background-color: white;
border-radius: $radius*2; border-radius: $radius * 2;
&:active { opacity: 0.95 !important; } &:active {
opacity: 0.95 !important;
}
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15); box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
border: 1px solid black; border: 1px solid black;
@@ -406,7 +430,7 @@
font-weight: 400; font-weight: 400;
display: block; display: block;
text-decoration: none; text-decoration: none;
border-radius: $radius*2; border-radius: $radius * 2;
position: relative; position: relative;
.item-thumbnail { .item-thumbnail {
@@ -420,8 +444,9 @@
overflow: hidden; overflow: hidden;
background-color: transparent; background-color: transparent;
border-top-left-radius: $radius*2; background-size: cover;
border-top-right-radius: $radius*2; border-top-left-radius: $radius * 2;
border-top-right-radius: $radius * 2;
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;
@@ -431,7 +456,7 @@
.item-title { .item-title {
display: block; display: block;
border-top: 1px solid rgba(0,0,0,0.05); border-top: 1px solid rgba(0, 0, 0, 0.05);
background-color: white; background-color: white;
font-size: 18px; font-size: 18px;
width: 100%; width: 100%;
@@ -439,15 +464,15 @@
left: 0px; left: 0px;
bottom: 0px; bottom: 0px;
position: absolute; position: absolute;
border-bottom-left-radius: $radius*2; border-bottom-left-radius: $radius * 2;
border-bottom-right-radius: $radius*2; border-bottom-right-radius: $radius * 2;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
color: $dark; color: $dark;
text-align: left; text-align: left;
} }
.thumbnail-loading { .thumbnail-loading {
position: absolute; position: absolute;
z-index: 0; z-index: 0;
@@ -464,11 +489,11 @@
} }
.item-meta { .item-meta {
border-top: 2px solid rgba(0,0,0,0.025); border-top: 2px solid rgba(0, 0, 0, 0.025);
border-radius: 2*$radius; border-radius: 2 * $radius;
border-top-left-radius: 0; border-top-left-radius: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
@include clearfix; @include clearfix;
color: $medium; color: $medium;
position: absolute; position: absolute;
@@ -493,8 +518,8 @@
left: 0px; left: 0px;
bottom: 0px; bottom: 0px;
position: relative; position: relative;
border-bottom-left-radius: $radius*2; border-bottom-left-radius: $radius * 2;
border-bottom-right-radius: $radius*2; border-bottom-right-radius: $radius * 2;
//white-space: nowrap; //white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@@ -509,7 +534,7 @@
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: $folder-gutter*2; right: $folder-gutter * 2;
color: $medium; color: $medium;
font-size: 11px; font-size: 11px;
font-family: $main-font; font-family: $main-font;

View File

@@ -2,9 +2,8 @@
<div class="footer"> <div class="footer">
<p> <p>
<div class="col-xs-6"> <div class="col-xs-6">
&copy; 2020 <a href="https://mntre.com">MNT Research GmbH</a>, Fehlerstr. 8, 12161 Berlin, Germany<br> Spacedeck is Free and Open Source Software. The developers are not affiliated with this website.<br>
&copy; 20112020 Spacedeck GmbH (in liquidation)<br> &copy; 20112020 <a href="https://github.com/mntmn/spacedeck-open">Spacedeck Open Developers</a><br>
Source Code: <a href="https://github.com/mntmn/spacedeck-open">https://github.com/mntmn/spacedeck-open</a>
<br> <br>
Font: <a href="https://rsms.me/inter/">Inter by rsms</a> Font: <a href="https://rsms.me/inter/">Inter by rsms</a>
</div> </div>

View File

@@ -3,7 +3,12 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Spacedeck Open</title> <title>Spacedeck Open</title>
<meta name="description" content=""> <meta name="title" content="Spacedeck Open">
<meta name="description" content="Whenever you need to lay out pictures, text notes, video and audio clips on a blank canvas, Spacedeck can help you."/>
<meta name="keywords" content="spacedeck, collaboration, teams, collages, moodboards, teaching, shared whiteboards, design thinking"/>
<meta name="author" content="MNT Research GmbH"/>
<meta name="copyright" content="MNT Research GmbH"/>
<meta name="robots" content="index,follow"/>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
@@ -24,6 +29,5 @@
<a class="btn btn-md btn-dark btn-round" href="/spaces"><%=__("spaces")%></a> <a class="btn btn-md btn-dark btn-round" href="/spaces"><%=__("spaces")%></a>
<a class="btn btn-md btn-dark btn-round" href="/logout"><%=__("logout")%></a> <a class="btn btn-md btn-dark btn-round" href="/logout"><%=__("logout")%></a>
<% } %> <% } %>
</div> </div>
</header> </header>

View File

@@ -110,7 +110,10 @@
</label> </label>
<label class="radio" v-bind:class="{checked: user.prefs_language=='es'}" v-on:click="save_user_language('es')"> <label class="radio" v-bind:class="{checked: user.prefs_language=='es'}" v-on:click="save_user_language('es')">
<input type="radio" id="user-preferences_language" name="language" value="es"><span>Español</span> <input type="radio" id="user-preferences_language" name="language" value="es"><span>Español</span>
</label> </label>
<label class="radio" v-bind:class="{checked: user.prefs_language=='hu'}" v-on:click="save_user_language('hu')">
<input type="radio" id="user-preferences_language" name="language" value="hu"><span>Magyar</span>
</label>
</div> </div>
</div> </div>

View File

@@ -85,13 +85,13 @@
v-bind:class="{text-editing:(editing_artifact_id==a._id && (a.view.major_type=='text' || a.view.major_type=='shape'))}" v-bind:class="{text-editing:(editing_artifact_id==a._id && (a.view.major_type=='text' || a.view.major_type=='shape'))}"
id="artifact-{{a._id}}"> id="artifact-{{a._id}}">
<div v-if="a.view && a.view.major_type" style="height:100%; width:100%" v-bind:title="(a.editor_name || (a.user && a.user.nickname) || '')"> <div v-if="a.view && a.view.major_type" style="height:100%; width:100%" v-bind:title="(a.editor_name || (a.user && a.user.nickname) || '')">
<span v-if="a.locked && is_selected(a)" class="link-wrapper"> <span v-if="a.locked && is_selected(a)" class="link-wrapper">
<span class="btn btn-sm btn-icon btn-round btn-primary"> <span class="btn btn-sm btn-icon btn-round btn-primary">
<span class="icon icon-lock-closed"></span> <span class="icon icon-lock-closed"></span>
</span>
</span> </span>
</span>
<!-- text --> <!-- text -->
<div v-if="a.view.major_type == 'text'" class="text" v-bind:style="a.view.inner_style"> <div v-if="a.view.major_type == 'text'" class="text" v-bind:style="a.view.inner_style">
<div class="text-table"> <div class="text-table">
@@ -156,8 +156,13 @@
<source v-for="rep in a.view.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" /> <source v-for="rep in a.view.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" />
<source v-if="a.view.payload_uri && a.view.mime" v-bind:src="a.view.payload_uri" v-bind:type="a.view.mime" /> <source v-if="a.view.payload_uri && a.view.mime" v-bind:src="a.view.payload_uri" v-bind:type="a.view.mime" />
</video> </video>
<div class="spinner"></div> <div class="progress" v-bind:style="{width: a.description}">
<div class="progress" v-bind:style="{width: a.view.progress+'%'}">{{a.description}}</div> <div class="progress-container">
<h3>
{{a.description}}
</h3>
</div>
</div>
</div> </div>
<!-- audio --> <!-- audio -->

View File

@@ -4,7 +4,7 @@
<div class="dialog-section"> <div class="dialog-section">
<label class="btn btn-xxl btn-transparent btn-icon"> <label class="btn btn-xxl btn-transparent btn-icon">
<span class="icon icon-picture-upload"></span> <span class="icon icon-picture-upload"></span>
<input type="file" accept="*/*" multiple v-on:change="handle_image_file_upload($event)" id="image_file_upload"> <input type="file" multiple v-on:change="handle_image_file_upload($event)" id="image_file_upload">
</label> </label>
<p>Click to Upload<br/> or drag file(s) anywhere.</p> <p>Click to Upload<br/> or drag file(s) anywhere.</p>
</div> </div>

View File

@@ -1,18 +1,18 @@
<div class="toolbar toolbar-elements" v-bind:class="{in:toolbar_artifacts_in,out:!toolbar_artifacts_in}" v-show="!is_active_space_role('viewer') && active_space_loaded"> <div class="toolbar toolbar-elements" v-bind:class="{in:toolbar_artifacts_in,out:!toolbar_artifacts_in}" v-show="!is_active_space_role('viewer') && active_space_loaded">
<div class="btn-group light vertical"> <div class="btn-group light vertical">
<a class="btn btn-icon btn-transparent" <a class="btn btn-icon btn-transparent"
title="<%=__("home")%>" href="/spaces" title="<%=__("home")%>" href="/spaces"
v-if="(!active_space.parent_space_id && !guest_nickname && !embedded)"> v-if="(!active_space.parent_space_id && !guest_nickname && !embedded)">
<span class="icon icon-folder"></span> <span class="icon icon-folder"></span>
</a> </a>
<a class="btn btn-icon btn-dark" <a class="btn btn-icon btn-dark"
title="Parent Folder" title="Parent Folder"
href="/folders/{{active_space.parent_space_id}}" href="/folders/{{active_space.parent_space_id}}"
v-if="(active_space.parent_space_id && !guest_nickname && !embedded)"> v-if="(active_space.parent_space_id && !guest_nickname && !embedded)">
<span class="icon icon-sd6 icon-svg"></span> <span class="icon icon-sd6 icon-svg"></span>
</a> </a>
@@ -35,15 +35,16 @@
<span class="icon icon-tool-scribble"></span> <span class="icon icon-tool-scribble"></span>
<span class="icon-label"><%=__("tool_scribble")%></span> <span class="icon-label"><%=__("tool_scribble")%></span>
</button> </button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_arrow()" v-bind:class="{active:active_tool=='arrow'}" title="<%=__("tool_arrow")%>"> <button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_arrow()" v-bind:class="{active:active_tool=='arrow'}" title="<%=__("tool_arrow")%>">
<span class="icon icon-tool-arrow"></span> <span class="icon icon-tool-arrow"></span>
<span class="icon-label"><%=__("tool_arrow")%></span> <span class="icon-label"><%=__("tool_arrow")%></span>
</button> </button>
<div class="dropdown bottom light center"> <div class="dropdown bottom light center">
<div class="btn-collapse in"> <div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="handle_insert_image_url()" v-on:touchstart="handle_insert_image_url()" title="<%=__("media")%>"> <input type="file" multiple v-on:change="handle_image_file_upload($event)" id="image_file_upload" class="btn btn-transparent btn-icon-labeled" style="position: absolute; z-index: 1; opacity: 0;">
<button class="btn btn-transparent btn-icon-labeled" title="<%=__("media")%>">
<span class="icon icon-upload"></span> <span class="icon icon-upload"></span>
<span class="icon-label" ><%=__("media")%></span> <span class="icon-label" ><%=__("media")%></span>
</button> </button>
@@ -59,19 +60,6 @@
</div> </div>
</div> </div>
<div class="dropdown top left light">
<div class="btn-collapse">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='image'}" v-on:click="open_dialog('image')" title="<%=__("image")%>">
<span class="icon icon-picture"></span>
<span class="icon-label"><%=__("image")%></span>
</button>
</div>
<div class="dialog">
<%- include("./image.html") %>
</div>
</div>
<div class="dropdown top left light" v-bind:class="{open:opened_dialog=='zones'}"> <div class="dropdown top left light" v-bind:class="{open:opened_dialog=='zones'}">
<div class="btn-collapse in"> <div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='zones'}" v-on:click="open_dialog('zones')" title="<%=__("tool_zones")%>"> <button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='zones'}" v-on:click="open_dialog('zones')" title="<%=__("tool_zones")%>">
@@ -84,7 +72,7 @@
<%- include("./zones.html") %> <%- include("./zones.html") %>
</div> </div>
</div> </div>
<button class="btn btn-divider" v-show="logged_in"></button> <button class="btn btn-divider" v-show="logged_in"></button>
<div class="dropdown top left center" v-show="logged_in" v-bind:class="{open:opened_dialog=='background'}"> <div class="dropdown top left center" v-show="logged_in" v-bind:class="{open:opened_dialog=='background'}">
@@ -107,8 +95,8 @@
<span class="icon icon-share"></span> <span class="icon icon-share"></span>
<span class="icon-label"><%= __('share') %></span> <span class="icon-label"><%= __('share') %></span>
</button> </button>
<!-- <!--
<li v-on:click="edit_space_title()" v-if="logged_in"> <li v-on:click="edit_space_title()" v-if="logged_in">
<span> <span>
<span class="icon icon-sm icon-tag"></span> <span class="icon icon-sm icon-tag"></span>

View File

@@ -40,7 +40,7 @@
<button class="btn btn-divider"></button> <button class="btn btn-divider"></button>
--> -->
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='text-styles'}"> <div class="dropdown top light right" v-bind:class="{open:opened_dialog=='text-styles'}">
<div class="btn-collapse in"> <div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-styles')" v-bind:class="{open : opened_dialog=='text-styles'}" title="<%=__("tool_styles")%>"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-styles')" v-bind:class="{open : opened_dialog=='text-styles'}" title="<%=__("tool_styles")%>">
@@ -53,7 +53,7 @@
<%- include("./text-styles.html") %> <%- include("./text-styles.html") %>
</div> </div>
</div> </div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='type-align'}"> <div class="dropdown top light right" v-bind:class="{open:opened_dialog=='type-align'}">
<div class="btn-collapse" v-bind:class="{in:selection_metrics.contains_text}"> <div class="btn-collapse" v-bind:class="{in:selection_metrics.contains_text}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('type-align')" v-bind:class="{open : opened_dialog=='type-align'}" title="<%=__("tool_align")%>"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('type-align')" v-bind:class="{open : opened_dialog=='type-align'}" title="<%=__("tool_align")%>">
@@ -80,9 +80,9 @@
<%- include("./layout.html") %> <%- include("./layout.html") %>
</div> </div>
</div> </div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='text-settings'}"> <div class="dropdown top light right" v-bind:class="{open:opened_dialog=='text-settings'}">
<div class="btn-collapse in"> <div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-settings')" v-bind:class="{open : opened_dialog=='text-settings'}" title="<%=__("tool_font")%>"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-settings')" v-bind:class="{open : opened_dialog=='text-settings'}" title="<%=__("tool_font")%>">
<span class="icon icon-text-typeface"></span> <span class="icon icon-text-typeface"></span>
@@ -97,6 +97,17 @@
<button class="btn btn-divider"></button> <button class="btn btn-divider"></button>
<div class="dropdown bottom light center">
<div class="btn-collapse" v-bind:class="{in:selection_metrics.contains_text}">
<button
class="btn btn-icon-labeled btn-transparent"
v-on:click="delayed_edit_artifact()">
<span class="icon icon-pencil"></span>
<span class="icon-label"><%=__("tool_edit_text")%></span>
</button>
</div>
</div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='object-options'}"> <div class="dropdown top light right" v-bind:class="{open:opened_dialog=='object-options'}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('object-options')" v-bind:class="{open : opened_dialog=='object-options'}"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('object-options')" v-bind:class="{open : opened_dialog=='object-options'}">
<span class="icon icon-cogwheel"></span> <span class="icon icon-cogwheel"></span>
@@ -107,6 +118,6 @@
<%- include("./object-options.html") %> <%- include("./object-options.html") %>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -17,7 +17,8 @@
webHost: location.host, webHost: location.host,
webEndpoint: location.origin, webEndpoint: location.origin,
apiEndpoint: location.origin, apiEndpoint: location.origin,
websocketsEndpoint: location.origin.replace("https:","wss:").replace("http:","ws:") websocketsEndpoint: location.origin.replace("https:","wss:").replace("http:","ws:"),
options: <%- config && config.spacedeck ? JSON.stringify(config.spacedeck) : "{}" %>
}; };
</script> </script>
@@ -56,6 +57,9 @@
<script src="/javascripts/spacedeck_directives.js"></script> <script src="/javascripts/spacedeck_directives.js"></script>
<script src="/javascripts/spacedeck_vue.js"></script> <script src="/javascripts/spacedeck_vue.js"></script>
<!-- TODO: only include based on config -->
<script src="https://js.chargebee.com/v2/chargebee.js"></script>
<script>if (window.module) module = window.module;</script> <script>if (window.module) module = window.module;</script>
</head> </head>
@@ -73,15 +77,17 @@
<script type="text/javascript"> <script type="text/javascript">
window.locales = {}; window.locales = {};
window.locales.en = {};
window.locales.de = {}; window.locales.de = {};
window.locales.fr = {}; window.locales.en = {};
window.locales.oc = {};
window.locales.es = {}; window.locales.es = {};
window.locales.en.translation = <%- include("./../locales/en.js") %>; window.locales.fr = {};
window.locales.hu = {};
window.locales.oc = {};
window.locales.de.translation = <%- include("./../locales/de.js") %>; window.locales.de.translation = <%- include("./../locales/de.js") %>;
window.locales.fr.translation = <%- include("./../locales/fr.js") %>; window.locales.en.translation = <%- include("./../locales/en.js") %>;
window.locales.oc.translation = <%- include("./../locales/oc.js") %>;
window.locales.es.translation = <%- include("./../locales/es.js") %>; window.locales.es.translation = <%- include("./../locales/es.js") %>;
window.locales.fr.translation = <%- include("./../locales/fr.js") %>;
window.locales.hu.translation = <%- include("./../locales/hu.js") %>;
window.locales.oc.translation = <%- include("./../locales/oc.js") %>;
</script> </script>
</html> </html>