Compare commits

..

No commits in common. "whiteboard" and "kaleidos-issues/23/deleting-space-user-is-not-possible" have entirely different histories.

69 changed files with 2708 additions and 1442 deletions

2
.gitignore vendored
View File

@ -1,8 +1,6 @@
node_modules node_modules
javascripts/maps javascripts/maps
javascripts/spacedeck.js javascripts/spacedeck.js
public/stylesheets/*.css
database.sqlite
*.swp *.swp
*~ *~

View File

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

View File

@ -1,15 +1,15 @@
# Spacedeck Open # Spacedeck Open
![Spacedeck 6.0 Screenshot](/public/images/sd6-screenshot.png)
This is the free and open source version of Spacedeck, a web based, real time, collaborative whiteboard application with rich media support. Spacedeck was developed in 6 major releases during Autumn 2011 until the end of 2016 and was originally a commercial SaaS. The developers were Lukas F. Hartmann (mntmn) and Martin GĂĽther (magegu). This is the free and open source version of Spacedeck, a web based, real time, collaborative whiteboard application with rich media support. Spacedeck was developed in 6 major releases during Autumn 2011 until the end of 2016 and was originally a commercial SaaS. The developers were Lukas F. Hartmann (mntmn) and Martin GĂĽther (magegu).
The spacedeck.com online service was shut down on May 1st 2018. We decided to open-source Spacedeck to allow educational and other organizations who currently rely on Spacedeck to migrate to a self-hosted or local version. The spacedeck.com online service was shut down on May 1st 2018. We decided to open-source Spacedeck to allow educational and other organizations who currently rely on Spacedeck to migrate to a self-hosted or local version.
[MNT Research GmbH](https://mntre.com) has restarted development of Spacedeck Open in 2020.
We appreciate filed issues, pull requests and general discussion. We appreciate filed issues, pull requests and general discussion.
**Windows users:** Try the one-click release at https://github.com/spacedeck/spacedeck-open/releases/tag/v0.9
Desktop releases for Linux and Mac will be published here soon. In the meantime, you have to install Node.JS to run Spacedeck.
# Features # Features
- Create virtual whiteboards called *Spaces* with virtually unlimited size - Create virtual whiteboards called *Spaces* with virtually unlimited size
@ -17,23 +17,27 @@ We appreciate filed issues, pull requests and general discussion.
- 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
- Turn your Space into a zooming presentation - Turn your Space into a zooming presentation
- Collaborate in realtime with teammates, students or friends - Collaborate and chat in realtime with teammates, students or friends
- Share Spaces on the web or via email - Share Spaces on the web or via email
- Export your work as printable PDF or ZIP (currently being fixed, stay tuned) - Export your work as printable PDF or ZIP
# Use Cases # Data Import from Spacedeck.com
- Education: Virtual classwork with multimedia Spacedeck Open has a data import feature that you can use to migrate your ZIP export from Spacedeck.com.
- Creative: Mood boards, Brainstorming, Design Thinking
- Visual note taking and planning 1. Just copy your downloaded ZIP file into the spacedeck root folder. Don't extract it.
2. Start your local Spacedeck.
3. Navigate to *Account / Profile* (person icon in the top right corner).
4. Click the *Import* button next to the ZIP file name. It is on the bottom of the page.
5. Wait until console output has finished and you're done.
# Requirements, Installation # Requirements, Installation
Spacedeck requires: Spacedeck requires:
- Node.js 10.x: Web Server / API. Download: https://nodejs.org - Node.js 9.x: Web Server / API. Download: https://nodejs.org
To run Spacedeck, you only need Node.JS 10.x. To run Spacedeck, you only need Node.JS 9.x.
To install all node dependencies, run (do this once): To install all node dependencies, run (do this once):
@ -49,6 +53,10 @@ See [config/default.json](config/default.json)
Then open http://localhost:9666 in a web browser. Then open http://localhost:9666 in a web browser.
# Run (desktop app with integrated web server)
electron .
# Optional Dependencies # Optional Dependencies
For advanced media conversion: For advanced media conversion:

0
bin/www Normal file → Executable file
View File

View File

@ -1,11 +1,8 @@
{ {
"team_name": "My Open Spacedeck", //"endpoint": "http://localhost:9000",
"contact_email": "support@example.org",
"endpoint": "http://localhost:9666", "endpoint": "http://localhost:9666",
"invite_code": "", //disabled invite code by default
"storage_region": "eu-central-1", "storage_region": "eu-central-1",
//"storage_bucket": "sdeck-development", //"storage_bucket": "sdeck-development",
//"storage_cdn": "http://localhost:9123/sdeck-development", //"storage_cdn": "http://localhost:9123/sdeck-development",
//"storage_endpoint": "http://storage:9000", //"storage_endpoint": "http://storage:9000",
@ -21,14 +18,5 @@
"google_access" : "", "google_access" : "",
"google_secret" : "", "google_secret" : "",
"admin_pass": "very_secret_admin_password", "admin_pass": "very_secret_admin_password",
"phantom_api_secret": "very_secret_phantom_password", "phantom_api_secret": "very_secret_phantom_password"
// Choose "console" or "smtp"
"mail_provider": "smtp",
"mail_smtp_host": "your.smtp.host",
"mail_smtp_port": 465,
"mail_smtp_secure": true,
"mail_smtp_require_tls": true,
"mail_smtp_user": "your.smtp.user",
"mail_smtp_pass": "your.secret.smtp.password"
} }

View File

@ -1,18 +1,18 @@
'use strict'; 'use strict';
const config = require('config'); var swig = require('swig');
const nodemailer = require('nodemailer');
const swig = require('swig');
//var AWS = require('aws-sdk'); //var AWS = require('aws-sdk');
module.exports = { module.exports = {
sendMail: (to_email, subject, body, options) => { sendMail: (to_email, subject, body, options) => {
if (!options) { if (!options) {
options = {}; options = {};
} }
const teamname = options.teamname || config.get('team_name'); // FIXME
const from = teamname + ' <' + config.get('contact_email') + '>'; const teamname = options.teamname || "My Open Spacedeck"
const from = teamname + ' <support@example.org>';
let reply_to = [from]; let reply_to = [from];
if (options.reply_to) { if (options.reply_to) {
@ -29,38 +29,33 @@ module.exports = {
options: options options: options
}); });
if (config.get('mail_provider') === 'console') { //if (process.env.NODE_ENV === 'development') {
console.log("Email: to " + to_email + " in production.\nreply_to: " + reply_to + "\nsubject: " + subject + "\nbody: \n" + htmlText + "\n\n plaintext:\n" + plaintext); console.log("Email: to " + to_email + " in production.\nreply_to: " + reply_to + "\nsubject: " + subject + "\nbody: \n" + htmlText + "\n\n plaintext:\n" + plaintext);
/*} else {
AWS.config.update({region: 'eu-west-1'});
var ses = new AWS.SES();
} else if (config.get('mail_provider') === 'smtp') { ses.sendEmail( {
Source: from,
const transporter = nodemailer.createTransport({ Destination: { ToAddresses: [to_email] },
host: config.get('mail_smtp_host'), ReplyToAddresses: reply_to,
port: config.get('mail_smtp_port'), Message: {
secure: config.get('mail_smtp_secure'), Subject: {
requireTLS: config.get('mail_smtp_require_tls'), Data: subject
auth: { },
user: config.get('mail_smtp_user'), Body: {
pass: config.get('mail_smtp_pass'), Text: {
Data: plaintext,
},
Html: {
Data: htmlText
} }
}
}
}, function(err, data) {
if (err) console.error("Error sending email:", err);
else console.log("Email sent.");
}); });
}*/
transporter.sendMail({
from: from,
replyTo: reply_to,
to: to_email,
subject: subject,
text: plaintext,
html: htmlText,
}, function(err, info) {
if (err) {
console.error("Error sending email:", err);
} else {
console.log("Email sent.");
}
});
}
} }
}; };

View File

@ -57,6 +57,11 @@ module.exports = (req, res, next) => {
"_id": spaceId "_id": spaceId
}}).then(function(space) { }}).then(function(space) {
//.populate("creator", userMapping)
//if (err) {
// res.status(400).json(err);
//} else {
if (space) { if (space) {
if (space.access_mode == "public") { if (space.access_mode == "public") {
if (space.password) { if (space.password) {

View File

@ -91,8 +91,7 @@ module.exports = {
user_id: Sequelize.STRING, user_id: Sequelize.STRING,
role: Sequelize.STRING, role: Sequelize.STRING,
code: Sequelize.STRING, code: Sequelize.STRING,
state: {type: Sequelize.STRING, defaultValue: "pending"}, // valid: "pending", "active" state: {type: Sequelize.STRING, defaultValue: "pending"},
email_invited: Sequelize.STRING,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}, created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW} updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}), }),
@ -279,20 +278,21 @@ module.exports = {
getUserRoleInSpace: (originalSpace, user, cb) => { getUserRoleInSpace: (originalSpace, user, cb) => {
originalSpace.path = []; originalSpace.path = [];
console.log("getUserRoleInSpace",originalSpace._id,user._id,user.home_folder_id);
if (originalSpace._id == user.home_folder_id || (originalSpace.creator_id && originalSpace.creator_id == user._id)) { if (originalSpace._id == user.home_folder_id || (originalSpace.creator_id && originalSpace.creator_id == user._id)) {
cb("admin"); cb("admin");
} else { } else {
var findMembershipsForSpace = function(space, allMemberships, prevRole) { var findMembershipsForSpace = function(space, allMemberships, prevRole) {
Membership.findAll({ where: { Membership.findAll({ where: {
"space_id": space._id "space": space._id
}}).then(function(parentMemberships) { }}).then(function(parentMemberships) {
var currentMemberships = parentMemberships.concat(allMemberships); var currentMemberships = parentMemberships.concat(allMemberships);
if (space.parent_space_id) { if (space.parent_space_id) {
Space.findOne({ where: { Space.findOne({ where: {
"_id": space.parent_space_id "_id": space.parent_space_id
}}).then(function(parentSpace) { }}, function(err, parentSpace) {
findMembershipsForSpace(parentSpace, currentMemberships, prevRole); findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
}); });
} else { } else {

View File

@ -2,7 +2,7 @@
module.exports = { module.exports = {
up: function(migration, DataTypes) { up: function(migration, DataTypes) {
return Promise.all([ return [
migration.changeColumn('memberships', 'space_id', migration.changeColumn('memberships', 'space_id',
{ {
type: DataTypes.STRING, type: DataTypes.STRING,
@ -36,11 +36,11 @@ module.exports = {
onUpdate: 'CASCADE' onUpdate: 'CASCADE'
} }
) )
]) ]
}, },
down: function(migration, DataTypes) { down: function(migration, DataTypes) {
return Promise.all([ return [
migration.changeColumn('memberships', 'space_id', migration.changeColumn('memberships', 'space_id',
{ {
type: DataTypes.STRING, type: DataTypes.STRING,
@ -52,6 +52,7 @@ module.exports = {
onUpdate: 'NO ACTION' onUpdate: 'NO ACTION'
} }
), ),
,
migration.changeColumn('artifacts', 'space_id', migration.changeColumn('artifacts', 'space_id',
{ {
type: DataTypes.STRING, type: DataTypes.STRING,
@ -74,6 +75,6 @@ module.exports = {
onUpdate: 'NO ACTION' onUpdate: 'NO ACTION'
} }
) )
]) ]
} }
} };

View File

@ -3,52 +3,48 @@
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node spacedeck.js" "start": "electron ."
}, },
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=7.8.0"
}, },
"dependencies": { "dependencies": {
"archiver": "1.3.0", "archiver": "1.3.0",
"async": "2.3.0", "async": "2.3.0",
"basic-auth": "1.1.0", "basic-auth": "1.1.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "^1.19.0", "body-parser": "~1.17.1",
"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",
"electron": "^1.8.4",
"execSync": "latest", "execSync": "latest",
"express": "^4.16.4", "express": "~4.13.0",
"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.0",
"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": "^2.6.0",
"moment": "^2.19.3", "moment": "^2.19.3",
"morgan": "^1.9.1", "morgan": "1.8.1",
"node-phantom-simple": "2.2.4", "node-phantom-simple": "2.2.4",
"node-server-screenshot": "^0.2.1", "phantomjs-prebuilt": "2.1.14",
"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.81.0",
"sanitize-html": "^1.11.1", "sanitize-html": "^1.11.1",
"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": "0.9.1",
"sqlite3": "^4.0.0", "sqlite3": "^4.0.0",
"swig": "1.4.2", "swig": "1.4.2",
"umzug": "^2.1.0", "umzug": "^2.1.0",
"underscore": "1.8.3", "underscore": "1.8.3",
"uuid": "^3.2.1", "uuid": "^3.2.1",
"validator": "7.0.0", "validator": "7.0.0",
"ws": "3.3.1" "ws": "2.2.3"
}, },
"main": "app.js", "main": "app.js",
"description": "", "description": "",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="17.370329mm"
height="17.370247mm"
viewBox="0 0 17.370329 17.370247"
version="1.1"
id="svg3417"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="sd6-icon-white.svg"
inkscape:export-filename="/home/mntmn/code/spacedeck-open/public/images/favicon.png"
inkscape:export-xdpi="93.585312"
inkscape:export-ydpi="93.585312">
<defs
id="defs3411" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="68.901329"
inkscape:cy="26.613846"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata3414">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-61.618407,-79.672019)">
<path
inkscape:connector-curvature="0"
id="path1681-6-5-3-7-4-9-2-0-2-9-7"
d="m 69.103371,79.69206 c -0.792105,0.07526 -1.553632,0.368078 -2.179688,0.99414 -0.967242,0.967233 -1.023215,2.24006 -0.822265,3.46875 -1.228429,-0.200703 -2.499819,-0.144769 -3.466797,0.822266 -1.252082,1.252133 -1.178244,3.043412 -0.677734,4.544922 0.500509,1.50151 1.477937,2.995513 2.832031,4.349611 1.354102,1.3541 2.848091,2.33152 4.349609,2.83203 1.501518,0.50051 3.292795,0.57437 4.544922,-0.67773 0.9673,-0.96727 1.023249,-2.24001 0.822266,-3.468755 1.228416,0.200714 2.499803,0.146691 3.466796,-0.820313 1.252124,-1.252112 1.17824,-3.045353 0.677735,-4.546874 -0.500505,-1.501522 -1.477926,-2.995502 -2.832031,-4.34961 -1.354109,-1.354105 -2.848087,-2.329573 -4.34961,-2.830078 -0.750761,-0.250253 -1.57313,-0.393617 -2.365234,-0.318359 z m 0.251953,3.427734 c -0.06232,0.06232 0.187775,-0.12686 1.025391,0.152344 0.837615,0.279204 1.980359,0.976455 3.005859,2.001953 1.025498,1.0255 1.720796,2.16629 2,3.003906 0.279204,0.837616 0.09198,1.087707 0.154297,1.025391 0.06232,-0.06232 -0.187775,0.124907 -1.025391,-0.154297 -0.817005,-0.272334 -1.926016,-0.966798 -2.93164,-1.951172 -0.02107,-0.02133 -0.03343,-0.04515 -0.05469,-0.06641 -0.02194,-0.02194 -0.04635,-0.0349 -0.06836,-0.05664 -0.984356,-1.005615 -1.678841,-2.112692 -1.951172,-2.929687 -0.279204,-0.837616 -0.09198,-1.087708 -0.154297,-1.025391 z m -4.289063,4.289063 c -0.06231,0.06232 0.187774,-0.124903 1.025391,0.154296 0.81575,0.271911 1.923337,0.965368 2.927735,1.947266 0.02276,0.02306 0.03561,0.04929 0.05859,0.07227 0.023,0.023 0.04918,0.03581 0.07227,0.05859 0.981898,1.004395 1.67535,2.111982 1.947265,2.927735 0.279205,0.837619 0.09198,1.087705 0.154297,1.025385 0.06232,-0.0623 -0.187772,0.12492 -1.02539,-0.154291 -0.837619,-0.27921 -1.980364,-0.974504 -3.00586,-2 -1.025488,-1.025491 -1.720791,-2.168245 -2,-3.00586 -0.279208,-0.837615 -0.09198,-1.087708 -0.154297,-1.02539 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.4395833;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="17.370329mm"
height="17.370247mm"
viewBox="0 0 17.370329 17.370247"
version="1.1"
id="svg3417"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="sd6-icon.svg">
<defs
id="defs3411" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="68.901329"
inkscape:cy="26.613846"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata3414">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-61.618407,-79.672019)">
<path
inkscape:connector-curvature="0"
id="path1681-6-5-3-7-4-9-2-0-2-9-7"
d="m 69.103371,79.69206 c -0.792105,0.07526 -1.553632,0.368078 -2.179688,0.99414 -0.967242,0.967233 -1.023215,2.24006 -0.822265,3.46875 -1.228429,-0.200703 -2.499819,-0.144769 -3.466797,0.822266 -1.252082,1.252133 -1.178244,3.043412 -0.677734,4.544922 0.500509,1.50151 1.477937,2.995513 2.832031,4.349611 1.354102,1.3541 2.848091,2.33152 4.349609,2.83203 1.501518,0.50051 3.292795,0.57437 4.544922,-0.67773 0.9673,-0.96727 1.023249,-2.24001 0.822266,-3.468755 1.228416,0.200714 2.499803,0.146691 3.466796,-0.820313 1.252124,-1.252112 1.17824,-3.045353 0.677735,-4.546874 -0.500505,-1.501522 -1.477926,-2.995502 -2.832031,-4.34961 -1.354109,-1.354105 -2.848087,-2.329573 -4.34961,-2.830078 -0.750761,-0.250253 -1.57313,-0.393617 -2.365234,-0.318359 z m 0.251953,3.427734 c -0.06232,0.06232 0.187775,-0.12686 1.025391,0.152344 0.837615,0.279204 1.980359,0.976455 3.005859,2.001953 1.025498,1.0255 1.720796,2.16629 2,3.003906 0.279204,0.837616 0.09198,1.087707 0.154297,1.025391 0.06232,-0.06232 -0.187775,0.124907 -1.025391,-0.154297 -0.817005,-0.272334 -1.926016,-0.966798 -2.93164,-1.951172 -0.02107,-0.02133 -0.03343,-0.04515 -0.05469,-0.06641 -0.02194,-0.02194 -0.04635,-0.0349 -0.06836,-0.05664 -0.984356,-1.005615 -1.678841,-2.112692 -1.951172,-2.929687 -0.279204,-0.837616 -0.09198,-1.087708 -0.154297,-1.025391 z m -4.289063,4.289063 c -0.06231,0.06232 0.187774,-0.124903 1.025391,0.154296 0.81575,0.271911 1.923337,0.965368 2.927735,1.947266 0.02276,0.02306 0.03561,0.04929 0.05859,0.07227 0.023,0.023 0.04918,0.03581 0.07227,0.05859 0.981898,1.004395 1.67535,2.111982 1.947265,2.927735 0.279205,0.837619 0.09198,1.087705 0.154297,1.025385 0.06232,-0.0623 -0.187772,0.12492 -1.02539,-0.154291 -0.837619,-0.27921 -1.980364,-0.974504 -3.00586,-2 -1.025488,-1.025491 -1.720791,-2.168245 -2,-3.00586 -0.279208,-0.837615 -0.09198,-1.087708 -0.154297,-1.02539 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.4395833;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -1,129 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="81.731232mm"
height="17.370247mm"
viewBox="0 0 81.731232 17.370247"
version="1.1"
id="svg2651"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="sd6-logo-black.svg">
<defs
id="defs2645" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="80.852573"
inkscape:cy="-16.110417"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata2648">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-29.059958,-86.19285)">
<g
id="g3248">
<path
inkscape:connector-curvature="0"
id="path1681-6-5-3-7-4-9-2-0-2-9-7"
d="m 36.544922,86.212891 c -0.792105,0.07526 -1.553632,0.368078 -2.179688,0.99414 -0.967242,0.967233 -1.023215,2.24006 -0.822265,3.46875 -1.228429,-0.200703 -2.499819,-0.144769 -3.466797,0.822266 -1.252082,1.252133 -1.178244,3.043412 -0.677734,4.544922 0.500509,1.50151 1.477937,2.995513 2.832031,4.349611 1.354102,1.3541 2.848091,2.33152 4.349609,2.83203 1.501518,0.50051 3.292795,0.57437 4.544922,-0.67773 0.9673,-0.96727 1.023249,-2.24001 0.822266,-3.468755 1.228416,0.200714 2.499803,0.146691 3.466796,-0.820313 1.252124,-1.252112 1.17824,-3.045353 0.677735,-4.546874 -0.500505,-1.501522 -1.477926,-2.995502 -2.832031,-4.34961 -1.354109,-1.354105 -2.848087,-2.329573 -4.34961,-2.830078 -0.750761,-0.250253 -1.57313,-0.393617 -2.365234,-0.318359 z m 0.251953,3.427734 c -0.06232,0.06232 0.187775,-0.12686 1.025391,0.152344 0.837615,0.279204 1.980359,0.976455 3.005859,2.001953 1.025498,1.0255 1.720796,2.16629 2,3.003906 0.279204,0.837616 0.09198,1.087707 0.154297,1.025391 0.06232,-0.06232 -0.187775,0.124907 -1.025391,-0.154297 -0.817005,-0.272334 -1.926016,-0.966798 -2.93164,-1.951172 -0.02107,-0.02133 -0.03343,-0.04515 -0.05469,-0.06641 -0.02194,-0.02194 -0.04635,-0.0349 -0.06836,-0.05664 -0.984356,-1.005615 -1.678841,-2.112692 -1.951172,-2.929687 -0.279204,-0.837616 -0.09198,-1.087708 -0.154297,-1.025391 z m -4.289063,4.289063 c -0.06231,0.06232 0.187774,-0.124903 1.025391,0.154296 0.81575,0.271911 1.923337,0.965368 2.927735,1.947266 0.02276,0.02306 0.03561,0.04929 0.05859,0.07227 0.023,0.023 0.04918,0.03581 0.07227,0.05859 0.981898,1.004395 1.67535,2.111982 1.947265,2.927735 0.279205,0.837619 0.09198,1.087705 0.154297,1.025385 0.06232,-0.0623 -0.187772,0.12492 -1.02539,-0.154291 -0.837619,-0.27921 -1.980364,-0.974504 -3.00586,-2 -1.025488,-1.025491 -1.720791,-2.168245 -2,-3.00586 -0.279208,-0.837615 -0.09198,-1.087708 -0.154297,-1.02539 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.4395833;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
id="g2614"
transform="matrix(0.26458333,0,0,0.26458333,-523.78744,61.714265)">
<g
id="flowRoot1610-0-6-8-1-1"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;line-height:1.25;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.37800002;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1"
transform="matrix(2.6369365,0,0,2.6369365,2045.0224,86.079903)"
aria-label="Spacedeck">
<path
id="path3214"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 52.795627,11.002199 h 2.693247 C 55.466146,8.82601 53.73315,7.3543837 51.028539,7.3543837 c -2.659155,0 -4.573974,1.4488985 -4.556928,3.6137233 -0.0057,1.767088 1.232985,2.76143 3.244397,3.221669 l 1.215938,0.284097 c 1.27844,0.295462 1.852317,0.642061 1.863681,1.295486 -0.01136,0.710244 -0.676152,1.204575 -1.806861,1.204575 -1.244349,0 -2.06255,-0.57956 -2.125052,-1.698905 h -2.693246 c 0.03409,2.721656 1.926182,4.022824 4.852389,4.022824 2.897797,0 4.613748,-1.312532 4.625112,-3.522812 -0.01136,-1.857999 -1.267076,-2.99439 -3.562586,-3.500084 l -1.000024,-0.227278 c -1.056844,-0.227279 -1.727315,-0.57956 -1.704587,-1.272758 0.0057,-0.636379 0.55115,-1.0966177 1.642085,-1.0966177 1.096618,0 1.698905,0.4943297 1.77277,1.3238957 z"
inkscape:connector-curvature="0" />
<path
id="path3216"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 57.068458,22.422928 h 2.778476 v -4.687613 h 0.05682 c 0.352281,0.806838 1.136391,1.53981 2.454605,1.53981 1.931864,0 3.48872,-1.5114 3.48872,-4.483062 0,-3.07962 -1.647767,-4.483063 -3.471674,-4.483063 -1.380715,0 -2.136415,0.806838 -2.471651,1.607994 h -0.08523 v -1.494355 h -2.750066 z m 2.721656,-7.636547 c 0,-1.426171 0.590923,-2.306874 1.607993,-2.306874 1.028434,0 1.59663,0.903431 1.59663,2.306874 0,1.409125 -0.568196,2.323919 -1.59663,2.323919 -1.01707,0 -1.607993,-0.909112 -1.607993,-2.323919 z"
inkscape:connector-curvature="0" />
<path
id="path3218"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 69.778991,19.297853 c 1.215939,0 2.056868,-0.471602 2.534152,-1.363669 h 0.06818 v 1.215938 h 2.613699 v -5.931961 c 0,-1.846635 -1.642085,-2.909161 -3.86373,-2.909161 -2.346647,0 -3.676224,1.181847 -3.897821,2.772794 l 2.562562,0.09091 c 0.119321,-0.556832 0.579559,-0.897749 1.312532,-0.897749 0.681834,0 1.113663,0.329553 1.113663,0.914794 v 0.02841 c 0,0.534104 -0.57956,0.647743 -2.068232,0.778428 -1.767088,0.147731 -3.244396,0.801156 -3.244396,2.73302 0,1.727315 1.198892,2.568244 2.869387,2.568244 z m 0.857975,-1.818226 c -0.642061,0 -1.096617,-0.306825 -1.096617,-0.886384 0,-0.562514 0.443193,-0.903431 1.232984,-1.022752 0.517058,-0.07387 1.153437,-0.187505 1.465945,-0.352282 v 0.829566 c 0,0.852293 -0.715927,1.431852 -1.602312,1.431852 z"
inkscape:connector-curvature="0" />
<path
id="path3220"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 80.767893,19.314899 c 2.454605,0 3.977369,-1.426171 4.051234,-3.585314 h -2.596653 c -0.102275,0.926159 -0.659107,1.431853 -1.420489,1.431853 -0.977296,0 -1.613675,-0.823883 -1.613675,-2.375057 0,-1.53981 0.642061,-2.363693 1.613675,-2.363693 0.795474,0 1.312532,0.539785 1.420489,1.431852 h 2.596653 c -0.0625,-2.147779 -1.630721,-3.54554 -4.056916,-3.54554 -2.744384,0 -4.403515,1.82959 -4.403515,4.505791 0,2.664836 1.647767,4.500108 4.409197,4.500108 z"
inkscape:connector-curvature="0" />
<path
id="path3222"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 90.336304,19.314899 c 2.289828,0 3.795546,-1.107981 4.113736,-2.823932 l -2.551198,-0.07387 c -0.215914,0.579559 -0.78411,0.892067 -1.5114,0.892067 -1.068208,0 -1.727315,-0.710245 -1.727315,-1.778452 v -0.07386 h 5.818322 v -0.693199 c 0,-2.875069 -1.750042,-4.454653 -4.227374,-4.454653 -2.636427,0 -4.32965,1.806862 -4.32965,4.511473 0,2.795521 1.670495,4.494426 4.414879,4.494426 z m -1.676177,-5.471723 c 0.03977,-0.869339 0.727291,-1.528446 1.647767,-1.528446 0.914795,0 1.573902,0.636379 1.585266,1.528446 z"
inkscape:connector-curvature="0" />
<path
id="path3224"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 99.10072,19.275125 c 1.31821,0 2.10232,-0.732972 2.4546,-1.53981 h 0.0852 v 1.414807 h 2.75007 V 7.5134784 h -2.77848 v 4.4035156 h -0.0568 C 101.22577,11.115838 100.46439,10.309 99.089356,10.309 c -1.823907,0 -3.477356,1.403443 -3.477356,4.483063 0,2.971662 1.562537,4.483062 3.48872,4.483062 z m 0.96593,-2.164825 c -1.028432,0 -1.602309,-0.914794 -1.602309,-2.323919 0,-1.403443 0.568196,-2.306874 1.602309,-2.306874 1.01707,0 1.608,0.880703 1.608,2.306874 0,1.414807 -0.59661,2.323919 -1.608,2.323919 z"
inkscape:connector-curvature="0" />
<path
id="path3226"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 110.24303,19.314899 c 2.28983,0 3.79555,-1.107981 4.11374,-2.823932 l -2.5512,-0.07387 c -0.21591,0.579559 -0.78411,0.892067 -1.5114,0.892067 -1.06821,0 -1.72731,-0.710245 -1.72731,-1.778452 v -0.07386 h 5.81832 v -0.693199 c 0,-2.875069 -1.75004,-4.454653 -4.22737,-4.454653 -2.63643,0 -4.32965,1.806862 -4.32965,4.511473 0,2.795521 1.67049,4.494426 4.41487,4.494426 z m -1.67617,-5.471723 c 0.0398,-0.869339 0.72729,-1.528446 1.64777,-1.528446 0.91479,0 1.5739,0.636379 1.58526,1.528446 z"
inkscape:connector-curvature="0" />
<path
id="path3228"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 119.89384,19.314899 c 2.4546,0 3.97736,-1.426171 4.05123,-3.585314 h -2.59665 c -0.10228,0.926159 -0.65911,1.431853 -1.42049,1.431853 -0.9773,0 -1.61368,-0.823883 -1.61368,-2.375057 0,-1.53981 0.64206,-2.363693 1.61368,-2.363693 0.79547,0 1.31253,0.539785 1.42049,1.431852 h 2.59665 c -0.0625,-2.147779 -1.63072,-3.54554 -4.05692,-3.54554 -2.74438,0 -4.40351,1.82959 -4.40351,4.505791 0,2.664836 1.64777,4.500108 4.4092,4.500108 z"
inkscape:connector-curvature="0" />
<path
id="path3230"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-size:16.00038528px;font-family:Inter;-inkscape-font-specification:'Inter, Ultra-Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
d="m 125.3826,19.150122 h 2.77848 v -2.619381 l 0.56251,-0.681835 2.0796,3.301216 h 3.2103 l -3.22167,-4.926255 3.09667,-3.801228 h -3.1478 l -2.45461,3.125076 h -0.125 V 7.5134784 h -2.77848 z"
inkscape:connector-curvature="0" />
</g>
<path
d="m 2146.72,133.51812 a 23.030019,11.514995 45 0 1 -24.427,-8.1423 23.030019,11.514995 45 0 1 -8.1423,-24.427 23.030019,11.514995 45 0 1 24.427,8.1423 23.030019,11.514995 45 0 1 8.1423,24.427 z m -16.2137,16.2138 a 23.030019,11.514995 45 0 1 -24.427,-8.1424 23.030019,11.514995 45 0 1 -8.1424,-24.4269 23.030019,11.514995 45 0 1 24.4271,8.1422 23.030019,11.514995 45 0 1 8.1423,24.4271 z"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:37.79527664;stroke-opacity:1"
id="path1681-6-5-3-7-4-9-2-0-2-7-6"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

View File

@ -133,6 +133,18 @@ function load_spaces(id, is_home, on_success, on_error) {
}, on_error); }, on_error);
} }
function load_importables(user, on_success, on_error) {
load_resource("get", "/users/"+user._id+"/importables", null, on_success, on_error);
}
function import_zip(user, filename, on_success, on_error) {
load_resource("get", "/users/"+user._id+"/import?zip="+filename, null, on_success, on_error);
}
function load_writable_folders(on_success, on_error) {
load_resource("get", "/spaces?writablefolders=true", null, on_success, on_error);
}
function load_history(s, on_success, on_error) { function load_history(s, on_success, on_error) {
load_resource("get", "/spaces/"+ s._id +"/digest", null, on_success, on_error); load_resource("get", "/spaces/"+ s._id +"/digest", null, on_success, on_error);
} }
@ -178,10 +190,12 @@ function delete_space(s, on_success, on_error) {
load_resource("delete", "/spaces/"+s._id, null, on_success, on_error); load_resource("delete", "/spaces/"+s._id, null, on_success, on_error);
} }
function delete_artifact(a, on_success, on_error) { function delete_artifact(a, on_success, on_error) {
load_resource("delete", "/spaces/"+a.space_id+"/artifacts/"+a._id); load_resource("delete", "/spaces/"+a.space_id+"/artifacts/"+a._id);
} }
function duplicate_space(s, to_space_id, on_success, on_error) { function duplicate_space(s, to_space_id, on_success, on_error) {
var path = "/spaces/"+s._id+"/duplicate"; var path = "/spaces/"+s._id+"/duplicate";
if(to_space_id) { if(to_space_id) {
@ -260,8 +274,8 @@ function delete_user(u, password, on_success, on_error) {
load_resource("delete", "/users/"+u._id +"?password="+password,null,on_success,on_error); load_resource("delete", "/users/"+u._id +"?password="+password,null,on_success,on_error);
} }
function create_user(name, email, password, password_confirmation, invite_code, on_success, on_error) { function create_user(name, email, password, password_confirmation, on_success, on_error) {
load_resource("post", "/users", {email:email, nickname:name, password:password, password_confirmation: password_confirmation, invite_code: invite_code}, on_success, on_error); load_resource("post", "/users", {email:email, nickname:name, password:password, password_confirmation: password_confirmation}, on_success, on_error);
} }
function create_session(email, password, on_success, on_error) { function create_session(email, password, on_success, on_error) {

View File

@ -9,12 +9,19 @@ SpacedeckAccount = {
account_tab: 'invoices', account_tab: 'invoices',
password_change_error: null, password_change_error: null,
feedback_text: "", feedback_text: "",
importables: [], // spacedeck.com zip import files
}, },
methods: { methods: {
show_account: function() { show_account: function() {
this.activate_dropdown('account'); this.activate_dropdown('account');
}, },
start_zip_import: function(f) {
if (confirm("Your archive will be imported in the background. This can take a few minutes. You can continue using Spacedeck in the meantime.")) {
import_zip(this.user, f);
}
},
account_save_user_digest: function(val) { account_save_user_digest: function(val) {
this.user.prefs_email_digest = val; this.user.prefs_email_digest = val;
this.save_user(function() { this.save_user(function() {

View File

@ -63,8 +63,8 @@ var SpacedeckSections = {
active_style: { active_style: {
border_radius: 0, border_radius: 0,
stroke: 0, stroke: 0,
font_family: "Inter", font_family: "Avenir W01",
font_size: 36, font_size: 18,
line_height: 1.5, line_height: 1.5,
letter_spacing: 0, letter_spacing: 0,
@ -110,30 +110,18 @@ var SpacedeckSections = {
color_picker_opacity: 255, color_picker_opacity: 255,
swatches: [ swatches: [
{id:1, hex:"#ff00ff"}, {id:0, hex:"#4a2f7e"},
{id:2, hex:"#ffff00"}, {id:1, hex:"#9b59b6"},
{id:3, hex:"#00ffff"}, {id:2, hex:"#3498db"},
{id:5, hex:"#ff0000"}, {id:3, hex:"#2ecc71"},
{id:6, hex:"#00ff00"}, {id:4, hex:"#f1c40f"},
{id:7, hex:"#0000ff"}, {id:5, hex:"#e67e22"},
{id:8, hex:"#000000"}, {id:6, hex:"#d55c4b"},
{id:9, hex:"#222222"}, {id:7, hex:"#6f4021"},
{id:10, hex:"#444444"}, {id:8, hex:"#ffffff"},
{id:11, hex:"#888888"}, {id:9, hex:"#95a5a6"},
{id:12, hex:"#bbbbbb"}, {id:10, hex:"#252525"},
{id:13, hex:"#dddddd"}, {id:11, hex:"rgba(0,0,0,0)"},
{id:14, hex:"#ffffff"},
{id:20, hex:"#4a2f7e"},
{id:21, hex:"#9b59b6"},
{id:22, hex:"#3498db"},
{id:23, hex:"#2ecc71"},
{id:24, hex:"#f1c40f"},
{id:25, hex:"#e67e22"},
{id:26, hex:"#d55c4b"},
{id:27, hex:"#6f4021"},
{id:29, hex:"#95a5a6"},
{id:30, hex:"rgba(0,0,0,0)"},
], ],
swatches_text: [ swatches_text: [
@ -148,8 +136,18 @@ var SpacedeckSections = {
], ],
fonts: [ fonts: [
"Inter", "Arial",
"Courier" "Courier",
"Georgia",
"Verdana",
"Comic Sans MS",
"Montserrat",
"Lato",
"Roboto",
"Crimson Text",
"EB Garamond",
"Vollkorn",
"Avenir W01"
], ],
detected_text_formats: {}, detected_text_formats: {},
@ -182,7 +180,7 @@ var SpacedeckSections = {
toolbar_props_in: false, toolbar_props_in: false,
toolbar_artifacts_x: "-1000px", toolbar_artifacts_x: "-1000px",
toolbar_artifacts_y: "-1000px", toolbar_artifacts_y: "-1000px",
toolbar_artifacts_in: true toolbar_artifacts_in: false
}, },
methods: { methods: {
@ -1059,7 +1057,7 @@ var SpacedeckSections = {
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.hide_toolbar_artifacts();
} }
this.selection_metrics.x1 = sr.x1; this.selection_metrics.x1 = sr.x1;
@ -1127,11 +1125,8 @@ var SpacedeckSections = {
var er = this.enclosing_rect(this.active_space_artifacts); var er = this.enclosing_rect(this.active_space_artifacts);
if (!er) return; if (!er) return;
// resize space this.active_space.width =Math.max(er.x2+100, window.innerWidth);
this.active_space.width =Math.max((parseInt(er.x2/window.innerWidth)+2)*window.innerWidth, window.innerWidth); this.active_space.height=Math.max(er.y2+100, window.innerHeight);
this.active_space.height=Math.max((parseInt(er.y2/window.innerHeight)+2)*window.innerHeight, window.innerHeight);
console.log("bounds: ",this.active_space.width,this.active_space.height);
if (this._last_bounds_width != this.active_space.width || if (this._last_bounds_width != this.active_space.width ||
this._last_bounds_height != this.active_space.height) { this._last_bounds_height != this.active_space.height) {
@ -1549,7 +1544,7 @@ var SpacedeckSections = {
add_artifact: function (space, item_type, url, evt) { add_artifact: function (space, item_type, url, evt) {
this.active_tool = "pointer"; this.active_tool = "pointer";
this.mouse_state = "idle"; this.mouse_state = "idle";
//this.hide_toolbar_artifacts(); this.hide_toolbar_artifacts();
if (!url && (item_type == 'image' || item_type == 'video' || item_type == 'embed')) { if (!url && (item_type == 'image' || item_type == 'video' || item_type == 'embed')) {
url = prompt("URL?"); url = prompt("URL?");
@ -1729,7 +1724,7 @@ var SpacedeckSections = {
var a = { var a = {
space_id: this.active_space._id, space_id: this.active_space._id,
mime: "x-spacedeck/shape", mime: "x-spacedeck/shape",
description: "", description: "Text",
x: point.x, x: point.x,
y: point.y, y: point.y,
z: point.z, z: point.z,
@ -1741,7 +1736,7 @@ var SpacedeckSections = {
fill_color: "#000000", fill_color: "#000000",
shape: shape_type, shape: shape_type,
valign: "middle", valign: "middle",
align: "center", align: "center"
}; };
if (this.guest_nickname) { if (this.guest_nickname) {
@ -1794,6 +1789,8 @@ var SpacedeckSections = {
return false; return false;
} }
this.hide_toolbar_artifacts();
// 1. create placeholder artifact // 1. create placeholder artifact
var w=300,h=150; var w=300,h=150;
var fill="transparent"; var fill="transparent";
@ -2296,6 +2293,11 @@ var SpacedeckSections = {
if (!pastedText) return; if (!pastedText) return;
if (!pastedText.match(/<[a-zA-Z]+>/g)) {
// crappy heuristic if this is actually HTML
pastedText = pastedText.replace(/\n/g,"<br>");
}
this.insert_embedded_artifact(pastedText); this.insert_embedded_artifact(pastedText);
}, },
@ -2342,6 +2344,32 @@ var SpacedeckSections = {
this.create_artifact_via_embed_url(text); this.create_artifact_via_embed_url(text);
return; return;
} }
var new_item = {
mime: "text/html",
description: text.replace("\n", "<br />"),
title: "",
space_id: space._id
};
var w = 400;
var h = 300;
var point = this.find_place_for_item(w,h);
new_item.x = point.x;
new_item.y = point.y;
new_item.w = w;
new_item.h = h;
new_item.z = point.z;
if (this.guest_nickname) {
new_item.editor_name = this.guest_nickname;
}
save_artifact(new_item, function(saved_item) {
this.update_board_artifact_viewmodel(saved_item);
this.active_space_artifacts.push(saved_item);
}.bind(this));
}, },
create_artifact_via_embed_url: function(url) { create_artifact_via_embed_url: function(url) {
@ -2500,11 +2528,20 @@ var SpacedeckSections = {
this.opened_dialog = "none"; this.opened_dialog = "none";
if (files && files.length) { if (files && files.length) {
console.log("file: ",files[0]);
for (var i=0; i<files.length; i++) { for (var i=0; i<files.length; i++) {
var file = files[i]; var file = files[i];
if (file.type === "application/pdf") {
var point = {x: 100, y: 100}; //fixme, center upload?
this.dropped_point = point;
this.pending_pdf_file = file;
this.activate_modal('pdfoptions');
} else {
this.create_artifact_via_upload(null, file, true); this.create_artifact_via_upload(null, file, true);
} }
} }
}
}, },
handle_image_file_upload: function(evt) { handle_image_file_upload: function(evt) {
@ -2541,11 +2578,12 @@ var SpacedeckSections = {
}, },
hide_toolbar_props: function() { hide_toolbar_props: function() {
// FIXME test this.toolbar_props_in = false;
//this.toolbar_props_in = false;
}, },
show_toolbar_artifacts: function(x,y) { show_toolbar_artifacts: function(x,y) {
this.toolbar_artifacts_x = (x-175)+"px";
this.toolbar_artifacts_y = y+"px";
this.toolbar_artifacts_in = true; this.toolbar_artifacts_in = true;
}, },
@ -2555,19 +2593,29 @@ var SpacedeckSections = {
start_adding_artifact: function(evt) { start_adding_artifact: function(evt) {
evt = fixup_touches(evt); evt = fixup_touches(evt);
// toggle
if (this.toolbar_artifacts_in) {
this.hide_toolbar_artifacts();
return;
}
this.show_toolbar_artifacts(evt.pageX,evt.pageY);
}, },
start_drawing_scribble: function(evt) { start_drawing_scribble: function(evt) {
this.hide_toolbar_artifacts();
this.active_tool = "scribble"; this.active_tool = "scribble";
this.opened_dialog = "none"; this.opened_dialog = "none";
}, },
start_drawing_arrow: function(evt) { start_drawing_arrow: function(evt) {
this.hide_toolbar_artifacts();
this.active_tool = "arrow"; this.active_tool = "arrow";
this.opened_dialog = "none"; this.opened_dialog = "none";
}, },
start_drawing_line: function(evt) { start_drawing_line: function(evt) {
this.hide_toolbar_artifacts();
this.active_tool = "line"; this.active_tool = "line";
this.opened_dialog = "none"; this.opened_dialog = "none";
}, },
@ -2846,6 +2894,32 @@ var SpacedeckSections = {
}.bind(this),500); }.bind(this),500);
}, },
approve_pdf_upload: function(evt,approve_pdf_upload, mode){
this.close_modal();
if(mode == "classic"){
this.create_artifact_via_upload(evt, this.pending_pdf_file, false);
}
if(mode == "grid") {
this.global_spinner = true;
save_pdf_file(this.active_space, this.dropped_point, this.pending_pdf_file, approve_pdf_upload, function(createdArtifacts){
this.global_spinner = false;
_.each(createdArtifacts, function(new_artifact){
this.update_board_artifact_viewmodel(new_artifact);
this.active_space_artifacts.push(new_artifact)
}.bind(this));
}.bind(this), function(xhr) {
this.global_spinner = false;
alert("Error PDF ("+xhr.status+")");
}.bind(this));
}
},
handle_data_drop: function(evt) { handle_data_drop: function(evt) {
if (this.active_space_role=="viewer") { if (this.active_space_role=="viewer") {
return false; return false;
@ -2858,8 +2932,17 @@ var SpacedeckSections = {
if (files && files.length) { if (files && files.length) {
for (var i=0; i<files.length; i++) { for (var i=0; i<files.length; i++) {
var file = files[i]; var file = files[i];
if (file.type === "application/pdf") {
var point = this.cursor_point_to_space(evt);
this.dropped_point = point;
this.pending_pdf_file = file;
this.activate_modal('pdfoptions');
} else {
this.create_artifact_via_upload(evt, file, (files.length>1)); this.create_artifact_via_upload(evt, file, (files.length>1));
} }
}
} else { } else {
var json = evt.dataTransfer.getData('application/json'); var json = evt.dataTransfer.getData('application/json');

View File

@ -18,6 +18,8 @@ var SpacedeckSpaces = {
active_space_path: [], active_space_path: [],
access_settings_space: null, access_settings_space: null,
access_settings_memberships: [], access_settings_memberships: [],
duplicate_folders: [],
duplicate_folder_id: "",
pending_pdf_files: [], pending_pdf_files: [],
meta_visible: false, meta_visible: false,
@ -108,6 +110,27 @@ var SpacedeckSpaces = {
space_auth = get_query_param("spaceAuth"); space_auth = get_query_param("spaceAuth");
var userReady = function() { var userReady = function() {
if (get_query_param("embedded")) {
this.embedded = true;
this.guest_signup_enabled = true;
if (get_query_param("publish_cta")) {
this.publish_cta = get_query_param("publish_cta");
}
if (get_query_param("nosocial")) {
this.social_bar = false;
}
}
if (get_query_param("confirm") && this.logged_in) {
var token = get_query_param("confirm");
confirm_user(this.user, token, function() {
this.redirect_to("/spaces/"+space_id+"?show_access=1");
}.bind(this), function() {
alert("An error occured confirming your email with the given token.");
});
return;
}
this.close_dropdown(); this.close_dropdown();
this.active_space_loaded = false; this.active_space_loaded = false;
@ -135,7 +158,9 @@ var SpacedeckSpaces = {
load_space(space_id, function(space, role) { load_space(space_id, function(space, role) {
document.title = space.name; document.title = space.name;
this.active_space_role = role || "viewer"; // via req header from backend this.active_space_role = role || "viewer"; //via req header from backend
this.space_embed_html = "<iframe width=\"1024\" height=\"768\" seamless src=\""+ENV.webEndpoint+"/spaces/"+space._id+"?embedded=1\"></iframe>";
if (!is_home) { if (!is_home) {
load_members(space, function(members) { load_members(space, function(members) {
@ -248,9 +273,9 @@ var SpacedeckSpaces = {
this.discover_zones(); this.discover_zones();
window.setTimeout(function() { //window.setTimeout(function() {
this.zoom_to_fit(); // this.zoom_to_fit();
}.bind(this),10); //}.bind(this),10);
if (on_success) { if (on_success) {
on_success(); on_success();
@ -276,6 +301,15 @@ var SpacedeckSpaces = {
// FIXME // FIXME
this.active_join_link = ""; this.active_join_link = "";
this.join_link_role = "viewer"; this.join_link_role = "viewer";
// FIXME
if (this.active_space_role == "admin") {
this.space_info_section="access";
} else if (this.active_space_role == "editor") {
//this.space_info_section="versions";
} else {
this.space_info_section="info";
}
} }
}.bind(this), function(xhr) { }.bind(this), function(xhr) {
@ -304,7 +338,7 @@ var SpacedeckSpaces = {
userReady(); userReady();
} }
if (!this.user && space_auth) { if (space_auth) {
if (this.guest_nickname) { if (this.guest_nickname) {
userReady(); userReady();
} else { } else {
@ -639,6 +673,47 @@ var SpacedeckSpaces = {
location.href = "/api/spaces/" + space._id + "/list"; location.href = "/api/spaces/" + space._id + "/list";
}, },
duplicate_space_into_folder: function() {
load_writable_folders( function(folders){
this.duplicate_folders = _.sortBy(folders, function (folder) { return folder.name; });
}.bind(this), function(xhr) {
console.error(xhr);
});
},
duplicate_folder_confirm: function() {
var folderId = this.duplicate_folder_id;
var idx = _.findIndex(this.duplicate_folders, function(s) { return s._id == folderId;});
if (idx<0) idx = 0;
var folder = this.duplicate_folders[idx];
console.log("df f",folder);
if (!folder) return;
duplicate_space(this.active_space, folder._id, function(new_space) {
this.duplicate_folders = [];
this.duplicate_folder = null;
smoke.quiz(__("duplicate_success", this.active_space.name, folder.name), function(e, test){
if (e == __("goto_space", new_space.name)){
this.redirect_to("/spaces/" + new_space._id);
}else if (e == __("goto_folder", folder.name)){
this.redirect_to("/folders/" + folder._id);
}
}.bind(this), {
button_1: __("goto_space", new_space.name),
button_2: __("goto_folder", folder.name),
button_cancel:__("stay_here")
});
}.bind(this), function(xhr){
console.error(xhr);
smoke.prompt("error: " + xhr.statusText);
}.bind(this));
},
toggle_follow_mode: function() { toggle_follow_mode: function() {
this.deselect(); this.deselect();
this.follow_mode = !this.follow_mode; this.follow_mode = !this.follow_mode;
@ -744,12 +819,9 @@ var SpacedeckSpaces = {
this.invite_message = ""; this.invite_message = "";
} }
}.bind(this), function(xhr){ }.bind(this), function(xhr){
try {
var res = JSON.parse(xhr.response); text = JSON.stringify(xhr.responseText);
alert("Error: "+res.error); smoke.alert("Error: "+text);
} catch (e) {
console.error(e, xhr);
}
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));
}, },
@ -757,13 +829,9 @@ var SpacedeckSpaces = {
update_member: function(space, m, role) { update_member: function(space, m, role) {
m.role = role; m.role = role;
save_membership(space, m, function() { save_membership(space, m, function() {
console.log("saved")
}.bind(this), function(xhr) { }.bind(this), function(xhr) {
try { console.error(xhr);
var res = JSON.parse(xhr.response);
alert("Error: "+res.error);
} catch (e) {
console.error(e, xhr);
}
}.bind(this)); }.bind(this));
}, },
@ -772,12 +840,7 @@ var SpacedeckSpaces = {
delete_membership(space, m, function() { delete_membership(space, m, function() {
this.access_settings_memberships.splice(this.access_settings_memberships.indexOf(m), 1); this.access_settings_memberships.splice(this.access_settings_memberships.indexOf(m), 1);
}.bind(this), function(xhr) { }.bind(this), function(xhr) {
try { console.error(xhr);
var res = JSON.parse(xhr.response);
alert("Error: "+res.error);
} catch (e) {
console.error(e, xhr);
}
}.bind(this)); }.bind(this));
}, },
@ -813,6 +876,10 @@ var SpacedeckSpaces = {
}.bind(this)); }.bind(this));
}, },
emojified_comment: function(comment) {
return twemoji.parse(comment);
},
set_folder_sorting: function(key,reverse) { set_folder_sorting: function(key,reverse) {
this.folder_sorting = key; this.folder_sorting = key;
this.folder_reverse = reverse?-1:1; this.folder_reverse = reverse?-1:1;

View File

@ -11,7 +11,6 @@ SpacedeckUsers = {
login_email: "", login_email: "",
login_password: "", login_password: "",
signup_password: "", signup_password: "",
signup_invite_code: "",
signup_password_confirmation: "", signup_password_confirmation: "",
account_remove_error: null, account_remove_error: null,
loading_user: false, loading_user: false,
@ -31,6 +30,12 @@ SpacedeckUsers = {
if (on_success) { if (on_success) {
on_success(user); on_success(user);
} }
// see spacedeck_account.js
load_importables(this.user, function(files) {
this.importables = files;
}.bind(this));
}.bind(this), function() { }.bind(this), function() {
// error // error
this.loading_user = false; this.loading_user = false;
@ -117,7 +122,7 @@ SpacedeckUsers = {
signup_guest: function(on_success) { signup_guest: function(on_success) {
}, },
signup_submit: function($event, name, email, password, password_confirmation, invite_code, on_success) { signup_submit: function($event, name, email, password, password_confirmation, on_success) {
this.creating_user = true; this.creating_user = true;
this.signup_error = null; this.signup_error = null;
@ -131,7 +136,7 @@ SpacedeckUsers = {
$event.stopPropagation(); $event.stopPropagation();
} }
create_user(name, email, password, password_confirmation, invite_code, function(session) { create_user(name, email, password, password_confirmation, function(session) {
this.creating_user = false; this.creating_user = false;
this.login_submit(email, password, null, on_success); this.login_submit(email, password, null, on_success);
}.bind(this), function(req) { }.bind(this), function(req) {
@ -147,8 +152,8 @@ SpacedeckUsers = {
}.bind(this)); }.bind(this));
}, },
signup_submit_modal: function($event, name, email, password, password_confirmation, invite_code) { signup_submit_modal: function($event, name, email, password, password_confirmation) {
this.signup_submit($event, name, email, password, password_confirmation, invite_code, function() { this.signup_submit($event, name, email, password, password_confirmation, function() {
alert("Success."); alert("Success.");
location.reload(); location.reload();
}); });
@ -196,29 +201,27 @@ SpacedeckUsers = {
this.password_reset_confirm_error = null; this.password_reset_confirm_error = null;
this.password_reset_send = false; this.password_reset_send = false;
if (password != password_confirmation) { if(password != password_confirmation) {
this.password_reset_confirm_error = "Passwords do not match."; this.password_reset_confirm_error = "Passwords do not match.";
return; return;
} }
if (password.length < 5) { if(password.length < 5) {
this.password_reset_confirm_error = "Password too short (must have at least 5 characters)."; this.password_reset_confirm_error = "Password too short (must have at least 5 characters).";
return; return;
} }
confirm_password_reset(password, this.reset_token, function(parsed,req) { confirm_password_reset(password, this.reset_token, function(parsed,req) {
if (req.status==201) { if(req.status==201){
alert("New password set successfully.");
this.active_view = "login"; this.active_view = "login";
} else {
alert("An unknown error occured.");
} }
}.bind(this), function(req) { }.bind(this), function(req) {
if (req.status==404) { if (req.status==404) {
alert("Error: Unknown user."); var msg = "user not found";
} else { } else {
alert("Error: "+req.statusText); var msg = "error: " + req.statusText;
} }
this.password_reset_confirm_error = msg;
}.bind(this)); }.bind(this));
}, },

View File

@ -80,16 +80,10 @@ function setup_whiteboard_directives() {
evt.stopPropagation(); evt.stopPropagation();
} }
if ($scope.active_tool == "zoom") return;
if (evt.which == 2) {
// middle mouse button
this.handle_mouse_down_space(evt);
return;
}
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 ($scope.active_tool == "zoom") return;
if ($scope.active_tool == "eyedrop") { if ($scope.active_tool == "eyedrop") {
var arts = $scope.selected_artifacts(); var arts = $scope.selected_artifacts();
if (!$scope.is_selected(a) && arts.length > 0) { if (!$scope.is_selected(a) && arts.length > 0) {
@ -202,9 +196,7 @@ function setup_whiteboard_directives() {
}, },
handle_mouse_down_space: function(evt) { handle_mouse_down_space: function(evt) {
if (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;
@ -222,7 +214,7 @@ function setup_whiteboard_directives() {
this.deselect(); this.deselect();
this.mouse_state = "transform"; this.mouse_state = "transform";
$scope.mouse_state = this.mouse_state; $scope.mouse_state = this.mouse_state;
this.start_drawing_note(evt); this.start_adding_note(evt);
return; return;
} else if ($scope.active_tool=="arrow") { } else if ($scope.active_tool=="arrow") {
@ -500,7 +492,6 @@ function setup_whiteboard_directives() {
if (!xdists[0] || xdists[0][0]>TOL) { if (!xdists[0] || xdists[0][0]>TOL) {
results.snapx = [0,x]; // distance, coordinate results.snapx = [0,x]; // distance, coordinate
} else { } else {
// FIXME snap rulers are broken
//$scope.snap_ruler_x = xdists[0][1]; //$scope.snap_ruler_x = xdists[0][1];
} }
if (!ydists[0] || ydists[0][0]>TOL) { if (!ydists[0] || ydists[0][0]>TOL) {
@ -525,41 +516,6 @@ function setup_whiteboard_directives() {
return point; return point;
}, },
start_drawing_note: function(evt) {
evt.preventDefault();
evt.stopPropagation();
var $scope = this.vm.$root;
var point = this.cursor_point_to_space(evt);
this.offset_point_in_wrapper(point);
var z = $scope.highest_z()+1;
var a = {
space_id: $scope.active_space._id,
mime: "text/html",
description: "<p>Text</p>",
x: point.x,
y: point.y,
z: z,
w: 64,
h: 64,
align: "center",
valign: "middle",
stroke_color: "#000000",
fill_color: "rgb(241, 196, 15)",
stroke: 0
};
$scope.save_artifact(a, function(saved_a) {
$scope.update_board_artifact_viewmodel(saved_a);
$scope.active_space_artifacts.push(saved_a);
$scope.select(evt,a);
$scope.transform_ox = 0;
$scope.transform_oy = 0;
$scope.begin_transaction();
}.bind(this));
},
start_drawing_scribble: function(evt) { start_drawing_scribble: function(evt) {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
@ -735,7 +691,7 @@ function setup_whiteboard_directives() {
this.mouse_state = "idle"; this.mouse_state = "idle";
$scope.mouse_state = this.mouse_state; $scope.mouse_state = this.mouse_state;
this.lasso = null; this.lasso = null;
//$scope.active_tool = "pointer"; disabled to keep the same tool (eg. "Scribble") after drawing a single line. $scope.active_tool = "pointer";
$scope.end_transaction(); $scope.end_transaction();
$scope.show_toolbar_props(); $scope.show_toolbar_props();
@ -895,7 +851,7 @@ function setup_whiteboard_directives() {
var scale_x = lead_x ? (moved_x)/lead_x : 1; var scale_x = lead_x ? (moved_x)/lead_x : 1;
var scale_y = lead_y ? (moved_y)/lead_y : 1; var scale_y = lead_y ? (moved_y)/lead_y : 1;
if ($scope.transform_lock) scale_y = scale_x; if (!$scope.transform_lock) scale_y = scale_x;
$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);

File diff suppressed because it is too large Load Diff

View File

@ -138,6 +138,7 @@ router.get('/', function(req, res, next) {
"$exists": 1 "$exists": 1
} }
}).populate("space").exec(function(err, memberships) { }).populate("space").exec(function(err, memberships) {
async.map(memberships, function(membership, memcb) { async.map(memberships, function(membership, memcb) {
Space.getRecursiveSubspacesForSpace(membership.space, function(err, spaces) { Space.getRecursiveSubspacesForSpace(membership.space, function(err, spaces) {
cb(null, spaces.map(function(s) { cb(null, spaces.map(function(s) {

View File

@ -51,7 +51,8 @@ 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) { phantom.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) {
@ -78,14 +79,14 @@ router.get('/png', function(req, res, next) {
var oldPath = url.parse(oldUrl).pathname; var oldPath = url.parse(oldUrl).pathname;
uploader.removeFile(oldPath, function(err, res) {}); uploader.removeFile(oldPath, function(err, res) {});
} }
fs.unlinkSync(local_path); fs.unlink(local_path);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
}); });
try { try {
fs.unlinkSync(localResizedFilePath); fs.unlink(localResizedFilePath);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
@ -94,7 +95,7 @@ router.get('/png', function(req, res, next) {
}, },
function() { function() {
// on_error // on_error
console.error("[space screenshot] could not create screenshot for space " + req.space_id); console.error("phantom could not create screenshot for space " + req.space_id);
res.status(404).send("Not found"); res.status(404).send("Not found");
}); });
} else { } else {

View File

@ -45,12 +45,10 @@ router.post('/', function(req, res, next) {
"email": membership.email_invited "email": membership.email_invited
}}).then(function(user) { }}).then(function(user) {
// existing user? then immediately activate membership
if (user) { if (user) {
membership.user_id = user._id; membership.user_id = user._id;
membership.state = "active"; membership.state = "active";
} else { } else {
// if not, invite via email and invite code
membership.code = crypto.randomBytes(64).toString('hex').substring(0, 12); membership.code = crypto.randomBytes(64).toString('hex').substring(0, 12);
} }
@ -86,13 +84,13 @@ router.post('/', function(req, res, next) {
} else { } else {
res.status(400).json({ res.status(400).json({
"error": "This email is already included in the Space memberships." "error": "user already in space"
}); });
} }
} else { } else {
res.status(403).json({ res.status(403).json({
"error": "Only administrators can do that." "error": "not_permitted"
}); });
} }
}); });
@ -104,19 +102,12 @@ router.put('/:membership_id', function(req, res, next) {
_id: req.params.membership_id _id: req.params.membership_id
}}).then(function(mem) { }}).then(function(mem) {
if (mem) { if (mem) {
// is the user trying to change their own role?
if (mem.user_id == req.user._id) {
res.status(400).json({
"error": "Cannot change your own role."
});
} else {
var attrs = req.body; var attrs = req.body;
mem.role = attrs.role; mem.role = attrs.role;
mem.save(function() { mem.save(function() {
res.status(201).json(mem); res.status(201).json(mem);
}); });
} }
}
}); });
} else { } else {
res.sendStatus(403); res.sendStatus(403);
@ -127,25 +118,13 @@ router.put('/:membership_id', function(req, res, next) {
}); });
router.delete('/:membership_id', function(req, res, next) { router.delete('/:membership_id', function(req, res, next) {
if (req.user && req.spaceRole == 'admin') { if (req.user) {
db.Membership.count({ where: {
space_id: req.space._id,
role: "admin"
}}).then(function(adminCount) {
db.Membership.findOne({ where: { db.Membership.findOne({ where: {
_id: req.params.membership_id _id: req.params.membership_id
}}).then(function(mem) { }}).then(function(mem) {
// deleting an admin? need at least 1
if (mem.role != "admin" || adminCount > 1) {
mem.destroy().then(function() { mem.destroy().then(function() {
res.sendStatus(204); res.sendStatus(204);
}); });
} else {
res.status(400).json({
"error": "Space needs at least one administrator."
});
}
})
}); });
} else { } else {
res.sendStatus(403); res.sendStatus(403);

View File

@ -48,11 +48,73 @@ router.get('/', function(req, res, next) {
error: "auth required" error: "auth required"
}); });
} else { } else {
if (req.query.search) { if (req.query.writablefolders) {
db.Membership.find({where: {
user_id: req.user._id
}}, (memberships) => {
var validMemberships = memberships.filter((m) => {
if (!m.space_id || (m.space_id == "undefined"))
return false;
return true;
});
var editorMemberships = validMemberships.filter((m) => {
return (m.role == "editor") || (m.role == "admin")
});
var spaceIds = editorMemberships.map(function(m) {
return m.space_id;
});
// TODO port
var q = {
"space_type": "folder",
"$or": [{
"creator": req.user._id
}, {
"_id": {
"$in": spaceIds
},
"creator": {
"$ne": req.user._id
}
}]
};
db.Space
.findAll({where: q})
.then(function(spaces) {
var updatedSpaces = spaces.map(function(s) {
var spaceObj = s; //.toObject();
return spaceObj;
});
async.map(spaces, (space, cb) => {
Space.getRecursiveSubspacesForSpace(space, (err, spaces) => {
var allSpaces = spaces;
cb(err, allSpaces);
})
}, (err, spaces) => {
var allSpaces = _.flatten(spaces);
var onlyFolders = _.filter(allSpaces, (s) => {
return s.space_type == "folder";
})
var uniqueFolders = _.unique(onlyFolders, (s) => {
return s._id;
})
res.status(200).json(uniqueFolders);
});
});
});
} else if (req.query.search) {
db.Membership.findAll({where:{ db.Membership.findAll({where:{
user_id: req.user._id user_id: req.user._id
}}).then(memberships => { }}).then(memberships => {
// search for spaces
var validMemberships = memberships.filter(function(m) { var validMemberships = memberships.filter(function(m) {
if (!m.space_id || (m.space_id == "undefined")) if (!m.space_id || (m.space_id == "undefined"))
@ -81,7 +143,6 @@ router.get('/', function(req, res, next) {
}); });
} else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) { } else if (req.query.parent_space_id && req.query.parent_space_id != req.user.home_folder_id) {
// list spaces in a folder
db.Space db.Space
.findOne({where: { .findOne({where: {
@ -115,8 +176,6 @@ router.get('/', function(req, res, next) {
}); });
} else { } else {
// list home folder and spaces/folders that the user is a member of
db.Membership.findAll({ where: { db.Membership.findAll({ where: {
user_id: req.user._id user_id: req.user._id
}}).then(memberships => { }}).then(memberships => {
@ -125,7 +184,6 @@ router.get('/', function(req, res, next) {
var validMemberships = memberships.filter(function(m) { var validMemberships = memberships.filter(function(m) {
if (!m.space_id || (m.space_id == "undefined")) if (!m.space_id || (m.space_id == "undefined"))
return false; return false;
return true;
}); });
var spaceIds = validMemberships.map(function(m) { var spaceIds = validMemberships.map(function(m) {
@ -170,18 +228,14 @@ router.post('/', function(req, res, next) {
attrs.creator_id = req.user._id; attrs.creator_id = req.user._id;
attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7); attrs.edit_hash = crypto.randomBytes(64).toString('hex').substring(0, 7);
attrs.edit_slug = slug(attrs.name); attrs.edit_slug = slug(attrs.name);
attrs.access_mode = "private";
db.Space.create(attrs).then(createdSpace => { db.Space.create(attrs).then(createdSpace => {
res.status(201).json(createdSpace); //if (err) res.sendStatus(400);
// create initial admin membership
var membership = { var membership = {
_id: uuidv4(), _id: uuidv4(),
user_id: req.user._id, user_id: req.user._id,
space_id: attrs._id, space_id: attrs._id,
role: "admin", role: "admin"
state: "active"
}; };
db.Membership.create(membership).then(() => { db.Membership.create(membership).then(() => {
@ -260,17 +314,8 @@ router.put('/:id', function(req, res) {
newAttr.edit_slug = slug(newAttr['name']); newAttr.edit_slug = slug(newAttr['name']);
delete newAttr['_id']; delete newAttr['_id'];
delete newAttr['editor_name'];
delete newAttr['creator']; delete newAttr['creator'];
delete newAttr['creator_id'];
delete newAttr['space_type'];
if (req['spaceRole'] != "admin") {
delete newAttr['access_mode']
delete newAttr['password']
delete newAttr['edit_hash']
delete newAttr['edit_slug']
delete newAttr['editors_locking']
}
db.Space.update(newAttr, {where: { db.Space.update(newAttr, {where: {
"_id": space._id "_id": space._id
@ -317,6 +362,43 @@ router.post('/:id/background', function(req, res, next) {
}); });
}); });
var handleDuplicateSpaceRequest = function(req, res, parentSpace) {
Space.duplicateSpace(req.space, req.user, 0, (err, newSpace) => {
if (err) {
console.error(err);
res.status(400).json(err);
} else {
res.status(201).json(newSpace);
}
}, parentSpace);
}
router.post('/:id/duplicate', (req, res, next) => {
if (req.query.parent_space_id) {
Space.findOne({
_id: req.query.parent_space_id
}).populate('creator', userMapping).exec((err, parentSpace) => {
if (!parentSpace) {
res.status(404).json({
"error": "parent space not found for duplicate"
});
} else {
db.getUserRoleInSpace(parentSpace, req.user, (role) => {
if (role == "admin" ||  role == "editor") {
handleDuplicateSpaceRequest(req, res, parentSpace);
} else {
res.status(403).json({
"error": "not authed for parent_space_id"
});
}
});
}
});
} else {
handleDuplicateSpaceRequest(req, res);
}
});
router.delete('/:id', function(req, res, next) { router.delete('/:id', function(req, res, next) {
if (req.user) { if (req.user) {
const space = req.space; const space = req.space;
@ -336,4 +418,136 @@ router.delete('/:id', function(req, res, next) {
} }
}); });
router.post('/:id/artifacts-pdf', function(req, res, next) {
if (req.spaceRole == "editor" || req.spaceRole == "admin") {
var withZones = (req.query.zones) ? req.query.zones == "true" : false;
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9\.]/g, '');
var localFilePath = os.tmpdir() + "/" + fileName;
var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream);
req.on('end', function() {
var rawName = fileName.slice(0, fileName.length - 4);
var outputFolder = os.tmpdir() + "/" + rawName;
fs.mkdir(outputFolder, function(err) {
var images = outputFolder + "/" + rawName + "-page-%03d.jpeg";
// FIXME not portable
exec.execFile("gs", ["-sDEVICE=jpeg", "-dDownScaleFactor=4", "-dDOINTERPOLATE", "-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-sOutputFile=" + images, "-r250", "-f", localFilePath], {}, function(error, stdout, stderr) {
if (error === null) {
glob(outputFolder + "/*.jpeg", function(er, files) {
var count = files.length;
var delta = 10;
var limitPerRow = Math.ceil(Math.sqrt(count));
var startX = parseInt(req.query.x, delta);
var startY = parseInt(req.query.y, delta);
async.mapLimit(files, 20, function(localfilePath, cb) {
var fileName = path.basename(localfilePath);
var baseName = path.basename(localfilePath, ".jpeg");
var number = parseInt(baseName.slice(baseName.length - 3, baseName.length), 10);
gm(localFilePath).size((err, size) => {
var w = 350;
var h = w;
var x = startX + (((number - 1) % limitPerRow) * w);
var y = startY + ((parseInt(((number - 1) / limitPerRow), 10) + 1) * w);
var userId;
if (req.user) userId = req.user._id;
var a = db.Artifact.create({
_id: uuidv4(),
mime: "image/jpg",
space_id: req.space._id,
user_id: userId,
editor_name: req.guest_name,
w: w,
h: h,
x: x,
y: y,
z: (number + (count + 100))
}).then(a => {
payloadConverter.convert(a, fileName, localfilePath, (error, artifact) => {
if (error) res.status(400).json(error);
else {
if (withZones) {
var zone = {
_id: uuidv4(),
mime: "x-spacedeck/zone",
description: "Zone " + (number),
space_id: req.space._id,
user_id: userId,
editor_name: req.guest_name,
w: artifact.w + 20,
h: artifact.h + 40,
x: x - 10,
y: y - 30,
z: number,
order: number,
valign: "middle",
align: "center"
};
db.Artifact.create(zone).then((z) => {
redis.sendMessage("create", "Artifact", z.toJSON(), req.channelId);
cb(null, [artifact, zone]);
});
} else {
cb(null, [artifact]);
}
}
});
});
});
}, function(err, artifacts) {
// FIXME not portable
exec.execFile("rm", ["-r", outputFolder], function(err) {
res.status(201).json(_.flatten(artifacts));
async.eachLimit(artifacts, 10, (artifact_or_artifacts, cb) => {
if (artifact_or_artifacts instanceof Array) {
_.each(artifact_or_artifacts, (a) => {
redis.sendMessage("create", "Artifact", JSON.stringify(a), req.channelId);
});
} else  {
redis.sendMessage("create", "Artifact", JSON.stringify(artifact_or_artifacts), req.channelId);
}
cb(null);
});
});
});
});
} else {
console.error("error:", error);
// FIXME not portable
exec.execFile("rm", ["-r", outputFolder], function(err) {
fs.unlink(localFilePath);
res.status(400).json({});
});
}
});
});
});
} else {
res.status(401).json({
"error": "no access"
});
}
});
module.exports = router; module.exports = router;

View File

@ -51,18 +51,12 @@ router.post('/', function(req, res) {
var nickname = req.body["nickname"]; var nickname = req.body["nickname"];
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"];
if (password_confirmation != password) { if (password_confirmation != password) {
res.status(400).json({"error":"password_confirmation"}); res.status(400).json({"error":"password_confirmation"});
return; return;
} }
if (config.invite_code && invite_code != config.invite_code) {
res.status(400).json({"error":"Invalid Invite Code."});
return;
}
if (!validator.isEmail(email)) { if (!validator.isEmail(email)) {
res.status(400).json({"error":"email_invalid"}); res.status(400).json({"error":"email_invalid"});
return; return;
@ -89,31 +83,28 @@ router.post('/', function(req, res) {
res.sendStatus(400); res.sendStatus(400);
}) })
.then(u => { .then(u => {
var homeFolder = { var homeSpace = {
_id: uuidv4(), _id: uuidv4(),
name: req.i18n.__("home"), name: req.i18n.__("home"),
space_type: "folder", space_type: "folder",
creator_id: u._id creator_id: u._id
}; };
db.Space.create(homeFolder) db.Space.create(homeSpace)
.error(err => { .error(err => {
res.sendStatus(400); res.sendStatus(400);
}) })
.then(homeFolder => { .then(homeSpace => {
u.home_folder_id = homeFolder._id; u.home_folder_id = homeSpace._id;
u.save() u.save()
.then(() => { .then(() => {
// home folder created, res.status(201).json({});
// auto accept pending invites
db.Membership.update({ mailer.sendMail(u.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), {
"state": "active" action: {
}, { link: config.endpoint + "/confirm/" + u.confirmation_token,
where: { name: req.i18n.__("confirm_action")
"email_invited": u.email,
"state": "pending"
} }
}); });
res.status(201).json({});
}) })
.error(err => { .error(err => {
res.status(400).json(err); res.status(400).json(err);
@ -128,6 +119,7 @@ router.post('/', function(req, res) {
db.User.findAll({where: {email: email}}) db.User.findAll({where: {email: email}})
.then(users => { .then(users => {
if (users.length == 0) { if (users.length == 0) {
//var domain = email.slice(email.lastIndexOf('@')+1);
createUser(); createUser();
} else { } else {
res.status(400).json({"error":"user_email_already_used"}); res.status(400).json({"error":"user_email_already_used"});
@ -176,35 +168,36 @@ router.post('/:id/password', function(req, res, next) {
}); });
}); });
} else { } else {
res.status(403).json({"error": "Please enter the correct current password."}); res.status(403).json({"error": "old password wrong"});
} }
} else { } else {
res.status(403).json({"error": "Access denied."}); res.status(403).json({"error": "wrong user"});
} }
} else { } else {
res.status(400).json({"error": "Please choose a new password with at least 6 characters."}); res.status(400).json({"error": "password_to_short"});
} }
}); });
router.delete('/:id', (req, res, next) => { router.delete('/:id', (req, res, next) => {
const user = req.user; const user = req.user;
if (user._id == req.params.id) { if(user._id == req.params.id) {
if (user.account_type == 'email') {
if (bcrypt.compareSync(req.query.password, user.password_hash)) { if (bcrypt.compareSync(req.query.password, user.password_hash)) {
user.remove((err) => {
// TODO: this doesn't currently work.
// all objects (indirectly) belonging to the user have
// to be walked and deleted first.
user.destroy().then(err => {
if(err)res.status(400).json(err); if(err)res.status(400).json(err);
else res.sendStatus(204); else res.sendStatus(204);
}); });
} else { } else {
res.bad_request("Please enter the correct current password."); res.bad_request("password_incorrect");
} }
} else { } else {
res.status(403).json({error: "Access denied."}); user.remove((err) => {
if (err) res.status(400).json(err);
else res.sendStatus(204);
});
} }
}
else res.status(403).json({error: ""});
}); });
router.put('/:user_id/confirm', (req, res) => { router.put('/:user_id/confirm', (req, res) => {
@ -260,6 +253,13 @@ router.post('/:user_id/avatar', (req, res, next) => {
}); });
}); });
router.post('/feedback', function(req, res, next) {
var text = req.body.text;
// FIXME
mailer.sendMail("support@example.org", "Support Request by " + req.user.email, text, {reply_to: req.user.email});
res.sendStatus(201);
});
router.post('/password_reset_requests', (req, res, next) => { router.post('/password_reset_requests', (req, res, next) => {
const email = req.query.email; const email = req.query.email;
db.User.findOne({where: {"email": email}}).then((user) => { db.User.findOne({where: {"email": email}}).then((user) => {
@ -283,16 +283,21 @@ router.post('/password_reset_requests', (req, res, next) => {
router.post('/password_reset_requests/:confirm_token/confirm', function(req, res, next) { router.post('/password_reset_requests/:confirm_token/confirm', function(req, res, next) {
var password = req.body.password; var password = req.body.password;
db.User User
.findOne({where: {"password_reset_token": req.params.confirm_token}}) .findOne({where: {"password_reset_token": req.params.confirm_token}})
.then((user) => { .then((user) => {
if (user) { if (user) {
bcrypt.genSalt(10, (err, salt) => { bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, function(err, hash) { bcrypt.hash(password, salt, function(err, hash) {
user.password_hash = hash; user.password_hash = hash;
user.password_token = null; user.password_token = null;
user.save().then(function(updatedUser) { user.save(function(err, updatedUser){
if (err) {
res.sendStatus(400);
} else {
res.sendStatus(201); res.sendStatus(201);
}
}); });
}); });
}); });
@ -310,4 +315,19 @@ router.post('/:user_id/confirm', function(req, res, next) {
res.sendStatus(201); res.sendStatus(201);
}); });
router.get('/:user_id/importables', function(req, res, next) {
glob('*.zip', function(err, files) {
res.status(200).json(files);
});
});
router.get('/:user_id/import', function(req, res, next) {
if (req.query.zip) {
res.send("importing");
importer.importZIP(req.user, req.query.zip);
} else {
res.sendStatus(400);
}
});
module.exports = router; module.exports = router;

View File

@ -54,6 +54,10 @@ router.get('/password-confirm/:token', (req, res) => {
res.render('spacedeck', { title: 'Signup' }); res.render('spacedeck', { title: 'Signup' });
}); });
router.get('/team', (req, res) => {
res.render('spacedeck');
});
router.get('/de/*', (req, res) => { router.get('/de/*', (req, res) => {
res.redirect("/t/de"); res.redirect("/t/de");
}); });
@ -78,6 +82,10 @@ router.get('/en', (req, res) => {
res.redirect("/t/end"); res.redirect("/t/end");
}); });
router.get('/it', (req, res) => {
res.redirect("/t/en");
});
router.get('/account', (req, res) => { router.get('/account', (req, res) => {
res.render('spacedeck'); res.render('spacedeck');
}); });

View File

@ -26,9 +26,6 @@ const serveStatic = require('serve-static');
const isProduction = app.get('env') === 'production'; const isProduction = app.get('env') === 'production';
// workaround for libssl_conf.so error triggered by phantomjs
process.env['OPENSSL_CONF'] = '/dev/null';
console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")"); console.log("Booting Spacedeck Open… (environment: " + app.get('env') + ")");
app.use(logger(isProduction ? 'combined' : 'dev')); app.use(logger(isProduction ? 'combined' : 'dev'));
@ -75,7 +72,7 @@ app.use(helmet.frameguard())
app.use(helmet.xssFilter()) app.use(helmet.xssFilter())
app.use(helmet.hsts({ app.use(helmet.hsts({
maxAge: 7776000000, maxAge: 7776000000,
includeSubDomains: true includeSubdomains: true
})) }))
app.disable('x-powered-by'); app.disable('x-powered-by');
app.use(helmet.noSniff()) app.use(helmet.noSniff())

View File

@ -26,12 +26,12 @@
} }
} }
&.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;
} }
/*&.artifact-text.text-blank [contentEditable=true].text-editing p:first-child::after { &.artifact-text.text-blank [contentEditable=true].text-editing p:first-child::after {
content: "Type here"; content: "Type here";
opacity: 0.25; opacity: 0.25;
}*/ }*/
@ -469,10 +469,11 @@
color: black; color: black;
//@include user-select(none); //@include user-select(none);
white-space: normal; white-space: normal;
font-size: 36px; font-size: 18px;
&.artifact-zone { &.artifact-zone {
background-color: rgba(0,0,0,0.05); border: 1px solid rgba(46,204,113,1);
background-color: rgba(46,204,113,0.025);
border-radius: 10px; border-radius: 10px;
&:after {display: none; } &:after {display: none; }
.shape {display: none; } .shape {display: none; }
@ -552,10 +553,6 @@ body:not(.present-mode) {
cursor: grab !important; cursor: grab !important;
} }
.tool-note {
cursor: crosshair !important;
}
.artifact.state-idle { .artifact.state-idle {
.progress, .progress-text { .progress, .progress-text {
display: none; display: none;

View File

@ -7,6 +7,12 @@
.btn-group.colors { .btn-group.colors {
.btn { .btn {
// padding: 4px;
// background-clip: content-box;
// padding-right: 2px;
// &:last-child {
// padding-right: 4px;
// }
box-shadow: inset 0 0 30px 0px rgba(40,40,40,0.1); box-shadow: inset 0 0 30px 0px rgba(40,40,40,0.1);
} }
} }
@ -58,7 +64,7 @@
backface-visibility: hidden; backface-visibility: hidden;
cursor: pointer; cursor: pointer;
background-color: $light; background-color: $light;
color: $black; color: $medium;;
@include user-select(none); @include user-select(none);
&:last-child {border: none;} &:last-child {border: none;}
@ -76,9 +82,12 @@
&.btn-link { &.btn-link {
background-color: transparent; background-color: transparent;
color: $medium; color: $medium;;
} }
&.facebook {background-color: $facebook !important; color: white !important;}
&.twitter {background-color: $twitter !important; color: white !important; }
&.btn-round { &.btn-round {
border-radius: 100px !important; border-radius: 100px !important;
} }
@ -87,10 +96,21 @@
border-radius: 6px !important; border-radius: 6px !important;
} }
// &.close {
// position: absolute;
// top: 15px;
// right: 15px;
// z-index: 4000;
// font-size: 40px;
// }
&.btn-nude { &.btn-nude {
min-width: 0 !important; min-width: 0 !important;
// font-size: inherit !important;
padding: 0 !important; padding: 0 !important;
// height: auto !important;
background-color: transparent; background-color: transparent;
color: $medium;
} }
&.btn-nude + .btn-nude { &.btn-nude + .btn-nude {
@ -103,7 +123,7 @@
&.btn-stroke { &.btn-stroke {
box-shadow: inset 0 0 0 1px $dark; box-shadow: inset 0 0 0 1px $dark;
color: $black; color: $dark !important;
background-color: transparent; background-color: transparent;
&:active { &:active {
box-shadow: inset 0 0 0 1px white; box-shadow: inset 0 0 0 1px white;
@ -112,8 +132,9 @@
} }
&.btn-stroke-darken { &.btn-stroke-darken {
border: 1px solid $black; //box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1);
color: $black; border: 1px solid rgba(0,0,0,0.1);
color: $medium;
background-color: transparent; background-color: transparent;
&:active { &:active {
//box-shadow: inset 0 0 0 1px $dark; //box-shadow: inset 0 0 0 1px $dark;
@ -242,18 +263,9 @@
&.btn-transparent { &.btn-transparent {
background-color: transparent; background-color: transparent;
color: $dark; color: $medium;
&.active { &.active {color: $darker !important; }
//color: $black !important; &.open {color: white !important; }
color: $white;
background-color: $black;
}
&.open {
//color: $black !important;
color: $white;
background-color: $black;
border-radius: 0;
}
} }
&.btn-transparent-medium { &.btn-transparent-medium {
@ -301,7 +313,7 @@
&.btn-dark { &.btn-dark {
background-color: $dark ; background-color: $dark ;
color: $white; color: $medium;
} }
&.btn-medium { &.btn-medium {
@ -469,6 +481,7 @@
&.btn-icon { &.btn-icon {
padding: 0px !important; padding: 0px !important;
font-weight: bold;
max-width: 60px; max-width: 60px;
&.btn-xl { max-width: 80px; } &.btn-xl { max-width: 80px; }
@ -495,6 +508,30 @@
} }
} }
&.btn-social {
position: relative;
&:hover .icon,
.number {
@include scale(0,0);
opacity: 0;
}
&:hover .number {
@include transition( all 0.1s 0.1s ease-in-out);
@include scale(1,1);
opacity: 1;
}
.number,
.icon {
@include transition( all 0.1s 0s ease-in-out);
position: absolute;
top: 0;
left: 0;
}
}
&.btn-md.btn-icon-labeled { &.btn-md.btn-icon-labeled {
.icon:before { .icon:before {
line-height: 29px; line-height: 29px;
@ -530,6 +567,7 @@
.icon:before {line-height: 42px; } .icon:before {line-height: 42px; }
.icon-label { .icon-label {
font-size: 11px; font-size: 11px;
text-transform: capitalize;
text-align: center; text-align: center;
margin: 8px 0; margin: 8px 0;
display: block; display: block;
@ -542,7 +580,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
padding: 0 0px; padding: 0 0px;
font-weight: 300; font-weight: bold;
} }
&.hover { &.hover {
@ -676,6 +714,7 @@
} }
> * { > * {
border-radius: 0 !important;
background-clip: padding-box; background-clip: padding-box;
width: 100%; width: 100%;
float: left; float: left;
@ -736,7 +775,7 @@
} }
} }
.btn-group { .btn-group {
//@include scale(0,0); @include scale(0,0);
//@include transition( all 0.1s 0s ease-in-out); //@include transition( all 0.1s 0s ease-in-out);
position: absolute; position: absolute;
@ -748,7 +787,7 @@
margin-left: -12px; margin-left: -12px;
.btn { .btn {
//@include scale(0,0); @include scale(0,0);
//@include transition( all 0.1s 0.05s ease-in-out); //@include transition( all 0.1s 0.05s ease-in-out);
@ -940,7 +979,31 @@
} }
} }
// !btn-group .btn-group.bottom-left > .btn {
border-radius: 0px;
&:first-child{
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
}
&.last,
&:last-child{
border-top-right-radius: 3px;
border-bottom-right-radius: 0px;
}
}
.btn-xyz {
position: relative;
display: inline-block;
line-height: 0px;
padding: 0px;
font-size: 0px;
vertical-align: middle;
white-space: nowrap;
@include clearfix;
min-height: 44px;
}
.btn-group { .btn-group {
position: relative; position: relative;
@ -951,16 +1014,13 @@
vertical-align: middle; vertical-align: middle;
white-space: nowrap; white-space: nowrap;
//border: 1px solid $dark;
border-radius: 5px;
&.dark { &.dark {
border-radius: $radius; border-radius: $radius;
background-color: $dark; background-color: $dark;
color: $white; color: $lighter;
.btn { .btn {
color: $white; color: $lighter;
} }
} }

View File

@ -96,14 +96,15 @@
border-bottom-right-radius: $radius*3; border-bottom-right-radius: $radius*3;
} }
.dialog-account {
width: 600px;
margin: auto;
margin-top: 100px;
}
.dialog { .dialog {
font-size: 13px;
ol, ul, p {
font-size: inherit;
}
> .btn-block:last-child { > .btn-block:last-child {
border-top-left-radius: 0px; border-top-left-radius: 0px;
border-top-right-radius: 0px; border-top-right-radius: 0px;
@ -111,21 +112,24 @@
border-bottom-right-radius: $radius*3; border-bottom-right-radius: $radius*3;
} }
position: absolute; min-width: 200px;
font-size: 15px;
border: 1px solid black; @include backface-visibility(hidden);
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
border-radius: 5px;
white-space: normal; white-space: normal;
z-index: 1000;
position: absolute;
// white-space: normal;
opacity: 0; opacity: 0;
@include transition(all 0.125s ease-in-out); @include user-select(none);
@include transition( all 0.125s ease-in-out);
pointer-events: none; pointer-events: none;
background-color: $light; background-color: $light;
color: $dark; color: $medium;
&.dark { &.dark {background-color: $dark; }
background-color: $dark;
} border-radius: $radius*3;
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05), 0 2px 7px rgba(0, 0, 0, 0.1);
.dialog-tabs-wrapper { .dialog-tabs-wrapper {
overflow: hidden; overflow: hidden;
@ -146,13 +150,15 @@
&:hover span {color: $dark; } &:hover span {color: $dark; }
&.open span { &.open span {
background-color: white; background-color: $light;
color: $dark; color: $dark;
opacity: 1; opacity: 1;
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05), 0 2px 7px rgba(0, 0, 0, 0.1) !important;
border-bottom-right-radius: 0px !important; border-bottom-right-radius: 0px !important;
border-bottom-left-radius: 0px !important; border-bottom-left-radius: 0px !important;
border-top-left-radius: $radius*3; border-top-left-radius: $radius*3;
border-top-right-radius: $radius*3; border-top-right-radius: $radius*3;
} }
&:first-child span { &:first-child span {
@ -194,6 +200,7 @@
text-align: center; text-align: center;
} }
.dialog-section { .dialog-section {
&:first-child {border: none !important; } &:first-child {border: none !important; }
border-top: 2px solid rgba(0,0,0,0.1); border-top: 2px solid rgba(0,0,0,0.1);
@ -221,13 +228,4 @@
h4 .icon { h4 .icon {
height: 38px; height: 38px;
} }
// account dialog
&.dialog-freestanding {
margin: auto;
position: relative;
top: 150px;
border: none;
width: 800px;
}
} }

View File

@ -43,6 +43,9 @@ $predelay: 0;
&.hover:hover, &.hover:hover,
&.open { &.open {
// &:before {opacity: 0.125; }
// pointer-events: auto;
background-color: $dark;
background-color: $light; background-color: $light;
> * { > * {
@ -108,8 +111,8 @@ $predelay: 0;
} }
&:last-child > .btn{ &:last-child > .btn{
border-top-right-radius: $radius; border-top-right-radius: $radius ;
border-bottom-right-radius: $radius; border-bottom-right-radius: $radius ;
} }
} }
} }
@ -119,10 +122,6 @@ $predelay: 0;
position: relative; position: relative;
vertical-align: middle; vertical-align: middle;
a {
text-decoration: none;
}
&.dropdown-block { &.dropdown-block {
display: block; display: block;
.dropdown-toggle { .dropdown-toggle {
@ -144,7 +143,8 @@ $predelay: 0;
&.light > .dropdown-menu, &.light > .dropdown-menu,
&.light > .dialog { &.light > .dialog {
background: white; background: $light;
color: $medium;
} }
> .dropdown-menu { > .dropdown-menu {
@ -189,6 +189,8 @@ $predelay: 0;
} }
} }
&.hover:hover > .dialog, &.hover:hover > .dialog,
&.hover:hover > .dropdown-menu, &.hover:hover > .dropdown-menu,
@ -204,7 +206,9 @@ $predelay: 0;
&.open { &.open {
> .dialog, > .dialog,
> .dropdown-menu { > .dropdown-menu {
//transform: translate3d(-50%, -50%, 100px) scale(1); -webkit-transform: translate3d(-50%, -50%, 100px) scale(1);
-ms-transform: translate3d(-50%, -50%, 100px) scale(1);
transform: translate3d(-50%, -50%, 100px) scale(1);
} }
} }
@ -213,8 +217,10 @@ $predelay: 0;
left: 50%; left: 50%;
top: 50%; top: 50%;
margin-top: 0px; margin-top: 0px;
//@include transform-origin(center center); @include transform-origin(center center);
//transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8); -webkit-transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
-ms-transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
transform: translate3d(-50%, -50%, 100px) scale(0.93,0.8);
} }
} }
@ -224,8 +230,10 @@ $predelay: 0;
top: auto; top: auto;
bottom: 100%; bottom: 100%;
margin-bottom: 16px; margin-bottom: 16px;
//@include transform-origin(bottom left); @include transform-origin(bottom left);
//transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8); -webkit-transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
-ms-transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
transform: translate3d(-33%, 0%, 100px) scale(0.93,0.8);
} }
} }
@ -235,8 +243,10 @@ $predelay: 0;
top: auto; top: auto;
bottom: 100%; bottom: 100%;
margin-bottom: 16px; margin-bottom: 16px;
//@include transform-origin(bottom center); @include transform-origin(bottom center);
//transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8); -webkit-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
-ms-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
} }
} }
@ -247,16 +257,10 @@ $predelay: 0;
top: 100%; top: 100%;
bottom: auto; bottom: auto;
margin-top: -16px; margin-top: -16px;
//@include transform-origin(top center); @include transform-origin(top center);
//transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8); -webkit-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
} -ms-transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
} transform: translate3d(-50%, 0%, 100px) scale(0.93,0.8);
&.top.left {
> .dialog,
> .dropdown-menu {
left: 70px;
margin-top: -60px;
} }
} }
@ -266,18 +270,20 @@ $predelay: 0;
top: 100%; top: 100%;
bottom: auto; bottom: auto;
left: auto; left: auto;
right: 0;
right: 70px; margin-top: 16px;
margin-top: -60px; @include transform-origin(top right);
-webkit-transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
//@include transform-origin(top right); -ms-transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
//transform: translate3d(0%, 0%, 100px) scale(0.93,0.8); transform: translate3d(0%, 0%, 100px) scale(0.93,0.8);
} }
&.hover:hover, &.hover:hover,
&.open { &.open {
> .dialog, > .dialog,
> .dropdown-menu { > .dropdown-menu {
//transform: translate3d(0%, 0%, 100px) scale(1); -webkit-transform: translate3d(0%, 0%, 100px) scale(1);
-ms-transform: translate3d(0%, 0%, 100px) scale(1);
transform: translate3d(0%, 0%, 100px) scale(1);
} }
} }
@ -306,7 +312,9 @@ $predelay: 0;
> .dialog, > .dialog,
> .dropdown-menu { > .dropdown-menu {
//transform: translate3d(-50%, 0%, 100px) scale(1); -webkit-transform: translate3d(-50%, 0%, 100px) scale(1);
-ms-transform: translate3d(-50%, 0%, 100px) scale(1);
transform: translate3d(-50%, 0%, 100px) scale(1);
} }
} }
} }
@ -316,7 +324,9 @@ $predelay: 0;
&.open { &.open {
> .dialog, > .dialog,
> .dropdown-menu { > .dropdown-menu {
//transform: translate3d(-33%, 0%, 100px) scale(1) !important; -webkit-transform: translate3d(-33%, 0%, 100px) scale(1) !important;
-ms-transform: translate3d(-33%, 0%, 100px) scale(1) !important;
transform: translate3d(-33%, 0%, 100px) scale(1) !important;
} }
} }
} }
@ -324,7 +334,7 @@ $predelay: 0;
.dropdown { .dropdown {
/*&.options-3 { &.options-3 {
&.option-1:after { margin-left: -68px;} &.option-1:after { margin-left: -68px;}
&.option-2:after { margin-left: -8px;} &.option-2:after { margin-left: -8px;}
&.option-3:after { margin-left: 52px;} &.option-3:after { margin-left: 52px;}
@ -338,9 +348,8 @@ $predelay: 0;
-webkit-transform: scale(1); -webkit-transform: scale(1);
-ms-transform: scale(1); -ms-transform: scale(1);
transform: scale(1); transform: scale(1);
}*/ }
/*
&:after { &:after {
@include transition( all 0.1s ease-in-out 0s); @include transition( all 0.1s ease-in-out 0s);
content: ""; content: "";
@ -353,24 +362,26 @@ $predelay: 0;
margin-left: -8px; margin-left: -8px;
pointer-events: none !important; pointer-events: none !important;
left: 50%; left: 50%;
//transform: scale(0,0); -webkit-transform: scale(0,0);
-ms-transform: scale(0,0);
transform: scale(0,0);
} }
&.bottom:after, &.bottomleft:after { &.bottom:after, &.bottomleft:after {
//@include transform-origin(bottom center); @include transform-origin(bottom center);
bottom: 100%; bottom: 100%;
border-bottom: 8px solid transparent; border-bottom: 8px solid transparent;
border-right: 8px solid transparent; border-right: 8px solid transparent;
border-top: 8px solid #303030; border-top: 8px solid #303030;
border-left: 8px solid transparent; border-left: 8px solid transparent;
} }
*/
/*&.top:after { &.top:after {
@include transform-origin(top center); @include transform-origin(top center);
top: 100%; top: 100%;
border-bottom: 8px solid #303030; border-bottom: 8px solid #303030;
border-right: 8px solid transparent; border-right: 8px solid transparent;
border-top: 8px solid transparent; border-top: 8px solid transparent;
border-left: 8px solid transparent; border-left: 8px solid transparent;
}*/ }
} }

View File

@ -254,6 +254,7 @@
// word-wrap: break-word; // word-wrap: break-word;
.item { .item {
box-shadow: 0 0 1pxrgba(0,0,0,0.1);
display: inline-block; display: inline-block;
text-align: left; text-align: left;
padding-right: $folder-gutter*2; padding-right: $folder-gutter*2;
@ -396,10 +397,7 @@
&: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 1px 1px rgba(0, 0, 0, 0.025), 0 2px 7px rgba(0, 0, 0, 0.025);
border: 1px solid black;
// ???
@include opacity(1); @include opacity(1);
color: $medium; color: $medium;
// color: white; // color: white;
@ -478,6 +476,7 @@
left: 0px; left: 0px;
z-index: 100; z-index: 100;
width: auto; width: auto;
background-color: rgba(255,255,255,1);
.dropdown { .dropdown {
position: absolute; position: absolute;
@ -502,6 +501,30 @@
color: $dark; color: $dark;
text-align: left; text-align: left;
} }
.item-social {
padding: 8px;
border-right: 2px solid rgba(0,0,0,0.025);
@include clearfix;
color: $medium;
.item-likes,
.item-comments,
.item-shares {
position: relative;
&:hover {
.icon {opacity: 0; }
.number {opacity: 1; }
}
.number {
position: absolute;
opacity: 0;
top: 0;
left: 0;
}
.icon {opacity: 0.5; }
}
}
} }
.item-appendix { .item-appendix {

View File

@ -28,6 +28,7 @@
line-height: 1.5; line-height: 1.5;
width: 100%; width: 100%;
text-align: left; text-align: left;
color: $medium;
font-weight: normal; font-weight: normal;
cursor: pointer; cursor: pointer;
border-radius: $radius; border-radius: $radius;

View File

@ -2,14 +2,24 @@
@import "mixins"; @import "mixins";
.input-select { .input-select {
background-color: rgba(255,255,255,0.04); // background-color: rgba(255,255,255,0.04);
background-image: url('images/select_arrow.gif'); // background-image: url('images/select_arrow.gif');
border-radius: $radius; border-radius: $radius;
display: inline-block; display: inline-block;
width: 100%; width: 100%;
} }
@-moz-document url-prefix() {
select.input{
background-repeat: no-repeat;
background-position: right center;
cursor: pointer;
}
}
select { select {
-webkit-appearance:none;
// -moz-appearance:window;
appearance:none; appearance:none;
padding-left: 0px; padding-left: 0px;
width: 100%; width: 100%;

View File

@ -23,6 +23,7 @@ input:invalid {
top: 0; top: 0;
right: 0; right: 0;
line-height: 1; line-height: 1;
font-size: 10px;
margin: 12px; margin: 12px;
color: red; color: red;
margin-right: 25px; margin-right: 25px;
@ -112,26 +113,43 @@ select {
&.input-white { &.input-white {
background-color: white; background-color: white;
color: $medium;
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, 0.05), inset 0 0px 4px rgba(0, 0, 0, 0.1); box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, 0.05), inset 0 0px 4px rgba(0, 0, 0, 0.1);
} }
&.input-light { &.input-light {
background-color: $light; background-color: $light;
color: $medium;
} }
&.input-dark { &.input-dark {
background-color: $darker; background-color: $darker;
color: $medium;
} }
&.input-lighten { &.input-lighten {
background-color: rgba(255,255,255,0.05); background-color: rgba(255,255,255,0.05);
color: $medium !important;
} }
&.input-darken { &.input-darken {
background-color: rgba(0,0,0,0.05); background-color: rgba(0,0,0,0.05);
color: $medium;
} }
&.input-transparent { &.input-transparent {
background-color: transparent; background-color: transparent;
color: $medium;
}
// &:focus {color: white; }
&:invalid {
// background-color: rgba(198,101,84,0.05);
// color: rgba(198,101,84,0.75)
&:after {
}
} }
@include input-focus(); @include input-focus();

View File

@ -69,27 +69,26 @@
} }
.handles { .handles {
//border: 1px solid rgba(255,255,255,0.5); // background-color: rgba(40,140,215,0.45);
border: 1px solid rgba(255,255,255,0.5);
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
bottom: -1; bottom: 0;
right: 0; right: 0;
z-index: 800; z-index: 800;
pointer-events: none; pointer-events: none;
background: rgba(255,255,255,0.1);
&:after{ &:after{
border: 4px dotted #000000; border: 1px dotted rgba(40,140,215,1);
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
height: auto; height: auto;
width: auto; width: auto;
top: 0px; top: -1px;
left: 0px; left: -1px;
right: 0px; right: -1px;
bottom: -1px; bottom: -1px;
} }
} }
@ -98,7 +97,7 @@
border: 8px solid rgba(255,255,255,0.5); border: 8px solid rgba(255,255,255,0.5);
&:after{ &:after{
border: 8px dotted #000000; border: 8px dotted rgba(40,140,215,1);
top: -4px; top: -4px;
left: -4px; left: -4px;
right: -4px; right: -4px;
@ -333,14 +332,15 @@
pointer-events:auto; pointer-events:auto;
z-index: 2000; z-index: 2000;
position: absolute; position: absolute;
width: 30px !important;
height: 30px !important;
border-radius: 100%; border-radius: 100%;
margin: -15px;
border: 1px solid black; border: 1px solid rgba(0,0,0,0.25);
margin: -5px;
padding: 4px;
&:hover { &:hover {
background-color: black; background-color: rgba(255,255,255,0.5);
cursor: move; cursor: move;
} }
} }
@ -428,7 +428,14 @@
border-style: solid; border-style: solid;
border-width: 10px; border-width: 10px;
border-color: transparent; border-color: transparent;
-webkit-background-clip: padding-box;
-moz-background-clip: padding-box;
background-clip: padding-box; background-clip: padding-box;
-webkit-transition: all .05s ease-in-out;
-moz-transition: all .05s ease-in-out;
-ms-transition: all .05s ease-in-out;
-o-transition: all .05s ease-in-out;
transition: all .05s ease-in-out; transition: all .05s ease-in-out;
} }

View File

@ -5,6 +5,7 @@
.header-left, .header-left,
.header-right { .header-right {
position: absolute; position: absolute;
//@include transition( all 0.25s ease-in-out);
@include backface-visibility(hidden); @include backface-visibility(hidden);
z-index: 3000; z-index: 3000;
top: 10px; top: 10px;
@ -26,21 +27,21 @@
.home { .home {
margin-top: -20px; margin-top: -20px;
margin-left: -20px; margin-left: -20px;
// .icon {color: $dark; }
} }
.header-left { .header-left {
@include transform-origin(center left);
left: 0; left: 0;
padding-left: 10px; padding-left: 10px;
padding-left: 20px;
padding-top: 20px;
} }
.header-right { .header-right {
@include transform-origin(center right);
right: 0; right: 0;
padding-right: 20px; padding-right: 10px;
padding-top: 20px;
} }
.header-center { .header-center {
@include transform-origin(center center);
width: 100%; width: 100%;
left: 0; left: 0;
right: 0; right: 0;
@ -55,7 +56,7 @@
} }
} }
.header-left > * { margin-right: 10px; } .header-left > * { margin-right: 10px; }
.header-right > * { margin-left: 10px; } .header-right > * { margin-left: 5px; }
.header-right { font-size: 0;} .header-right { font-size: 0;}
.title { .title {
@ -89,3 +90,21 @@
opacity: 0.5; opacity: 0.5;
} }
} }
.present-mode #space-header {
background-color: transparent !important;
}
#space-siblings {
background-color: rgba(245, 245, 245, 0.95);
padding: 35px;
max-height: 450px;
overflow-y: scroll;
margin-top: 54px;
border-bottom: 1px solid #eee;
.btn {
margin-bottom: 50px;
}
}

View File

@ -85,12 +85,3 @@
transform: rotateZ(45deg) translateX(-8px); transform: rotateZ(45deg) translateX(-8px);
} }
.icon-svg {
background-size: 26px;
background-position: center;
background-repeat: no-repeat;
}
.icon-sd6 {
background-image: url(/images/sd6-icon-white.svg);
}

View File

@ -1,52 +1,257 @@
@import "vars"; @import "vars";
#landing-header { #landing-header {
background-color: white; background-color: rgba(255,255,255,0.3);
height: 64px; height: 64px;
position: relative; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
} }
#landing { .landing-keyvisual-wrapper {
margin-top: 100px; background-image: url("../images/sd5-keyvisual-compressed.jpg");
background-size: cover;
background-position: center;
padding-top: 40px;
padding-bottom: 40px;
}
section { .landing-plans-wrapper {
margin-left: 300px; background-image: url("../images/sd5-hero2-compressed.jpg");
background-size: cover;
background-position: center;
padding-top: 80px;
padding-bottom: 100px;
}
> * { .landing-box {
max-width: 600px; width: 800px;
margin: auto;
max-width: 90%;
background-color: white;
padding: 40px;
margin-bottom: 80px;
margin-top: 80px;
position: relative;
box-shadow: 0px 0px 50px rgba(0,0,0,0.2);
h1 {
margin-bottom: 20px;
} }
&.black {
background-color: #222;
color: white;
padding: 20px;
text-align: center;
}
&.overlap {
position: absolute;
z-index: 2;
margin-top: -65px;
left: 50%;
top: 0px;
margin-left: -250px;
width: 500px;
}
&.screenshot {
width: 90%;
max-width: 90%;
padding: 20px;
box-shadow: none;
background-color: transparent;
img {
width: 100%;
position: absolute;
top: 0px;
left: 0px;
opacity: 0.3;
}
}
&.landing-box-left {
margin-left: 30px;
}
}
.lead-xxl {
}
.lead {
margin-bottom: 20px;
}
.lead-xl {
}
.plans-box {
background: linear-gradient(to bottom, #FEFFFF 25%,#D0D8E2 100%);
padding: 40px;
border-radius: 9px;
}
.landing-box.plans-box {
margin-top: 200px;
width: 900px;
}
.plans-table {
tr {
vertical-align: top;
}
th {
font-size: 42px;
padding-top: 40px;
text-align: center;
}
th.best-plan {
padding-top: 20px;
font-size: 48px;
padding-bottom: 0px;
}
td {
padding: 20px;
width: 30%;
p, li {
font-size: 18px;
}
li {
margin-bottom: 10px;
}
}
td.best-plan {
width: 40%;
p {
font-size: 22px;
}
}
td li {
list-style-type: none;
text-align: center;
}
ul {
margin: 0 !important;
padding: 0 !important;
}
.upgrade-buttons {
text-align:center;
margin-top:20px;
}
}
.logo-row {
position: relative;
padding: 80px;
background-color: white;
text-align: center;
width: 100%;
&.blue {
background-color: $blue;
color: white;
}
}
.logo-row div {
display: inline-block;
width: 200px;
}
.landing-row {
background-color: white;
padding-bottom: 80px;
padding-top: 40px;
}
#keyvisual {
border-radius: 20px;
box-shadow: 0px 0px 20px #eee;
width: 640px;
height: 420px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-image: url('/images/landing/spacedeck-screenshot1.jpg');
background-color: white;
margin: auto;
margin-top: 40px;
margin-bottom: 40px;
border: 1px solid #eee;
}
#legal {
.landing-box {
width: 800px;
} }
} }
.footer { .footer {
margin-left: 300px; padding: 40px;
margin-top: 100px; padding-bottom: 80px;
margin-bottom: 100px; text-align: center;
color: $medium;
a {
margin-right: 20px;
}
} }
@media screen and (max-width: 1000px) {
#landing { @media screen and (min-width: 801px) {
section { .plans-table-mobile {
margin-left: 20px; display: none;
margin-right: 20px;
} }
} }
.footer {
margin-left: 20px; @media screen and (max-width: 800px) {
margin-right: 20px;
ul.lead.lead-xl, p.lead.lead-xl, ol.lead.lead-xl {
font-size: 20px !important;
} }
.header-right { .header-right {
right: auto; > span:first-child {
padding-left: 10px; display: none;
padding-right: 20px; }
padding-top: 80px;
} }
#folder-wrapper { .plans-table {
padding-top: 128px; display: none;
}
.plans-table-mobile {
display: block;
tbody {
display: block;
width: 100%;
}
tr {
display: block;
width: 100%;
}
td, th {
display: block;
width: 100%;
}
ul, li {
width: 100%;
}
} }
} }

View File

@ -2,6 +2,7 @@
@import "mixins"; @import "mixins";
.wrapper { .wrapper {
//@include transition( all 0.25s ease-in-out);
position: relative; position: relative;
margin: auto; margin: auto;
max-width: 1160px; max-width: 1160px;

View File

@ -59,8 +59,8 @@
} }
.close { .close {
margin-left: 44px; position: fixed;
margin-bottom: 44px; margin: 44px 44px;
.icon {display: block; } .icon {display: block; }
} }
@ -135,6 +135,7 @@
outline: none; outline: none;
display: inline-block; display: inline-block;
text-align: left; text-align: left;
@include user-select(none);
border-radius: $radius*3; border-radius: $radius*3;
background-color: $light !important; background-color: $light !important;
@ -145,6 +146,7 @@
.modal-header { .modal-header {
padding: 30px 40px; padding: 30px 40px;
position: relative; position: relative;
color: $medium;
} }
.close-search { .close-search {
@ -277,5 +279,25 @@
// Footer (for actions) // Footer (for actions)
.modal-footer { .modal-footer {
margin-top: 20px; // border-bottom-left-radius: $radius;
// border-bottom-right-radius: $radius;
// background-color: $dark !important;
// padding: 40px;
// padding-top: 0px;
// text-align: right; // right align buttons
@include clearfix(); // clear it in case folks use .pull-* classes on buttons
// Properly space out buttons
// .btn + .btn {
// margin-left: 5px;
// margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs
// }
// // but override that for button groups
// .btn-group .btn + .btn {
// margin-left: -1px;
// }
// // and override it for block buttons as well
// .btn-block + .btn-block {
// margin-left: 0;
// }
} }

12
styles/normalize.scss vendored
View File

@ -1,5 +1,17 @@
/*! normalize.css v3.0.0 | MIT License | git.io/normalize */ /*! normalize.css v3.0.0 | MIT License | git.io/normalize */
//
// 1. Set default font family to sans-serif.
// 2. Prevent iOS text size adjust after orientation change, without disabling
// user zoom.
//
html {
font-family: sans-serif; // 1
-ms-text-size-adjust: 100%; // 2
-webkit-text-size-adjust: 100%; // 2
}
// //
// Remove default margin. // Remove default margin.
// //

View File

@ -27,5 +27,6 @@
right: 0; right: 0;
z-index: 800; z-index: 800;
pointer-events: none; pointer-events: none;
opacity: 0.25;
display: block; display: block;
} }

View File

@ -6,18 +6,22 @@
li { li {
&.checked { &.checked {
&:before {background-color: $medium !important; }
> a, > a,
> span { > span {
color: $medium;
} }
} }
&:hover { &:hover {
&:before {background-color: $medium; }
> a, > a,
> span { > span {
background-color: rgba(0,0,0,0.025) !important; background-color: rgba(0,0,0,0.025) !important;
} }
} }
&:before {background-color: $medium; }
> a, > a,
> span { > span {
color: $medium; color: $medium;
@ -26,7 +30,7 @@
} }
.select-list { .select-list {
&:empty:before { &:empty:before{
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -41,14 +45,17 @@
opacity: 0.5; opacity: 0.5;
} }
-webkit-mask-image: -webkit-gradient(linear, left top, left 15px, from(rgba(0,0,0,0)), to(rgba(0,0,0,0.5)));
background-clip: padding-box; background-clip: padding-box;
//font-size: 15px; font-size: 15px;
//line-height: 14px; line-height: 14px;
list-style: none; list-style: none;
margin: 0px; margin: 0px;
padding: 15px 0; padding: 15px 0;
text-align: left; text-align: left;
// background-color: $dark; // background-color: $dark;
color: $medium;
border-radius: $radius; border-radius: $radius;
.divider + li span {border: none !important; } .divider + li span {border: none !important; }
@ -83,11 +90,15 @@
} }
&:hover { &:hover {
background-color: black; // background-color: rgba(0,0,0,0.025);
&:before {
background-color: $medium;
display: block;
}
> a, > a,
> span { > span {
color: white; color: $medium;
color: $dark;
} }
} }
@ -115,8 +126,9 @@
display: block; display: block;
cursor: pointer; cursor: pointer;
white-space: nowrap; white-space: nowrap;
color: $medium;
margin: 0 25px; margin: 0 25px;
padding: 10px 0px; padding: 16px 3px;
// line-height: 50px; // line-height: 50px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;

View File

@ -118,7 +118,7 @@
padding: 0 !important; padding: 0 !important;
.wrapper { .wrapper {
border: 4px solid black; border: 1px dotted rgba(128,128,128,0.5);
transition-duration: 0.25s; transition-duration: 0.25s;
transition-property: width, height, background-color; transition-property: width, height, background-color;
@ -132,27 +132,32 @@
max-height: 100%; max-height: 100%;
position: relative; position: relative;
overflow: scroll; overflow: scroll;
/** {
-moz-user-select: none !important; // firefox has selection problems
}*/
} }
.snap-ruler-h { .snap-ruler-h {
pointer-events: none; pointer-events: none;
position: fixed; position: fixed;
z-index: 2000; z-index: 0;
right: 0px; right: 0px;
height: 1px; height: 1px;
background-color: black; background-color: rgba(0,0,0,0.5);
left: 0px; left: 0px;
} }
.snap-ruler-v { .snap-ruler-v {
pointer-events: none; pointer-events: none;
position: fixed; position: fixed;
z-index: 2000; z-index: 0;
top: 0px; top: 0px;
bottom: 0px; bottom: 0px;
width: 1px; width: 1px;
background-color: black; background-color: rgba(0,0,0,0.5);
} }
.cursor { .cursor {
@ -222,12 +227,30 @@
} }
#space { #space {
// user-select: all; /*-webkit-user-select: all;
-ms-user-select: all;
-moz-user-select: all;
user-select: all;*/
position: relative; position: relative;
height: 100% !important; height: 100% !important;
//padding-top: 64px !important;
background-color: #eee; background-color: #eee;
} }
#made-with {
position: fixed;
width: 100%;
bottom: 0;
padding: 12px;
opacity: 0.25;
a {color: $dark; }
p {
text-align: center;
font-size: 11px;
}
}
#baseline { #baseline {
position: absolute; position: absolute;
width: 100%; width: 100%;
@ -275,8 +298,8 @@
.space-bounds { .space-bounds {
position: absolute; position: absolute;
left: 0; left: 0px;
top: 0; top: 0px;
pointer-events: none; pointer-events: none;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;

View File

@ -65,15 +65,10 @@
html, html,
body { body {
height:100%; height:100%;
-webkit-tap-highlight-color: transparent;
background-color: white; background-color: white;
color: $black; background-color: $light;
} color: $darker;
body {
max-width: 100%;
padding: 0px;
text-rendering: optimizeLegibility;
cursor: default;
} }
*[contenteditable="true"] { *[contenteditable="true"] {
@ -86,12 +81,70 @@ body {
@include box-sizing(border-box); @include box-sizing(border-box);
} }
body {
max-width: 100%;
padding: 0px;
text-rendering: optimizeLegibility;
//@include user-select(none);
cursor: default;
}
.img img { .img img {
max-width: 100%; max-width: 100%;
height: auto; height: auto;
} }
/*.layer { .plan {
color: $medium;
border-radius: $radius;
display: inline-block;
padding: 30px;
background-color: transparent;
border: 2px solid rgba(0,0,0,0.05);
width: 100%;
&.active {
background-color: white;
border: none;
}
h4 {
color: black;
margin-bottom: 0px;
}
p {
font-size: 13px;
line-height: 1.4;
margin-top: 5px;
margin-bottom: 5px;
}
ul {
list-style: none;
font-size: 10px;
margin: 0px;
padding: 0px;
border-top: 2px solid rgba(0,0,0,0.05);
padding-top: 20px;
margin-top: 20px;
margin-bottom: 20px;
li {padding-top: 2px; }
}
}
#startup {
background-position: center;
background-image:url(/images/diamond.svg);
background-repeat: no-repeat;
}
#home {
background-color: white;
}
.layer {
@include transition( all 0.2s ease-in-out); @include transition( all 0.2s ease-in-out);
@include backface-visibility(hidden); @include backface-visibility(hidden);
position: absolute; position: absolute;
@ -119,7 +172,7 @@ body {
pointer-events: auto; pointer-events: auto;
opacity: 1; opacity: 1;
} }
}*/ }
[draggable] { [draggable] {
-moz-user-select: none; -moz-user-select: none;

View File

@ -8,9 +8,10 @@
} }
.table { .table {
width: 100%; width: 100%;
color: $medium;;
font-family: $main-font; font-family: $main-font;
border-radius: $radius; // border-radius: $radius;
border: 2px solid rgba(0,0,0,0.0125); // border: 2px solid rgba(0,0,0,0.0125) !important;
} }
.table thead > tr > th:first-child, .table thead > tr > th:first-child,

View File

@ -19,23 +19,50 @@
} }
margin: auto; margin: auto;
//text-align: center;
position: fixed; position: fixed;
top: 20px; bottom: 0px;
//width: 100%;
z-index: 3000; z-index: 3000;
padding: 0; padding: $gutter-b;
font-size: 0; font-size: 0;
line-height: 0; line-height: 0;
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15); transition-duration: 0.15s;
border: 1px solid black; transition-timing-function: ease-in-out;
border-radius: 5px; transition-delay: initial;
transition-property: opacity, transform;
// FIXME questionable? @include backface-visibility(hidden);
@include translate3d(0, 10px, 0);
pointer-events: none !important; pointer-events: none !important;
opacity: 0;
&.out {
@include translate3d(0, 10px, 0);
* {pointer-events: none !important; }
button, input, .dialog {
display: none;
}
}
&.in {
@include translate3d(0, 0, 0);
&.out {
@include translate3d(0, 10px, 0);
* {pointer-events: none !important; }
}
}
> * { > * {
margin: 0 2px;
margin-top: 4px;
pointer-events: auto !important; pointer-events: auto !important;
&.out {
margin: 0;
opacity: 0;
}
} }
&.toolbar-vertical { &.toolbar-vertical {
@ -160,6 +187,7 @@
} }
.toolbar-properties { .toolbar-properties {
bottom: 64px;
z-index: 0; z-index: 0;
&.in { &.in {
@ -168,12 +196,12 @@
.icon-sm { .icon-sm {
z-index: 110; z-index: 110;
//background-color: #222; background-color: #222;
border-radius: 50px; border-radius: 50px;
} }
.jewel { .jewel {
border: 2px solid #888; border: 2px solid rgba(255,255,255,0.5);
background-color: transparent; background-color: transparent;
color: #989898; color: #989898;
width: 36px; width: 36px;
@ -200,22 +228,5 @@
.toolbar-elements > .btn-group, .toolbar-elements > .btn-group,
.toolbar-properties > .btn-group { .toolbar-properties > .btn-group {
//box-shadow: 0 0 30px rgba(0,0,0,0.5); box-shadow: 0 0 30px rgba(0,0,0,0.5);
background-color: $white;
}
.toolbar-elements {
left: 20px;
}
.toolbar-properties {
right: 30px;
}
.zoom-bar {
position: absolute;
bottom: 30px;
right: 30px;
box-shadow: 0 0 30px 1px rgba(0, 0, 0, 0.15);
border: 1px solid black;
} }

View File

@ -33,6 +33,9 @@
@include translate3d(0, 0, 0); @include translate3d(0, 0, 0);
// @include backface-visibility(hidden); // @include backface-visibility(hidden);
-webkit-perspective: 1000;
-moz-perspective: 1000;
-ms-perspective: 1000;
perspective: 1000; perspective: 1000;
.panel-toggles { .panel-toggles {
@ -96,6 +99,9 @@
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
// @include backface-visibility(hidden); // @include backface-visibility(hidden);
-webkit-perspective: 1000;
-moz-perspective: 1000;
-ms-perspective: 1000;
perspective: 1000; perspective: 1000;
z-index: 1000; z-index: 1000;

View File

@ -1,8 +1,6 @@
@import "vars"; @import "vars";
@import "mixins"; @import "mixins";
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;900&display=swap');
body { body {
background-color: $light; background-color: $light;
color: $medium; color: $medium;
@ -27,7 +25,7 @@ hr {
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
color: inherit; color: inherit;
font-family: inherit; font-family: inherit;
font-weight: 900; font-weight: 500;
line-height: 1.3; line-height: 1.3;
margin-top: 0px; margin-top: 0px;
margin-bottom: 1em; margin-bottom: 1em;
@ -48,7 +46,8 @@ strong {font-weight: 500; }
small {font-size: 75%; } small {font-size: 75%; }
a { a {
color: black; text-decoration: none;
color: $medium;
} }
dl { dl {

View File

@ -9,6 +9,13 @@ $green: #2ecc71;
$red: #ff5955; $red: #ff5955;
$yellow: #f1c40f; $yellow: #f1c40f;
$light: #f5f5f5;
$lightish: #eee;
$facebook: #3e5b97;
$twitter: #2aa7de;
$color-1 : #4a2f7e; // purple $color-1 : #4a2f7e; // purple
$color-2 : #9b59b6; // lilac $color-2 : #9b59b6; // lilac
$color-3 : #3498db; // blue $color-3 : #3498db; // blue
@ -25,18 +32,15 @@ $black: #111; // black
$darker: #292929; $darker: #292929;
$dark: #222; // dark $dark: #222; // dark
$medium: #888; // medium $medium: #888; // medium
$light: #f5f5f5;
$lightish: #eee; // fixme
$lighter: #989898; $lighter: #989898;
$white: #ffffff;
$sidebar-width: 280px; $sidebar-width: 280px;
$main-font: Inter; $main-font: Avenir W01;
$sec-font: Inter; $sec-font: Avenir W01;
$font-size: 20px; $font-size: 18px;
$line-height: 1.5em; $line-height: 24px;
$gutter-a: 10px; $gutter-a: 10px;
$gutter-b: 20px; $gutter-b: 20px;

View File

@ -1,34 +1,62 @@
{% extends 'layouts/outer.html' %} {% extends 'layouts/outer.html' %}
{% block title %}Spacedeck{% endblock %} {% block title %}[[ __("welcome") ]]{% endblock %}
{% block content %} {% block content %}
<div id="landing"> <div id="landing">
<section> <div class="landing-keyvisual-wrapper">
<h1>Work Together, Visually.</h1> <div class="landing-box">
<p> <h2>[[__("landing_title")]]</h2>
Whenever you need to lay out pictures, text notes, video and audio clips on a blank canvas,
Spacedeck can help you. <p class="lead">
<a href="/signup" class="btn btn-primary btn-block btn-xl">[[__("signup")]]</a>
</p> </p>
<p>
Spacedeck is a browser based application. It is the right tool for you if you want to quickly put together a collage of your idea or concept, either for yourself or to share it with teammembers, clients or students. Changes are updated in realtime. <p class="lead">
<a href="/login" class="btn btn-primary btn-block btn-xl">[[__("login")]]</a>
</p> </p>
<p>
Spacedeck is not meant for creating polished designs, but it is a good fit for: <p class="lead">
[[__("landing_claim")]]
</p> </p>
<p class="lead">
[[__("landing_example")]]
</p>
<ul> <ul>
<li>Moodboards</li> <li class="lead">
<li>Collages</li> [[__("landing_features_1") | safe ]]
<li>Teaching (Virtual Blackboards)</li> </li>
<li>Shared Whiteboards</li>
<li>Design Thinking</li> <li class="lead">
[[__("landing_features_2") | safe ]]
</li>
<li class="lead">
[[__("landing_features_3") | safe ]]
</li>
<li class="lead">
[[__("landing_features_4") | safe ]]
</li>
<li class="lead">
[[__("landing_features_5") | safe ]]
</li>
<li class="lead">
[[__("landing_features_6") | safe ]]
</li>
<li class="lead">
[[__("landing_features_7") | safe ]]
</li>
</ul> </ul>
<img src="/images/sd6-screenshot.png" alt="Screenshot of Spacedeck 6.0"> </div>
<p> </div>
The hosted version of Spacedeck 6.0 is currently in beta and invite only. You can also self-host and <a href="https://github.com/spacedeck/spacedeck-open">participate in the open source development</a>.
</p>
</section>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -9,25 +9,39 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link href="[[ '/images/favicon.png' | cdn ]]" rel="icon" type="image/x-icon" /> <link href="[[ '/images/favicon.png' | cdn ]]" rel="icon" type="image/x-icon" />
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,700,600,800,300|Montserrat:400,700|EB+Garamond|Vollkorn|Lato|Roboto|Source+Code+Pro|Ubuntu|Raleway|Playfair+Display|Crimson+Text' rel='stylesheet' type='text/css'>
<link type="text/css" rel="stylesheet" href="https://fast.fonts.net/cssapi/ee1a3484-4d98-4f9f-9f55-020a7b37f3c5.css"/>
<link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]"> <link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]">
<script> var csrf_token = '[[ csrf_token ]]'; </script> <script> var csrf_token = '[[ csrf_token ]]'; </script>
<!--script src="[[ '/javascripts/jquery-2.1.4.min.js' | cdn ]]"></script--> <script src="[[ '/javascripts/jquery-2.1.4.min.js' | cdn ]]"></script>
</head> </head>
<body> <body>
<!--[if lt IE 10]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<header id="landing-header" class="header"> <header id="landing-header" class="header">
<div class="header-left"> <div class="header-left">
<a class="btn btn-transparent btn-nude" href="[[config.endpoint]]/"><img src="[[ '/images/sd6-logo-black.svg' | cdn ]]" width="190"></a> <a class="btn btn-transparent btn-nude" href="[[config.endpoint]]/"><img src="[[ '/images/sd5-logo.svg' | cdn ]]" width="190"></a>
</div> </div>
<div class="header-right pull-right"> <div class="header-right pull-right">
{% if !user %} {% if !user %}
<span class="btn-group dark round">
{% if (locale != "de") %}<a href="/t/de" rel="alternate" hreflang="de" class="btn btn-transparent btn-md">Deutsch</a>{% endif %}
{% if (locale != "en") %}<a href="/t/en" rel="alternate" hreflang="en" class="btn btn-transparent btn-md">English</a>{% endif %}
{% if (locale != "fr") %}<a href="/t/fr" rel="alternate" hreflang="fr" class="btn btn-transparent btn-md">Français</a>{% endif %}
</span>
<a class="btn btn-md btn-dark btn-round" href="/login">[[__("login")]]</a> <a class="btn btn-md btn-dark btn-round" href="/login">[[__("login")]]</a>
<a class="btn btn-md btn-dark btn-round" href="/signup">[[__("signup")]]</a> <a class="btn btn-md btn-blue btn-round" href="/signup">[[__("signup")]]</a>
{% else %} {% else %}
<a class="btn btn-md btn-dark btn-round" href="/spaces">[[__("spaces")]]</a> <a class="btn btn-md btn-blue 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>
{% endif %} {% endif %}
</div> </div>
@ -38,11 +52,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> <a href="/contact">[[ __("contact") ]]</a>
&copy; 2011–2019 Spacedeck GmbH (in liquidation)<br> <span style="color:#888">&copy; 2011–2018 The Spacedeck Open Developers <a href="https://github.com/spacedeck/spacedeck-open">https://github.com/spacedeck/spacedeck-open</a></span>
Source Code: <a href="https://github.com/mntmn/spacedeck-open">https://github.com/mntmn/spacedeck-open</a>
<br>
Font: <a href="https://rsms.me/inter/">Inter by rsms</a>
</div> </div>
</p> </p>
</div> </div>

View File

@ -1,30 +1,16 @@
<header id="dialog-header" class="header" v-if="(active_view == 'account' && user)" v-cloak> <div id="team" class="dialog in" style="padding:100px;z-index:20000;position:absolute;width:100%;min-height:100%;background-color:#eee" v-if="active_view == 'account' && user" v-cloak>
<div v-cloak class="header-left pull-left">
<a class="btn btn-dark btn-md btn-round btn-icon" href="/spaces">
<span class="icon icon-svg icon-sd6"></span>
</a>
<h5>Edit Account</h5>
</div>
<div class="header-right pull-right"> <a href="/spaces" class="btn btn-round btn-icon btn-dark btn-md pull-right" style="position:absolute;top:30px;right:30px"><span class="icon icon-cross-0"></span></a>
<a class="btn btn-dark btn-md btn-round btn-icon" href="/spaces">
<span class="icon icon-cross-0"></span>
</a>
</div>
</header>
<div class="dialog-freestanding dialog in" v-if="active_view == 'account' && user" v-cloak>
<div class="dialog-tabs" style="margin:auto"> <div class="dialog-tabs" style="margin:auto">
<div class="dialog-tab" v-bind:class="{open:account=='profile'}" v-on:click="account='profile'"><span>[[__("profile_caption")]]</span></div> <div class="dialog-tab" v-bind:class="{open:account=='profile'}" v-on:click="account='profile'"><span>[[__("profile_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='language'}" v-on:click="account='language'"><span>[[__("language_caption")]]</span></div> <div class="dialog-tab" v-bind:class="{open:account=='language'}" v-on:click="account='language'"><span>[[__("language_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='notifications'}" v-on:click="account='notifications'"><span>[[__("notifications_caption")]]</span></div> <div class="dialog-tab" v-bind:class="{open:account=='notifications'}" v-on:click="account='notifications'"><span>[[__("notifications_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='password'}" v-on:click="account='password'"><span>[[__("password_caption")]]</span></div> <div class="dialog-tab" v-if="user.account_type=='email'" v-bind:class="{open:account=='password'}" v-on:click="account='password'"><span>[[__("password_caption")]]</span></div>
<div class="dialog-tab" v-bind:class="{open:account=='terminate'}" v-on:click="account='terminate'"><span>[[__("terminate_caption")]]</span></div> <div class="dialog-tab" v-bind:class="{open:account=='terminate'}" v-on:click="account='terminate'"><span>[[__("terminate_caption")]]</span></div>
</div> </div>
<div class="dialog-section text-left"> <div class="dialog-section text-left" style="background-color:#f5f5f5;padding-top:40px;padding-bottom:40px">
<div class="collapse" v-bind:class="{in:account=='profile'}"> <div class="collapse" v-bind:class="{in:account=='profile'}">
<div class="labels-inline relative" style="margin-bottom:40px"> <div class="labels-inline relative" style="margin-bottom:40px">
<div class="form-group"> <div class="form-group">
@ -78,7 +64,29 @@
v-on:change="user.email_changed=true" v-on:change="user.email_changed=true"
placeholder="mail@example.com"> placeholder="mail@example.com">
<button class="btn btn-md btn-dark" v-on:click=" save_user()" style="margin-top:20px">Save</button> <button class="btn btn-md btn-darken" v-if="user.account_type=='email'" v-on:click=" save_user()" style="margin-top:20px">[[__("ok")]]</button>
</div>
<div class="form-group" v-if="!user.confirmed_at">
<p v-if="!user.confirmed_at && !account_confirmed_sent">[[__("confirmation_sent_long")]]</p>
<span
class="btn btn-xs btn-stroke-darken btn-round"
v-on:click=" confirm_again()"
v-if="!user.confirmed_at && !account_confirmed_sent"
>[[__("send_again")]]</span>
<p v-if="account_confirmed_sent">
<span class="icon icon-check"></span> <span>[[__("confirmation_sent_another")]]</span>
</p>
</div>
<div class="form-group">
<label class="label">Spacedeck.com Data Import</label>
<p v-if="!importables">No .ZIP files found in Spacedeck application folder.</p>
<ul>
<li v-for="f in importables">{{f}} <button v-on:click="start_zip_import(f)">Start Import</button></li>
</ul>
</div> </div>
</div> </div>
</div> </div>
@ -113,7 +121,7 @@
</div> </div>
<div class="collapse" v-bind:class="{in:account=='password'}"> <div class="collapse" v-bind:class="{in:account=='password'}">
<h4>Change Password</h4> <h4 class="modal-title">Change Password</h4>
<div class="modal-section labels-inline"> <div class="modal-section labels-inline">
<div class="form-group"> <div class="form-group">
<label class="label">[[__("current_password")]]</label> <label class="label">[[__("current_password")]]</label>
@ -132,17 +140,23 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<div class="btn-cluster">
<button <button
class="btn btn-dark btn-md" class="btn btn-transparent btn-block"
v-on:click="save_user_password(password_change_current, password_change_new, password_change_new_confirmation);" > v-on:click="save_user_password(password_change_current, password_change_new, password_change_new_confirmation);" >
[[__("change_password")]] [[__("change_password")]]
</button> </button>
</div> </div>
</div> </div>
</div>
<div class="collapse" v-bind:class="{in:account=='terminate'}"> <div class="collapse" v-bind:class="{in:account=='terminate'}">
<h4>Terminate Account</h4> <div class="">
<div class="modal-section labels-inline"> <p>[[__("terminate_warning")]]</p>
<p>[[__("terminate_warning2")]]</p>
</div>
<div class="labels-inline" v-if="user.account_type == 'email'">
<div class="form-group"> <div class="form-group">
<label class="label">[[__("current_password")]]</label> <label class="label">[[__("current_password")]]</label>
<input v-model="account_remove_password" class="input input-white no-b" type="password"> <input v-model="account_remove_password" class="input input-white no-b" type="password">
@ -153,15 +167,11 @@
<textarea class="input input-white no-b" v-model="account_remove_feedback"></textarea> <textarea class="input input-white no-b" v-model="account_remove_feedback"></textarea>
<p class="message">[[__("terminate_reason_caption")]]</p> <p class="message">[[__("terminate_reason_caption")]]</p>
</div> </div>
</div>
<div class="modal-section labels-inline">
<div class="center alert alert-danger" v-if="account_remove_error">{{account_remove_error}}</div> <div class="center alert alert-danger" v-if="account_remove_error">{{account_remove_error}}</div>
</div> </div>
<div class="modal-footer"> <button class="btn btn-transparent btn-block" v-on:click="remove_account(account_remove_password, account_remove_feedback)">[[__("terminate_terminate")]]</button>
<button class="btn btn-stroke-darken btn-md" v-on:click="remove_account(account_remove_password, account_remove_feedback)">Terminate Account</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,23 +1,15 @@
<header id="folder-header" class="header" v-if="(active_view == 'folders' && active_folder)" v-cloak> <header id="folder-header" class="header" v-if="(active_view == 'folders' && active_folder)" v-cloak>
<div v-cloak class="header-left pull-left"> <div v-cloak class="header-left pull-left">
<a class="btn btn-dark btn-md btn-round btn-icon" href="/spaces"> <a class="btn btn-stroke-darken btn-md btn-round btn-icon" href="/spaces">
<span class="icon icon-svg icon-sd6"></span> <span class="icon icon-home"></span>
</a> </a>
<button v-if="logged_in && (active_space_role == 'editor' || active_space_role == 'admin')" class="btn btn-dark btn-md btn-round" v-on:click="create_space('space')">[[ __('create_space') ]]</button> <button v-if="logged_in && (active_space_role == 'editor' || active_space_role == 'admin')" class="btn btn-primary btn-md btn-round" v-on:click="create_space('space')">[[ __('create_space') ]]</button>
<button v-if="logged_in && (active_space_role == 'editor' || active_space_role == 'admin')" class="btn btn-stroke-darken btn-md btn-round" v-on:click="create_space('folder')"> <button v-if="logged_in && (active_space_role == 'editor' || active_space_role == 'admin')" class="btn btn-stroke-darken btn-md btn-round" v-on:click="create_space('folder')">
<span>[[ __('create_folder') ]]</span> <span v-bind:class="{'disabled-pro':!is_pro(user)}">[[ __('create_folder') ]]</span>
</button> </button>
</div>
<label class="relative compact-hidden"> <div class="header-right pull-right">
<span class="icon icon-sm icon-zoom no-events absolute-top-left" style="margin: 5px;"></span>
<input id="folder-search"
type="search" name="search"
style="padding-left: 40px !important; margin-right: 10px;"
placeholder="[[ __('search') ]]"
class="input input-md input-white input-round no-b w-2"
v-model="folder_spaces_search" v-on:change="search_spaces">
</label>
<div class="dropdown top light m-r-20 compact-hidden" v-bind:class="{open : active_dropdown=='folder_sorting'}"> <div class="dropdown top light m-r-20 compact-hidden" v-bind:class="{open : active_dropdown=='folder_sorting'}">
<button class="btn btn-sm btn-nude" v-on:click="activate_dropdown('folder_sorting')"> <button class="btn btn-sm btn-nude" v-on:click="activate_dropdown('folder_sorting')">
<span>[[ __('sort_by') ]]</span>: <span>[[ __('sort_by') ]]</span>:
@ -41,15 +33,29 @@
v-on:click="set_folder_sorting('space_type',false)"> v-on:click="set_folder_sorting('space_type',false)">
<span>[[ __('type') ]]</span> <span>[[ __('type') ]]</span>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</div>
<div class="header-right pull-right"> <label class="relative compact-hidden">
<span class="icon icon-sm icon-zoom no-events absolute-top-left" style="margin: 5px;"></span>
<input id="folder-search"
type="search" name="search"
style="padding-left: 40px !important; margin-right: 10px;"
placeholder="[[ __('search') ]]"
class="input input-md input-white input-round no-b w-2"
v-model="folder_spaces_search" v-on:change="search_spaces">
</label>
<button class="btn btn-stroke-darken btn-md btn-round" v-if="!user.confirmed_at" v-on:click="confirm_again()">
<span v-if="!account_confirmed_sent">[[ __('email_unconfirmed') ]]</span>
<span v-if="account_confirmed_sent">[[ __('confirmation_sent') ]]</span>
</button>
<div class="dropdown top right light" v-bind:class="{open: active_dropdown=='account'}"> <div class="dropdown top right light" v-bind:class="{open: active_dropdown=='account'}">
<button <button
class="profile-avatar btn btn-md btn-icon btn-dark btn-round" class="profile-avatar btn btn-md btn-icon btn-darken btn-round"
v-bind:style="background_image_style([user.avatar_thumb_uri])" v-bind:style="background_image_style([user.avatar_thumb_uri])"
v-bind:class="{'has-avatar-image':!!user.avatar_thumb_uri}" v-on:click="show_account();"> v-bind:class="{'has-avatar-image':!!user.avatar_thumb_uri}" v-on:click="show_account();">
<span class="icon icon-user" v-if="logged_in && !user.avatar_thumb_uri"></span></button> <span class="icon icon-user" v-if="logged_in && !user.avatar_thumb_uri"></span></button>
@ -70,6 +76,13 @@
</a> </a>
</li> </li>
<li v-on:click="activate_modal('support')">
<span>
<span class="icon icon-sm icon-info"></span>
<span>[[ __('support') ]]</span>
</span>
</li>
<li v-on:click="logout()"> <li v-on:click="logout()">
<span> <span>
<span class="icon icon-sm icon-logout"></span> <span class="icon icon-sm icon-logout"></span>
@ -81,12 +94,12 @@
</div> </div>
<!--div class="btn-group dark round" id="meta-toggle" style="margin-right:10px"> <div class="btn-group dark round" id="meta-toggle" style="margin-right:10px">
<button class="btn btn-md btn-transparent btn-icon btn-icon" v-on:click="toggle_meta()"> <button class="btn btn-md btn-transparent btn-icon btn-icon" v-on:click="toggle_meta()">
<span class="jewel" style="color: white; background-color: red" v-if="meta_unseen>0">{{meta_unseen}}</span> <span class="jewel" style="color: white; background-color: red" v-if="meta_unseen>0">{{meta_unseen}}</span>
<span class="icon icon-menu"></span> <span class="icon icon-menu"></span>
</button> </button>
</div--> </div>
</div> </div>
</header> </header>
@ -98,8 +111,10 @@
<div id="folder-breadcrumb"> <div id="folder-breadcrumb">
<span v-for="item in active_space_path" class="btn btn-sm btn-transparent" v-sd-droppable="handle_folder_drop;item"> <a v-for="item in active_space_path" type="button" class="btn btn-sm btn-transparent" href="/{{item.space_type}}s/{{item._id}}" v-sd-droppable="handle_folder_drop;item">
<a href="/{{item.space_type}}s/{{item._id}}">{{item.name}}</a>&nbsp; â–¶</span> <span>{{item.name}}</span>
<span class="seperator">/</span>
</a>
<a v-if="(active_space_role != 'admin')" type="button" class="btn btn-sm btn-transparent"> <a v-if="(active_space_role != 'admin')" type="button" class="btn btn-sm btn-transparent">
<span>{{active_folder.name}}</span> <span>{{active_folder.name}}</span>
@ -113,10 +128,12 @@
<ul class="select-list"> <ul class="select-list">
<li><span class="tile-rename" v-on:click="rename_folder(active_folder)">[[__("rename")]]</span></li> <li><span class="tile-rename" v-on:click="rename_folder(active_folder)">[[__("rename")]]</span></li>
<li v-if="active_space_role == 'admin'"><span class="tile-share" v-on:click="activate_access()">[[__("share")]]</span></li> <li v-if="active_space_role == 'admin'"><span class="tile-share" v-on:click="activate_access()">[[__("share")]]</span></li>
<li><a v-on:click=" open_url('[[config.endpoint]]/api/spaces/'+active_folder._id+'/list')" target="_blank">[[__("list")]]</a></li>
</ul> </ul>
</div> </div>
</div> </div>
<div v-if="active_folder._id == user.home_folder_id"> <div v-if="active_folder._id == user.home_folder_id">
<span>[[ __('home') ]]</span> <span>[[ __('home') ]]</span>
</div> </div>
@ -129,6 +146,7 @@
</div> </div>
</div> </div>
<div id="folder-empty" v-if="folder_spaces_filter"> <div id="folder-empty" v-if="folder_spaces_filter">
<div v-if="active_profile_spaces | empty?"> <div v-if="active_profile_spaces | empty?">
<p><b>"{{folder_spaces_filter}}"</b> <br/>[[ __('search_no_results') ]]</p> <p><b>"{{folder_spaces_filter}}"</b> <br/>[[ __('search_no_results') ]]</p>
@ -156,6 +174,7 @@
<div class="dropdown-menu" role="menu"> <div class="dropdown-menu" role="menu">
<ul class="select-list"> <ul class="select-list">
<li v-on:click="duplicate_space(item)"><span><span class="icon icon-sm icon-duplicate"></span>[[ __('duplicate') ]]</span></li>
<li v-on:click="rename_space(item)"><span><span class="icon icon-sm icon-tag"></span>[[ __('rename') ]]</span></li> <li v-on:click="rename_space(item)"><span><span class="icon icon-sm icon-tag"></span>[[ __('rename') ]]</span></li>
<li v-on:click="delete_space(item)"><span><span class="icon icon-sm icon-trash"></span>[[ __('delete') ]]</span></li> <li v-on:click="delete_space(item)"><span><span class="icon icon-sm icon-trash"></span>[[ __('delete') ]]</span></li>
</ul> </ul>

View File

@ -1,11 +1,17 @@
<header id="landing-header" class="header" v-cloak v-if="(active_view == 'login' || active_view == 'signup' || active_view == 'password-reset' || active_view == 'password-confirm')"> <header id="landing-header" class="header" v-cloak v-if="(active_view == 'login' || active_view == 'signup' || active_view == 'password-reset' || active_view == 'password-confirm')">
<div class="header-left"> <div class="header-left">
<a class="btn btn-transparent btn-nude" href="/"><img src="/images/sd6-logo-black.svg" width="190"></a> <a class="btn btn-transparent btn-nude" href="/"><img src="/images/sd5-logo.svg" width="190"></a>
</div> </div>
<div class="header-right pull-right"> <div class="header-right pull-right">
<a v-if="active_view != 'login'" class="btn btn-md btn-dark btn-round" href="/login">[[__("login")]]</a> <span class="btn-group dark round">
<a v-if="active_view != 'signup'" class="btn btn-md btn-dark btn-round" href="/signup">[[__("signup")]]</a> {% if (locale != "de") %}<a href="/t/de?r={{active_view}}" class="btn btn-transparent btn-md">Deutsch</a>{% endif %}
{% if (locale != "en") %}<a href="/t/en?r={{active_view}}" class="btn btn-transparent btn-md">English</a>{% endif %}
{% if (locale != "fr") %}<a href="/t/fr?r={{active_view}}" class="btn btn-transparent btn-md">Français</a>{% endif %}
</span>
<a class="btn btn-md btn-dark btn-round" href="/login">[[__("login")]]</a>
<a class="btn btn-md btn-blue btn-round" href="/signup">[[__("signup")]]</a>
</div> </div>
</header> </header>
@ -17,7 +23,10 @@
<div id="login" v-bind:class="{active : active_view == 'login'}"> <div id="login" v-bind:class="{active : active_view == 'login'}">
<div class="content"> <div class="content">
<form v-on:submit="login_submit(user_forms_email, login_password, $event)"> <form v-on:submit="login_submit(user_forms_email, login_password, $event)">
<h3>Login</h3>
<span class="btn btn-xs btn-darken pull-right" v-on:click="login_google();">[[__("login_google")]]</span>
<h4>[[__("login")]]</h4>
<div class="tight"> <div class="tight">
<div class="form-group"> <div class="form-group">
@ -28,15 +37,16 @@
</div> </div>
</div> </div>
<button type="submit" class="btn btn-dark btn-block"> <button type="submit" class="btn btn-primary btn-block">
<span v-show="!loading_user">Login</span> <span v-show="!loading_user">[[__("login")]]</span>
<span v-show="loading_user">Logging in…</span> <span v-show="loading_user">[[__("logging_in")]]</span>
</button> </button>
<div class="center alert alert-danger" v-if="login_error">{{login_error}}</div> <div class="center alert alert-danger" v-if="login_error">{{login_error}}</div>
<div style="margin-top:2em"> <div class="pull-right">
<a href="/password-reset">Forgot Password</a> <a class="btn btn-xs btn-darken" href="/signup">[[__("signup")]]</a>&nbsp;
<a class="btn btn-xs btn-darken" href="/password-reset">[[__("reset_password")]]</a>
</div> </div>
</form> </form>
</div> </div>
@ -44,12 +54,23 @@
<div id="signup" v-bind:class="{active : active_view == 'signup'}"> <div id="signup" v-bind:class="{active : active_view == 'signup'}">
<div class="content"> <div class="content">
<form v-on:submit="signup_submit($event, user_forms_name, user_forms_email, signup_password, signup_password_confirmation, signup_invite_code)"> <form v-on:submit="signup_submit($event, user_forms_name, user_forms_email, signup_password, signup_password_confirmation)">
<span class="btn btn-xs btn-darken pull-right" v-on:click="login_google();">[[__("login_google")]]</span>
<h4>[[__("signup")]]</h4> <h4>[[__("signup")]]</h4>
<div class="tight"> <div class="tight">
<div class="form-group"> <div class="form-group">
<input class="input" type="email" required id="user-email" v-model="user_forms_email" placeholder="[[__("email")]]" autofocus v-focus> <input class="input" type="text" id="user-name" v-model="user_forms_name" placeholder="[[__("name")]]" v-focus autofocus>
</div>
</div>
<div class="tight">
<div class="form-group">
<input class="input" type="email" required id="user-email" v-model="user_forms_email" placeholder="[[__("email")]]">
</div> </div>
<div class="form-group"> <div class="form-group">
@ -57,33 +78,21 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<input class="input" id="user-password-confirmation" required type="password" v-model="signup_password_confirmation" placeholder="Repeat Password"> <input class="input" id="user-password-confirmation" required type="password" v-model="signup_password_confirmation" placeholder="[[__("password_confirmation")]]">
</div> </div>
</div> </div>
<div class="tight"> <div style="margin-top: -7px; margin-bottom: 7px;"><small>By signing up you agree to our <a href="/terms" target="_blank">TOS</a> and <a href="/privacy" target="_blank">Privacy Policy.</a></small><br/>
<div class="form-group">
<input class="input" type="text" id="user-name" v-model="user_forms_name" placeholder="Pick a username">
</div>
<!--
Disabled textbox so people don't have to input beta code when registering.
Remember to also set invite_code to an empty string ("") in /config/default.conf!
<div class="form-group">
<input class="input" id="invite-code" required type="text" v-model="signup_invite_code" placeholder="Beta Invite Code">
</div>-->
</div> </div>
<!--div style="margin-top: -7px; margin-bottom: 7px;"><small>By signing up you agree to our <a href="/terms" target="_blank">TOS</a> and <a href="/privacy" target="_blank">Privacy Policy.</a></small><br/> <button class="btn btn-primary btn-block">
</div-->
<button class="btn btn-dark btn-block">
<span v-if="!creating_user">[[__("signup")]]</span> <span v-if="!creating_user">[[__("signup")]]</span>
<span v-if="creating_user">[[__("signing_up")]]</span> <span v-if="creating_user">[[__("signing_up")]]</span>
</button> </button>
<div class="center alert alert-danger" style="width:100%;" v-if="signup_error">{{signup_error}}</div> <div class="center alert alert-danger" style="width:100%;" v-if="signup_error">{{signup_error}}</div>
<a class="btn btn-link btn-block" href="/login" style="margin-top: 20px">[[__("login")]]</a>
</form> </form>
</div> </div>
</div> </div>
@ -98,12 +107,12 @@
</div> </div>
</div> </div>
<div class="text-center alert alert-danger" v-if="password_reset_error">{{password_reset_error}}</div> <div class="text-center alert alert-danger" v-if="password_reset_error">{{password_reset_error}}</div>
<button class="btn btn-dark btn-block" v-on:click="password_reset_submit($event, reset_email)">[[__("reset_password")]]</button> <button class="btn btn-primary btn-block" v-on:click="password_reset_submit($event, reset_email)">[[__("reset_password")]]</button>
</form> </form>
</div> </div>
<div class="content" v-if="password_reset_send==true"> <div class="content" v-if="password_reset_send==true">
<h4>Reset Password</h4> <h4>[[__("password_confirmation")]]</h4>
Please check your email inbox. [[__("password_check_inbox")]]
</div> </div>
</div> </div>
@ -114,16 +123,16 @@
<div class="tight"> <div class="tight">
<div class="form-group"> <div class="form-group">
<input class="input" id="user-password" type="password" v-model="signup_password" placeholder="New Password"> <input class="input" id="user-password" type="password" v-model="signup_password" placeholder="[[__("password")]]">
</div> </div>
<div class="form-group"> <div class="form-group">
<input class="input" id="user-password" type="password" v-model="signup_password_confirmation" placeholder="Repeat Password"> <input class="input" id="user-password" type="password" v-model="signup_password_confirmation" placeholder="[[__("password_confirmation")]]">
</div> </div>
</div> </div>
<div class="text-center alert alert-danger" v-if="password_reset_confirm_error">{{password_reset_confirm_error}}</div> <div class="text-center alert alert-danger" v-if="password_reset_confirm_error">{{password_reset_confirm_error}}</div>
<button class="btn btn-dark btn-block" v-on:click="password_reset_confirm($event, signup_password, signup_password_confirmation)">[[__("save")]]</button> <button class="btn btn-primary btn-block" v-on:click="password_reset_confirm($event, signup_password, signup_password_confirmation)">[[__("save")]]</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content" style="width:760px"> <div class="modal-content" style="width:760px">
<div class="modal-header" style="padding-bottom:0"> <div class="modal-header" style="padding-bottom:0">
<h3 class="text-left">[[__("share")]]: {{access_settings_space.name}}</h3> <h3 class="text-left"><span class="icon icon-share icon-sm"></span> [[__("share")]]: {{access_settings_space.name}}</h3>
<button type="button" class="btn btn-icon btn-light btn-round close" v-on:click=" close_modal()"> <button type="button" class="btn btn-icon btn-light btn-round close" v-on:click=" close_modal()">
<span class="icon icon-cross-1"></span> <span class="icon icon-cross-1"></span>
</button> </button>
@ -68,7 +68,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<button class="btn btn-primary btn-md" v-on:click="invite_member(access_settings_space, invite_email, invite_message, invite_member_role)"> [[__("invite")]] </button> <button class="btn btn-primary btn-md btn-round" v-on:click="invite_member(access_settings_space, invite_email, invite_message, invite_member_role)"> [[__("invite")]] </button>
</div> </div>
</div> </div>
@ -85,19 +85,14 @@
</tr> </tr>
<tr v-for="member in access_settings_memberships" v-bind:class="member.state"> <tr v-for="member in access_settings_memberships" v-bind:class="member.state">
<td> <td>
<span class="editor-avatar btn btn-md btn-dark btn-icon btn-round" <span class="editor-avatar btn btn-xs btn-round btn-icon" v-if="member.user">{{member.user.initials}}</span>
v-if="member.user" <span class="editor-avatar btn btn-xs btn-round btn-icon icon-hourglass" v-if="!member.user"></span>
v-bind:style="background_image_style([member.user.avatar_thumb_uri])"
v-bind:class="{'has-avatar-image':!!member.user.avatar_thumb_uri}">
<span class="icon icon-user" v-if="!member.user.avatar_thumb_uri"></span>
</span>
<span class="editor-avatar btn btn-md btn-round btn-icon icon-hourglass" v-if="!member.user"></span>
</td> </td>
<td> <td>
<span class="editor-name" v-if="member.user && member.state == 'active'">{{member.user.nickname}}</span> <span class="editor-email" v-if="member.state == 'active'">{{member.user.email}}</span>
<span class="editor-email" v-if="!member.user || member.state == 'pending'">(pending)</span> <span class="editor-email" v-if="member.state == 'pending'">{{member.email_invited}}</span>
<span class="editor-email" v-if="member.user && member.state == 'active'">({{member.user.email}})</span> <span class="editor-name" v-if="member.state == 'active'">{{member.user.nickname}}</span>
<span class="editor-email" v-if="!member.user || member.state == 'pending'">({{member.email_invited}})</span> <span class="editor-email" v-if="member.state == 'pending'">(pending)</span>
</td> </td>
<td> <td>
<div class="form-group"> <div class="form-group">
@ -118,7 +113,7 @@
</p> </p>
<div class="form-group" style="padding-top: 40px"> <div class="form-group" style="padding-top: 40px">
<button class="btn btn-primary btn-md" v-on:click="close_modal();"> [[__("ok")]] </button> <button class="btn btn-primary btn-md btn-round" v-on:click="close_modal();"> [[__("ok")]] </button>
</div> </div>
</div> </div>

View File

@ -1,3 +1,131 @@
<!-- FIXME modal -->
<div class="modal" v-if="(duplicate_folders.length > 0)" v-cloak>
<div class="modal-wrapper">
<div class="modal-dialog">
<button type="button" class="btn btn-icon btn-light btn-round close" v-on:click="duplicate_folders = []">
<span class="icon icon-cross-1"></span>
</button>
<div class="modal-content">
<div class="modal-body labels-inline">
<div class="modal-section">
<div class="form-group">
<label>
[[__("duplicate_destination")]]
</label>
</div>
<div class="form-group">
<select v-on:change="duplicate_folder_id=$event.target.value">
<option v-for="f in duplicate_folders" value="{{f._id}}">{{f.name}}</option>
</select>
</div>
<div class="form-group">
<button class="btn btn-md btn-round btn-primary" v-on:click="duplicate_folder_confirm(); ">
<span class="icon-label">[[__("ok")]]</span>
</button>
<button class="btn btn-md btn-round btn-darken pull-right" v-on:click="duplicate_folders = [];">
<span class="icon-label">[[__("cancel")]]</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-cloak class="header-left" v-show="active_space_loaded">
<div class="btn-group dark">
<div class="pull-left">
<a
class="btn btn-stroke-darken btn-md btn-round btn-icon"
title="[[__("home")]]" href="/spaces"
v-if="(logged_in && !embedded && !active_space.parent_space_id && !guest_nickname)">
<span class="icon icon-home"></span>
</a>
<a
class="btn btn-stroke-darken btn-md btn-round btn-icon"
title="[[__("parent_folder")]]"
href="/folders/{{active_space.parent_space_id}}"
v-if="(active_space.parent_space_id && !guest_nickname)">
<span class="icon icon-arrow-left-light"></span>
</a>
<input class="input input-md input-transparent w-auto"
id="space-title"
v-model="active_space.name" name="title" v-on:keydown="save_space_keydown($event)"
v-if="space_editing_title && logged_in" style="padding-right:0" v-focus>
<span class="input input-md input-transparent w-auto"
v-if="!space_editing_title && logged_in"
v-on:click="edit_space_title()">{{active_space.name}}</span>
<span v-if="!logged_in" class="btn btn-dark btn-round btn-md">{{active_space.name}}</span>
<button class="btn btn-md btn-transparent btn-icon" v-if="space_editing_title" v-on:click="save_space_keydown()">
<span class="icon icon-check"></span>
</button>
<div class="dropdown top left light" v-bind:class="{open: active_dropdown=='space'}">
<button class="btn btn-md btn-icon btn-dark" v-on:click="activate_dropdown('space')">
<span class="icon icon-triangle-down"></span></button>
<div class="dropdown-menu" role="menu">
<ul class="select-list">
<li v-on:click="activate_access()" v-if="logged_in">
<span>
<span class="icon icon-sm icon-share"></span>
<span>[[ __('share') ]]</span>
</span>
</li>
<li v-on:click="edit_space_title()" v-if="logged_in">
<span>
<span class="icon icon-sm icon-tag"></span>
<span>[[ __('rename') ]]</span>
</span>
</li>
<li v-on:click="duplicate_space_into_folder()" v-if="logged_in">
<span>
<span class="icon icon-sm icon-duplicate"></span>
<span>[[ __('duplicate') ]]</span>
</span>
</li>
<li v-on:click="download_space()">
<span>
<span class="icon icon-sm icon-download"></span>
<span>[[ __('download_space') ]]</span>
</span>
</li>
<li v-on:click="activate_modal('support')" v-if="logged_in">
<span>
<span class="icon icon-sm icon-info"></span>
<span>[[ __('support') ]]</span>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<span class="btn btn-red btn-md" id="offline-indicator" v-bind:class="{offline: was_offline}" v-on:click="show_offline_help()">[[__("offline")]]</span>
</div>
<div v-cloak class="header-right" v-if="active_space_loaded"> <div v-cloak class="header-right" v-if="active_space_loaded">
<span v-for="active_user in active_space_users" > <span v-for="active_user in active_space_users" >
@ -13,7 +141,13 @@
</button> </button>
</span> </span>
<div class="btn-group light round" v-if="zones.length"> <div class="btn-group dark" v-if="active_space_role!='viewer'">
<button class="btn btn-md btn-transparent btn-icon" title="Start Presentation (others follow what you see)" v-on:click="toggle_present_mode()" v-bind:class="{open:present_mode}">
<span class="icon icon-presentation"></span>
</button>
</div>
<div class="btn-group dark round" v-if="zones.length">
<button class="btn btn-md btn-transparent btn-icon" v-on:click="go_to_previous_zone()" title="[[__("Previous Zone")]]"> <button class="btn btn-md btn-transparent btn-icon" v-on:click="go_to_previous_zone()" title="[[__("Previous Zone")]]">
<span class="icon icon-triangle-4-left"></span> <span class="icon icon-triangle-4-left"></span>
</button> </button>
@ -25,12 +159,16 @@
</button> </button>
</div> </div>
<!--div class="btn-group light" id="meta-toggle" style="margin-right:10px"> <!--button class="btn btn-md btn-dark btn-round btn-icon" v-on:click="download_space()" title="[[__("download_space")]]">
<span class="icon icon-download"></span>
</button-->
<div class="btn-group dark" id="meta-toggle" style="margin-right:10px">
<button class="btn btn-md btn-transparent btn-icon" v-on:click="toggle_meta()" title="[[__("chat")]]"> <button class="btn btn-md btn-transparent btn-icon" v-on:click="toggle_meta()" title="[[__("chat")]]">
<span class="jewel" style="color: white; background-color: red" v-if="meta_unseen>0">{{meta_unseen}}</span> <span class="jewel" style="color: white; background-color: red" v-if="meta_unseen>0">{{meta_unseen}}</span>
<span class="icon icon-messages"></span> <span class="icon icon-messages"></span>
</button> </button>
</div--> </div>
</div> </div>
{% include "./tool/toolbar-elements.html" %} {% include "./tool/toolbar-elements.html" %}
@ -43,8 +181,9 @@
<div class="space-empty" v-cloak v-if="active_view == 'space' && !present_mode && active_space_artifacts.length == 0"> <div class="space-empty" v-cloak v-if="active_view == 'space' && !present_mode && active_space_artifacts.length == 0">
<div class="table-fake"> <div class="table-fake">
<div class="cell"> <div class="cell">
<p>Use the toolbar to add content.<br> <p>Click anywhere to add content.<br>
You can also drop images or sound and video files.</p> You can also drop images, sounds and video<br>
or use copy and paste.</p>
</div> </div>
</div> </div>
</div> </div>
@ -67,7 +206,7 @@
<div id="space-clipboard" style="position:fixed;top:0;left:0;z-index:0;opacity:0;background-color:white"><textarea v-model="selected_artifacts_json" cols="2" rows="2" id="clipboard-ta" class="mousetrap"></textarea></div> <div id="space-clipboard" style="position:fixed;top:0;left:0;z-index:0;opacity:0;background-color:white"><textarea v-model="selected_artifacts_json" cols="2" rows="2" id="clipboard-ta" class="mousetrap"></textarea></div>
<div class="space-bounds" v-bind:style="{width: (active_space.width*bounds_zoom+1000) + 'px', height: (active_space.height*bounds_zoom+1000) + 'px', 'background-color': active_space.background_color}"></div> <div class="space-bounds" v-bind:style="{width: active_space.width*bounds_zoom + 'px', height: active_space.height*bounds_zoom + 'px', 'background-color': active_space.background_color}"></div>
<div class="wrapper" <div class="wrapper"
v-bind:style="{ v-bind:style="{
@ -105,6 +244,7 @@
<a class="link btn btn-round btn-primary btn-sm" v-if="a.view.link" v-bind:href="a.view.link" target="_blank">{{a.view.link_caption}}</a> <a class="link btn btn-round btn-primary btn-sm" v-if="a.view.link" v-bind:href="a.view.link" target="_blank">{{a.view.link_caption}}</a>
</span> </span>
<div class="btn btn-xs btn-icon btn-round btn-primary edit" v-show="editing_artifact_id!=a._id && is_selected(a)" v-on:touchstart="delayed_edit_artifact($event)"><span class="icon icon-pencil" v-on:click="toggle_selected_artifact_editing(true)" v-on:"touchstart:delayed_edit_artifact($event)"></span></div>
<input v-show="is_selected(a)" type="text" id="ios-focuser-{{a._id}}" class="ios-focuser"> <input v-show="is_selected(a)" type="text" id="ios-focuser-{{a._id}}" class="ios-focuser">
</div> </div>
@ -126,6 +266,7 @@
<a class="link btn btn-round btn-primary btn-sm" v-if="a.view.link" v-bind:href="a.view.link" target="_blank">{{a.view.link_caption}}</a> <a class="link btn btn-round btn-primary btn-sm" v-if="a.view.link" v-bind:href="a.view.link" target="_blank">{{a.view.link_caption}}</a>
</span> </span>
<div class="btn btn-xs btn-icon btn-round btn-primary edit" v-show="editing_artifact_id!=a._id && is_selected(a)" v-on:touchstart="delayed_edit_artifact($event)"><span class="icon icon-pencil" v-on:click="toggle_selected_artifact_editing(true)" v-on:"touchstart:delayed_edit_artifact($event)"></span></div>
<input v-show="is_selected(a)" type="text" id="ios-focuser-{{a._id}}" class="ios-focuser"> <input v-show="is_selected(a)" type="text" id="ios-focuser-{{a._id}}" class="ios-focuser">
</div> </div>
@ -145,6 +286,11 @@
<source v-for="rep in a.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" /> <source v-for="rep in a.payload_alternatives" v-bind:src="rep.payload_uri" v-bind:type="rep.mime" />
</video> </video>
<a class="btn btn-md btn-icon btn-round btn-primary edit"
v-show="a.mime == 'application/pdf'"
v-bind:href="a.payload_uri" target="_blank"
><span class="icon icon-link"></span></a>
<span v-if="a.view.link.length>0" class="link-wrapper"> <span v-if="a.view.link.length>0" class="link-wrapper">
<a class="link btn btn-round btn-primary btn-sm" v-if="a.view.link" v-bind:href="a.view.link" target="_blank">{{a.view.link_caption}}</a> <a class="link btn btn-round btn-primary btn-sm" v-if="a.view.link" v-bind:href="a.view.link" target="_blank">{{a.view.link_caption}}</a>
</span> </span>
@ -317,7 +463,7 @@
</div> </div>
<div v-if="active_space_loaded" v-cloak> <div v-if="active_space_loaded" v-cloak>
<!--div id="minimap" <div id="minimap"
v-bind:style="{width: ''+(active_space.width/minimap_scale)+'px', height: ''+(active_space.height/minimap_scale)+'px', bottom: '66px', right: '20px'}" v-bind:style="{width: ''+(active_space.width/minimap_scale)+'px', height: ''+(active_space.height/minimap_scale)+'px', bottom: '66px', right: '20px'}"
v-if="active_space" v-if="active_space"
v-on:mousedown="handle_minimap_mousedown($event)" v-on:mousedown="handle_minimap_mousedown($event)"
@ -329,16 +475,17 @@
v-on:mouseup="handle_minimap_mouseup($event)"> v-on:mouseup="handle_minimap_mouseup($event)">
<div v-for="a in active_space_artifacts" v-bind:style="{left: ''+(a.x/minimap_scale)+ 'px', top: ''+(a.y/minimap_scale) + 'px', width: ''+(a.w/minimap_scale)+ 'px', height: ''+(a.h/minimap_scale) + 'px'}"></div> <div v-for="a in active_space_artifacts" v-bind:style="{left: ''+(a.x/minimap_scale)+ 'px', top: ''+(a.y/minimap_scale) + 'px', width: ''+(a.w/minimap_scale)+ 'px', height: ''+(a.h/minimap_scale) + 'px'}"></div>
<div class="window" v-bind:style="{left: ''+(scroll_left/minimap_scale) + 'px', top: ''+(scroll_top/minimap_scale)+ 'px', width: ''+(window_width/minimap_scale)+ 'px', height: ''+(window_height/minimap_scale) + 'px'}"></div> <div class="window" v-bind:style="{left: ''+(scroll_left/minimap_scale) + 'px', top: ''+(scroll_top/minimap_scale)+ 'px', width: ''+(window_width/minimap_scale)+ 'px', height: ''+(window_height/minimap_scale) + 'px'}"></div>
</div-->
<div class="btn-group light zoom-bar"> </div>
<button class="btn btn-icon btn-md btn-white" v-on:click="zoom_in()">
<div class="btn-group dark" style="position:absolute;bottom:20px;right:20px;">
<button class="btn btn-icon btn-md btn-transparent" v-on:click="zoom_in()">
<span class="icon icon-plus"></span> <span class="icon icon-plus"></span>
</button> </button>
<button class="btn btn-md btn-white no-p" v-on:click="zoom_to_original()"> <button class="btn btn-md btn-transparent no-p" v-on:click="zoom_to_original()">
{{viewport_zoom_percent}}% {{viewport_zoom_percent}}%
</button> </button>
<button class="btn btn-icon btn-md btn-white" v-on:click="zoom_out()"> <button class="btn btn-icon btn-md btn-transparent" v-on:click="zoom_out()">
<span class="icon icon-minus"></span> <span class="icon icon-minus"></span>
</button> </button>
</div> </div>

View File

@ -126,12 +126,20 @@
<label class="label label-sm text-center">[[__("font_size")]]</label> <label class="label label-sm text-center">[[__("font_size")]]</label>
<input class="input no-b no-p text-center text-large" spellcheck="false" type="text" pattern="[0-9]" maxlength="64" v-model="active_style.font_size"> <input class="input no-b no-p text-center text-large" spellcheck="false" type="text" pattern="[0-9]" maxlength="64" v-model="active_style.font_size">
<button tabindex="-1" class="input-drag btn btn-transparent btn-icon" style="cursor: ns-resize;" v-sd-fader="true" sd-fader-var-y="active_style.font_size" sd-fader-min-y="30" sd-fader-max-y="200" sd-fader-sens="5"> <!--button tabindex="-1" class="input-drag btn btn-transparent btn-icon" style="cursor: ns-resize;" v-sd-fader="true" sd-fader-var-y="active_style.font_size" sd-fader-min-y="8" sd-fader-max-y="400" sd-fader-sens="0.2">
<span class="icon icon-triangles-vertical"></span> <span class="icon icon-triangles-vertical"></span>
</button> </button-->
<span class="input-unit">px</span> <span class="input-unit">px</span>
</div> </div>
<div class="form-group no-m">
<span class="font-size-swatches" v-show="opened_dialog=='color-text'">
<button class="btn btn-sm" v-on:click="apply_font_size(64)" style="font-size:32px">Big</button>
<button class="btn btn-sm" v-on:click="apply_font_size(32)" style="font-size:24px">Medium</button>
<button class="btn btn-sm" v-on:click="apply_font_size(18)" style="font-size:14px">Small</button>
</span>
</div>
<!--div class="form-group no-m"> <!--div class="form-group no-m">
<label class="label label-sm text-center">[[__("line_height")]]</label> <label class="label label-sm text-center">[[__("line_height")]]</label>
<input disabled class="input no-b no-p text-center text-large" spellcheck="false" type="text" pattern="[0-9\.]" maxlength="64" v-model="active_style.line_height"> <input disabled class="input no-b no-p text-center text-large" spellcheck="false" type="text" pattern="[0-9\.]" maxlength="64" v-model="active_style.line_height">

View File

@ -1,5 +1,5 @@
<div id="layout" class="relative"> <div id="layout" class="relative">
<div class="dialog-section"> <div class="dialog-section no-p-b">
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-transparent btn-icon" v-on:click="layout_stack_top()"> <button class="btn btn-transparent btn-icon" v-on:click="layout_stack_top()">
<span class="icon icon-stack-3d-top"></span> <span class="icon icon-stack-3d-top"></span>

View File

@ -1,8 +1,7 @@
<h4 class="dialog-title">[[__("tool_shape")]]</h4> <h4 class="dialog-title">[[__("tool_shape")]]</h4>
<div id="shapes"> <div id="shapes">
<div class="dialog-section"> <div class="dialog-section no-p-h" style="white-space: normal;">
<div class="btn-group">
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('ellipse',$event)"> <button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('ellipse',$event)">
<span class="icon icon-shape-circle"></span> <span class="icon icon-shape-circle"></span>
<span class="icon-label">[[__("tool_circle")]]</span> <span class="icon-label">[[__("tool_circle")]]</span>
@ -18,6 +17,11 @@
<span class="icon-label">[[__("tool_square")]]</span> <span class="icon-label">[[__("tool_square")]]</span>
</button> </button>
<!--button class="btn btn-icon-labeled btn-transparent rot45" v-on:click="add_shape('diamond',$event)">
<span class="icon icon-shape-square"></span>
<span class="icon-label">[[__("tool_diamond")]]</span>
</button-->
<button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('speechbubble',$event)"> <button class="btn btn-icon-labeled btn-transparent" v-on:click="add_shape('speechbubble',$event)">
<span class="icon icon-shape-bubble"></span> <span class="icon icon-shape-bubble"></span>
<span class="icon-label">[[__("tool_bubble")]]</span> <span class="icon-label">[[__("tool_bubble")]]</span>
@ -43,6 +47,23 @@
<span class="icon-label">[[__("tool_heart")]]</span> <span class="icon-label">[[__("tool_heart")]]</span>
</button> </button>
</div> <button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_arrow()">
<span class="icon icon-tool-arrow"></span>
<span class="icon-label">[[__("tool_arrow")]]</span>
</button>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_line()">
<span class="icon icon-tool-line"></span>
<span class="icon-label">[[__("tool_line")]]</span>
</button>
</div> </div>
</div> </div>
<!--
<div class="dialog-section no-p">
<div class="btn-cluster">
<button class="btn btn-transparent text-center"> Upload </button>
<button class="btn btn-transparent text-center" v-on:click="start_drawing_scribble()"> Draw </button>
</div>
</div>
-->

View File

@ -24,6 +24,6 @@
<!--button class="btn btn-transparent btn-icon-labeled" v-on:click="apply_formatting($event,'insertUnorderedList')"> <!--button class="btn btn-transparent btn-icon-labeled" v-on:click="apply_formatting($event,'insertUnorderedList')">
<span class="icon icon-text-list-bullet"></span> <span class="icon icon-text-list-bullet"></span>
<span class="icon-label">Bullets</span> <span class="icon-label">Bullets</span>
</button--!> </button-->
</div> </div>
</div> </div>

View File

@ -1,24 +1,21 @@
<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" v-bind:style="{left:toolbar_artifacts_x,top:toolbar_artifacts_y}">
<div class="btn-group light vertical"> <div class="btn-group dark">
<a class="btn btn-icon btn-transparent" <!--div id="search-dialog" class="dropdown bottom light center static" v-bind:class="{open:opened_dialog=='search'}">
title="[[__("home")]]" href="/spaces" <div class="btn-collapse in">
v-if="(!active_space.parent_space_id && !guest_nickname)"> <button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='search'}" v-on:click="open_dialog('search')" >
<span class="icon icon-folder"></span> <span class="icon icon-search"></span>
</a> <span class="icon-label">[[__("tool_search")]]</span>
</button>
</div>
<a class="btn btn-icon btn-dark" <div class="dialog dialog-search">
title="Parent Folder" xinclude "./search.html"
href="/folders/{{active_space.parent_space_id}}" </div>
v-if="(active_space.parent_space_id && !guest_nickname)"> </div-->
<span class="icon icon-sd6 icon-svg"></span> <div class="dropdown bottom light center" v-bind:class="{open:opened_dialog=='shapes'}">
</a>
<button class="btn btn-divider"></button>
<div class="dropdown top left light" v-bind:class="{open:opened_dialog=='shapes'}">
<div class="btn-collapse in"> <div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='shapes'}" v-on:click="open_dialog('shapes')"> <button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='shapes'}" v-on:click="open_dialog('shapes')">
<span class="icon icon-shapes"></span> <span class="icon icon-shapes"></span>
@ -31,35 +28,30 @@
</div> </div>
</div> </div>
<button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_scribble()" v-bind:class="{active:active_tool=='scribble'}"> <button class="btn btn-icon-labeled btn-transparent" v-on:click="start_drawing_scribble()">
<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'}">
<span class="icon icon-tool-arrow"></span>
<span class="icon-label">[[__("tool_arrow")]]</span>
</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()"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="handle_insert_image_url()" v-on:touchstart="handle_insert_image_url()">
<span class="icon icon-upload"></span> <span class="icon icon-upload"></span>
<span class="icon-label" >Media</span> <span class="icon-label" >[[__("tool_upload")]]</span>
</button> </button>
</div> </div>
</div> </div>
<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="active_tool='note'" v-bind:class="{active:active_tool=='note'}"> <button class="btn btn-transparent btn-icon-labeled" v-on:click=" add_artifact(active_space, 'text', null, $event)">
<span class="icon icon-tool-text"></span> <span class="icon icon-tool-text"></span>
<span class="icon-label">[[__("tool_text")]]</span> <span class="icon-label">[[__("tool_text")]]</span>
</button> </button>
</div> </div>
</div> </div>
<div class="dropdown top left light"> <div class="dropdown bottom light center">
<div class="btn-collapse"> <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')"> <button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='image'}" v-on:click="open_dialog('image')">
<span class="icon icon-picture"></span> <span class="icon icon-picture"></span>
@ -72,7 +64,7 @@
</div> </div>
</div> </div>
<div class="dropdown top left light" v-bind:class="{open:opened_dialog=='zones'}"> <div class="dropdown bottom light center" 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')"> <button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='zones'}" v-on:click="open_dialog('zones')">
<span class="icon icon-zone"></span> <span class="icon icon-zone"></span>
@ -87,7 +79,7 @@
<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 bottom light center" v-show="logged_in" v-bind:class="{open:opened_dialog=='background'}">
<div class="btn-collapse in"> <div class="btn-collapse in">
<button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='background'}" v-on:click="open_dialog('background')"> <button class="btn btn-transparent btn-icon-labeled" v-bind:class="{open:opened_dialog=='background'}" v-on:click="open_dialog('background')">
<span class="letter">bg</span> <span class="letter">bg</span>
@ -100,25 +92,6 @@
</div> </div>
</div> </div>
<button class="btn btn-transparent btn-icon-labeled"
v-if="active_space_role=='admin'"
v-on:click="activate_access()">
<span class="icon icon-share"></span>
<span class="icon-label">[[ __('share') ]]</span>
</button>
<!--
<li v-on:click="edit_space_title()" v-if="logged_in">
<span>
<span class="icon icon-sm icon-tag"></span>
<span>[[ __('rename') ]]</span>
</span>
</li>
-->
<button class="btn btn-transparent btn-icon-labeled" title="Start Presentation (others follow what you see)" v-on:click="toggle_present_mode()" v-bind:class="{open:present_mode}">
<span class="icon icon-presentation"></span>
<span class="icon-label">[[ __('present') ]]</span>
</button>
</div> </div>
</div> </div>

View File

@ -1,38 +1,42 @@
<div class="toolbar toolbar-properties" v-cloak v-show="active_space_loaded && !is_active_space_role('viewer')" v-bind:class="{in:toolbar_props_in,out:!toolbar_props_in}" v-if="active_space_loaded"> <div class="toolbar toolbar-properties" v-cloak v-show="active_space_loaded && !is_active_space_role('viewer')" v-bind:class="{in:toolbar_props_in,out:!toolbar_props_in}" v-bind:style="{left:toolbar_props_x,top:toolbar_props_y}" v-if="active_space_loaded">
<div class="btn-group light vertical"> <div class="btn-group dark">
<div class="dropdown top right light" <div class="dropdown topleft light"
v-bind:class="{open : opened_dialog.match('color') , v-bind:class="{open : opened_dialog.match('color') ,
'option-1':opened_dialog=='color-fill' , 'option-1':opened_dialog=='color-fill' ,
'option-2':opened_dialog=='color-stroke' , 'option-2':opened_dialog=='color-stroke' ,
'option-3':opened_dialog=='color-text', 'option-3':opened_dialog=='color-text',
'options-3':selection_metrics.contains_text}"> 'options-3':selection_metrics.contains_text}">
<button <label
class="dropdown-toggle btn btn-icon btn-transparent" class="dropdown-toggle btn btn-icon btn-transparent no-r-r"
v-on:click="open_dialog('color-fill')" v-on:click="open_dialog('color-fill')"
v-bind:class="{open:opened_dialog=='color-fill'}"> v-bind:class="{open:opened_dialog=='color-fill'}">
<span class="icon icon-tool-fill icon-sm"></span> <span class="icon icon-tool-fill icon-sm"></span>
<span class="jewel" v-bind:style="{'background-color':active_style.fill_color}"></span> <span class="jewel" v-bind:style="{'background-color':active_style.fill_color}"></span>
</button><br> </label>
<button <label
class="dropdown-toggle btn btn-icon btn-transparent" class="dropdown-toggle btn btn-icon btn-transparent no-r"
v-bind:class="{open:opened_dialog=='color-stroke'}" v-bind:class="{open:opened_dialog=='color-stroke'}"
v-on:click="open_dialog('color-stroke')"> v-on:click="open_dialog('color-stroke')">
<span class="icon icon-tool-stroke icon-sm"></span> <span class="icon icon-tool-stroke icon-sm"></span>
<span class="jewel jewel-stroke" v-bind:style="{'border-color':active_style.stroke_color}"></span> <span class="jewel jewel-stroke" v-bind:style="{'border-color':active_style.stroke_color}"></span>
</button><br> </label>
<button <label
class="dropdown-toggle btn btn-icon btn-transparent" class="dropdown-toggle btn btn-icon btn-transparent no-r-l"
v-on:click="open_dialog('color-text')" v-on:click="open_dialog('color-text')"
v-bind:class="{open:opened_dialog=='color-text'}"> v-bind:class="{open:opened_dialog=='color-text'}">
<span class="icon icon-tool-text icon-sm"></span> <span class="icon icon-tool-text icon-sm"></span>
<span class="jewel" v-bind:style="{'border-color':active_style.text_color}">{{active_style.font_size}}</span> <span class="jewel" v-bind:style="{'border-color':active_style.text_color}">{{active_style.font_size}}</span>
</button> </label>
<div class="dialog"> <div class="dialog">
{% include "./color.html" %} {% include "./color.html" %}
</div> </div>
</div> </div>
</div>
<div class="btn-group dark">
<!-- <button class="btn btn-transparent btn-icon-labeled"> <!-- <button class="btn btn-transparent btn-icon-labeled">
<span class="icon icon-tool-eyedrop"></span> <span class="icon icon-tool-eyedrop"></span>
<span class="icon-label">Eyedrop</span> <span class="icon-label">Eyedrop</span>
@ -41,11 +45,11 @@
<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 center" 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'}"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-styles')" v-bind:class="{open : opened_dialog=='text-styles'}">
<span class="icon icon-text-styles"></span> <span class="icon icon-text-styles"></span>
<span class="icon-label">Styles</span> <span class="icon-label">styles</span>
</button> </button>
</div> </div>
@ -54,7 +58,21 @@
</div> </div>
</div> </div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='type-align'}"> <div class="dropdown top light center" v-bind:class="{open:opened_dialog=='filter'}">
<div class="btn-collapse" v-bind:class="{in:selection_metrics.contains_images}">
<!-- <div class="btn-collapse" v-bind:class="in:selection_metrics.count>0"> -->
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('filter')" v-bind:class="{open : opened_dialog=='filter'}">
<span class="icon icon-contrast"></span>
<span class="icon-label">[[__("tool_filter")]]</span>
</button>
</div>
<div class="dialog">
{% include "./filter.html" %}
</div>
</div>
<div class="dropdown top light center" 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'}"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('type-align')" v-bind:class="{open : opened_dialog=='type-align'}">
<span class="icon icon-text-align-left-alt"></span> <span class="icon icon-text-align-left-alt"></span>
@ -67,9 +85,9 @@
</div> </div>
</div> </div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='layout'}"> <div class="dropdown top light center" v-bind:class="{open:opened_dialog=='layout'}">
<div class="btn-collapse in"> <div class="btn-collapse" v-bind:class="{in:selection_metrics.count>0}">
<button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('layout')" v-bind:class="{open : opened_dialog=='layout'}"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('layout')" v-bind:class="{open : opened_dialog=='layout'}">
<span class="icon icon-cluster"></span> <span class="icon icon-cluster"></span>
<span class="icon-label">[[__("tool_layout")]]</span> <span class="icon-label">[[__("tool_layout")]]</span>
@ -81,7 +99,7 @@
</div> </div>
</div> </div>
<div class="dropdown top light right" v-bind:class="{open:opened_dialog=='text-settings'}"> <div class="dropdown top light center" 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'}"> <button class="btn btn-transparent btn-icon-labeled" v-on:click="open_dialog('text-settings')" v-bind:class="{open : opened_dialog=='text-settings'}">
@ -97,7 +115,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=='object-options'}"> <div class="dropdown top light center" 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>
<span class="icon-label">[[__("more")]]</span> <span class="icon-label">[[__("more")]]</span>

View File

@ -2,11 +2,11 @@
<div id="zones" style="max-height:500px;overflow-y:scroll"> <div id="zones" style="max-height:500px;overflow-y:scroll">
<div class="dialog-section"> <div class="dialog-section">
<!--p v-if="zones.length<2"> <p v-if="zones.length<2">
Turn your Space into a zooming presentation by placing some Zones and switch through them when presenting. Turn your Space into a zooming presentation by placing some Zones and switch through them when presenting.
</p--> </p>
<button v-on:click="add_zone()" class="btn btn-sm btn-dark">[[__("add_zone")]]</button> <button v-on:click="add_zone()" class="btn btn-sm btn-primary">[[__("add_zone")]]</button>
</div> </div>
<div class="dialog-section no-p" v-for="z in zones | orderBy 'order'" style="white-space: nowrap;text-align:left;cursor:pointer" v-on:click="zoom_to_zone(z)"> <div class="dialog-section no-p" v-for="z in zones | orderBy 'order'" style="white-space: nowrap;text-align:left;cursor:pointer" v-on:click="zoom_to_zone(z)">

View File

@ -9,11 +9,14 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link href="[[ '/images/favicon.png' | cdn ]]" rel="icon" type="image/x-icon" /> <link href="[[ '/images/favicon.png' | cdn ]]" rel="icon" type="image/x-icon" />
<link href='https://fonts.googleapis.com/css?family=Inter' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,700,600,800,300|Montserrat:400,700|EB+Garamond|Vollkorn|Lato|Roboto|Source+Code+Pro|Ubuntu|Raleway|Playfair+Display|Crimson+Text' rel='stylesheet' type='text/css'>
<link type="text/css" rel="stylesheet" href="https://fast.fonts.net/cssapi/ee1a3484-4d98-4f9f-9f55-020a7b37f3c5.css"/>
<link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]"> <link rel="stylesheet" href="[[ '/stylesheets/style.css' | cdn ]]">
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script> <script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twemoji/1.3.2/twemoji.min.js"></script>
<script> <script>
window.socket_auth = '[[socket_auth]]'; window.socket_auth = '[[socket_auth]]';
window.browser_lang = '[[locale]]'; window.browser_lang = '[[locale]]';
@ -28,45 +31,53 @@
}; };
</script> </script>
<script src="/javascripts/jquery-2.1.4.min.js"></script> {% if process.env.NODE_ENV == "production" %}
<script src="/javascripts/i18next-1.11.2.js"></script> <script src="[[ '/javascripts/spacedeck.js' | cdn ]]"></script>
<script src="/javascripts/clipboard.js"></script> {% else %}
<script minify src="/javascripts/jquery-2.1.4.min.js"></script>
<script minify src="/javascripts/i18next-1.11.2.js"></script>
<script minify src="/javascripts/clipboard.js"></script>
<script src="/javascripts/lodash.compat.js"></script> <script minify src="/javascripts/lodash.compat.js"></script>
<script src="/javascripts/fastclick.js"></script> <script minify src="/javascripts/fastclick.js"></script>
<script src="/javascripts/vue.js"></script> <script minify src="/javascripts/vue.js"></script>
<script src="/javascripts/moment.js"></script> <script minify src="/javascripts/moment.js"></script>
<script src="/javascripts/medium.patched.js"></script> <script minify src="/javascripts/medium.patched.js"></script>
<script src="/javascripts/route-recognizer.js"></script> <script minify src="/javascripts/route-recognizer.js"></script>
<script src="/javascripts/backend.js"></script> <script minify src="/javascripts/backend.js"></script>
<script src="/javascripts/link_parser.js"></script> <script minify src="/javascripts/link_parser.js"></script>
<script src="/javascripts/vector-render.js"></script> <script minify src="/javascripts/vector-render.js"></script>
<script src="/javascripts/mousetrap.js"></script> <script minify src="/javascripts/mousetrap.js"></script>
<script src="/javascripts/smoke.js"></script> <script minify src="/javascripts/smoke.js"></script>
<script src="/javascripts/helper.js"></script> <script minify src="/javascripts/helper.js"></script>
<script src="/javascripts/packer.growing.js"></script> <script minify src="/javascripts/packer.growing.js"></script>
<script src="/javascripts/spacedeck_routes.js"></script> <script minify src="/javascripts/spacedeck_routes.js"></script>
<script src="/javascripts/spacedeck_formatting.js"></script> <script minify src="/javascripts/spacedeck_formatting.js"></script>
<script src="/javascripts/spacedeck_sections.js"></script> <script minify src="/javascripts/spacedeck_sections.js"></script>
<script src="/javascripts/spacedeck_spaces.js"></script> <script minify src="/javascripts/spacedeck_spaces.js"></script>
<script src="/javascripts/spacedeck_teams.js"></script> <script minify src="/javascripts/spacedeck_teams.js"></script>
<script src="/javascripts/spacedeck_board_artifacts.js"></script> <script minify src="/javascripts/spacedeck_board_artifacts.js"></script>
<script src="/javascripts/spacedeck_users.js"></script> <script minify src="/javascripts/spacedeck_users.js"></script>
<script src="/javascripts/spacedeck_account.js"></script> <script minify src="/javascripts/spacedeck_account.js"></script>
<script src="/javascripts/spacedeck_modals.js"></script> <script minify src="/javascripts/spacedeck_modals.js"></script>
<script src="/javascripts/spacedeck_avatars.js"></script> <script minify src="/javascripts/spacedeck_avatars.js"></script>
<script src="/javascripts/spacedeck_websockets.js"></script> <script minify src="/javascripts/spacedeck_websockets.js"></script>
<script src="/javascripts/spacedeck_whiteboard.js"></script> <script minify src="/javascripts/spacedeck_whiteboard.js"></script>
<script src="/javascripts/spacedeck_directives.js"></script> <script minify src="/javascripts/spacedeck_directives.js"></script>
<script src="/javascripts/spacedeck_vue.js"></script> <script minify src="/javascripts/spacedeck_vue.js"></script>
{% endif %}
<script>if (window.module) module = window.module;</script> <script>if (window.module) module = window.module;</script>
</head> </head>
<body id="main" v-bind:class="{'present-mode':present_mode,'modal-open':active_modal}" v-on:click="handle_body_click($event)"> <body id="main" v-bind:class="{'present-mode':present_mode,'modal-open':active_modal}" v-on:click="handle_body_click($event)">
<!--[if lt IE 10]>
<p class="browsehappy">You are using an <strong>outdated</strong> version of Internet Explorer. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
{% include "./partials/login.html" %} {% include "./partials/login.html" %}
{% include "./partials/space.html" %} {% include "./partials/space.html" %}
{% include "./partials/folders.html" %} {% include "./partials/folders.html" %}
@ -77,6 +88,10 @@
{% include "./partials/modal/access.html" %} {% include "./partials/modal/access.html" %}
{% include "./partials/modal/folder-settings.html" %} {% include "./partials/modal/folder-settings.html" %}
{% include "./partials/modal/support.html" %}
{% include "./partials/modal/login.html" %}
{% include "./partials/modal/pdfoptions.html" %}
</body> </body>
<script type="text/javascript"> <script type="text/javascript">