WIP first partially working version without mongodb, using sqlite/sequelize

This commit is contained in:
Lukas F. Hartmann 2018-04-11 19:59:18 +02:00
parent 8e0bc69a11
commit 960a4d6866
42 changed files with 1124 additions and 1701 deletions

32
app.js
View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
require('./models/schema'); const db = require('./models/db.js');
require("log-timestamp"); require("log-timestamp");
const config = require('config'); const config = require('config');
@ -16,7 +16,7 @@ const logger = require('morgan');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const mongoose = require('mongoose'); //const mongoose = require('mongoose');
const swig = require('swig'); const swig = require('swig');
const i18n = require('i18n-2'); const i18n = require('i18n-2');
@ -81,33 +81,30 @@ app.use(helmet.noSniff())
// CUSTOM MIDDLEWARES // CUSTOM MIDDLEWARES
app.use(require("./middlewares/templates")); //app.use(require("./middlewares/error_helpers"));
app.use(require("./middlewares/error_helpers")); app.use(require("./middlewares/session"));
app.use(require("./middlewares/setuser")); //app.use(require("./middlewares/cors"));
app.use(require("./middlewares/cors")); //app.use(require("./middlewares/i18n"));
app.use(require("./middlewares/i18n"));
app.use("/api", require("./middlewares/api_helpers")); app.use("/api", require("./middlewares/api_helpers"));
app.use('/api/spaces/:id', require("./middlewares/space_helpers")); app.use('/api/spaces/:id', require("./middlewares/space_helpers"));
app.use('/api/spaces/:id/artifacts/:artifact_id', require("./middlewares/artifact_helpers")); app.use('/api/spaces/:id/artifacts/:artifact_id', require("./middlewares/artifact_helpers"));
app.use('/api/teams', require("./middlewares/team_helpers"));
// REAL ROUTES // REAL ROUTES
app.use('/api/users', require('./routes/api/users')); app.use('/api/users', require('./routes/api/users'));
app.use('/api/memberships', require('./routes/api/memberships')); //app.use('/api/memberships', require('./routes/api/memberships'));
const spaceRouter = require('./routes/api/spaces'); const spaceRouter = require('./routes/api/spaces');
app.use('/api/spaces', spaceRouter); app.use('/api/spaces', spaceRouter);
spaceRouter.use('/:id/artifacts', require('./routes/api/space_artifacts')); spaceRouter.use('/:id/artifacts', require('./routes/api/space_artifacts'));
spaceRouter.use('/:id/memberships', require('./routes/api/space_memberships')); spaceRouter.use('/:id/memberships', require('./routes/api/space_memberships'));
spaceRouter.use('/:id/messages', require('./routes/api/space_messages')); //spaceRouter.use('/:id/messages', require('./routes/api/space_messages'));
spaceRouter.use('/:id/digest', require('./routes/api/space_digest')); spaceRouter.use('/:id/digest', require('./routes/api/space_digest'));
spaceRouter.use('/:id', require('./routes/api/space_exports')); //spaceRouter.use('/:id', require('./routes/api/space_exports'));
app.use('/api/sessions', require('./routes/api/sessions')); app.use('/api/sessions', require('./routes/api/sessions'));
app.use('/api/teams', require('./routes/api/teams')); //app.use('/api/webgrabber', require('./routes/api/webgrabber'));
app.use('/api/webgrabber', require('./routes/api/webgrabber'));
app.use('/', require('./routes/root')); app.use('/', require('./routes/root'));
if (config.get('storage_local_path')) { if (config.get('storage_local_path')) {
@ -117,7 +114,7 @@ if (config.get('storage_local_path')) {
} }
// catch 404 and forward to error handler // catch 404 and forward to error handler
app.use(require('./middlewares/404')); //app.use(require('./middlewares/404'));
if (app.get('env') == 'development') { if (app.get('env') == 'development') {
app.set('view cache', false); app.set('view cache', false);
swig.setDefaults({cache: false}); swig.setDefaults({cache: false});
@ -128,8 +125,9 @@ if (app.get('env') == 'development') {
module.exports = app; module.exports = app;
// CONNECT TO DATABASE // CONNECT TO DATABASE
const mongoHost = process.env.MONGO_PORT_27017_TCP_ADDR || config.get('mongodb_host'); //const mongoHost = process.env.MONGO_PORT_27017_TCP_ADDR || config.get('mongodb_host');
mongoose.connect('mongodb://' + mongoHost + '/spacedeck'); //mongoose.connect('mongodb://' + mongoHost + '/spacedeck');
db.init();
// START WEBSERVER // START WEBSERVER
const port = 9666; const port = 9666;
@ -174,7 +172,7 @@ redis.connectRedis();
process.on('message', (message) => { process.on('message', (message) => {
console.log("Process message:", message); console.log("Process message:", message);
if (message === 'shutdown') { if (message === 'shutdown') {
console.log("Exiting spacedeck."); console.log("Exiting Spacedeck.");
process.exit(0); process.exit(0);
} }
}); });

View File

@ -4,11 +4,15 @@ const exec = require('child_process');
const gm = require('gm'); const gm = require('gm');
const async = require('async'); const async = require('async');
const fs = require('fs'); const fs = require('fs');
const Models = require('../models/schema'); const Models = require('../models/db');
const uploader = require('../helpers/uploader'); const uploader = require('../helpers/uploader');
const path = require('path'); const path = require('path');
const os = require('os'); const os = require('os');
const db = require('../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const fileExtensionMap = { const fileExtensionMap = {
".amr" : "audio/AMR", ".amr" : "audio/AMR",
".ogg" : "audio/ogg", ".ogg" : "audio/ogg",
@ -301,15 +305,11 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF
a.payload_uri = results.original; a.payload_uri = results.original;
var factor = 320/size.width; var factor = 320/size.width;
var newBoardSpecs = a.board; a.w = Math.round(size.width*factor);
newBoardSpecs.w = Math.round(size.width*factor); a.h = Math.round(size.height*factor);
newBoardSpecs.h = Math.round(size.height*factor);
a.board = newBoardSpecs;
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err) { a.save().then(function() {
if(err) payloadCallback(err, null);
else {
fs.unlink(originalFilePath, function (err) { fs.unlink(originalFilePath, function (err) {
if (err){ if (err){
console.error(err); console.error(err);
@ -319,7 +319,6 @@ var resizeAndUploadImage = function(a, mime, size, fileName, fileNameOrg, imageF
payloadCallback(null, a); payloadCallback(null, a);
} }
}); });
}
}); });
}); });
}; };
@ -371,15 +370,11 @@ module.exports = {
a.payload_uri = url; a.payload_uri = url;
var factor = 320/size.width; var factor = 320/size.width;
var newBoardSpecs = a.board; a.w = Math.round(size.width*factor);
newBoardSpecs.w = Math.round(size.width*factor); a.h = Math.round(size.height*factor);
newBoardSpecs.h = Math.round(size.height*factor);
a.board = newBoardSpecs;
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err){ a.save().then(function() {
if(err) payloadCallback(err, null);
else {
fs.unlink(localFilePath, function (err) { fs.unlink(localFilePath, function (err) {
if (err){ if (err){
console.error(err); console.error(err);
@ -389,7 +384,6 @@ module.exports = {
payloadCallback(null, a); payloadCallback(null, a);
} }
}); });
}
}); });
} }
}); });
@ -483,6 +477,8 @@ module.exports = {
]; ];
} }
db.packArtifact(a);
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err) { a.save(function(err) {
if (err) payloadCallback(err, null); if (err) payloadCallback(err, null);
@ -564,9 +560,10 @@ module.exports = {
]; ];
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err){
if(err) payloadCallback(err, null); db.packArtifact(a);
else {
a.save().then(function(){
fs.unlink(localFilePath, function (err) { fs.unlink(localFilePath, function (err) {
if (err){ if (err){
console.error(err); console.error(err);
@ -576,7 +573,6 @@ module.exports = {
payloadCallback(null, a); payloadCallback(null, a);
} }
}); });
}
}); });
} }
}); });
@ -594,13 +590,10 @@ module.exports = {
a.payload_uri = url; a.payload_uri = url;
a.updated_at = new Date(); a.updated_at = new Date();
a.save(function(err) { a.save().then(function() {
if(err) payloadCallback(err, null);
else {
fs.unlink(localFilePath, function (err) { fs.unlink(localFilePath, function (err) {
payloadCallback(null, a); payloadCallback(null, a);
}); });
}
}); });
}); });
} }

View File

@ -5,7 +5,7 @@ const config = require('config')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
require('../models/schema') require('../models/db')
module.exports = { module.exports = {
importZIP: function(user, zipPath) { importZIP: function(user, zipPath) {

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
var config = require('config'); var config = require('config');
var phantom = require('node-phantom-simple'); var phantom = require('node-phantom-simple');

View File

@ -1,5 +1,8 @@
'use strict'; 'use strict';
require('../models/schema');
const db = require('../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const config = require('config'); const config = require('config');
@ -8,7 +11,6 @@ const WebSocketServer = require('ws').Server;
const RedisConnection = require('ioredis'); const RedisConnection = require('ioredis');
const async = require('async'); const async = require('async');
const _ = require("underscore"); const _ = require("underscore");
const mongoose = require("mongoose");
const crypto = require('crypto'); const crypto = require('crypto');
const redisMock = require("./redis.js"); const redisMock = require("./redis.js");
@ -45,11 +47,11 @@ module.exports = {
const editorAuth = msg.editor_auth; const editorAuth = msg.editor_auth;
const spaceId = msg.space_id; const spaceId = msg.space_id;
Space.findOne({"_id": spaceId}).populate('creator').exec((err, space) => { db.Space.findOne({where: {"_id": spaceId}}).then(space => {
if (space) { if (space) {
const upgradeSocket = function() { const upgradeSocket = function() {
if (token) { if (token) {
User.findBySessionToken(token, function(err, user) { db.findUserBySessionToken(token, function(err, user) {
if (err) { if (err) {
console.error(err, user); console.error(err, user);
} else { } else {
@ -268,10 +270,10 @@ module.exports = {
}, },
distributeUsers: function(spaceId) { distributeUsers: function(spaceId) {
if(!spaceId) if (!spaceId)
return; return;
this.state.smembers("space_" + spaceId, function(err, list) { /*this.state.smembers("space_" + spaceId, function(err, list) {
async.map(list, function(item, callback) { async.map(list, function(item, callback) {
this.state.get(item, function(err, userId) { this.state.get(item, function(err, userId) {
console.log(item, "->", userId); console.log(item, "->", userId);
@ -292,16 +294,14 @@ module.exports = {
return {nickname: realNickname, email: null, avatar_thumbnail_uri: null }; return {nickname: realNickname, email: null, avatar_thumbnail_uri: null };
}); });
User.find({"_id" : { "$in" : validUserIds }}, { "nickname" : 1 , "email" : 1, "avatar_thumbnail_uri": 1 }, function(err, users) { db.User.findAll({where: {
if (err) "_id" : { "$in" : validUserIds }}, attributes: ["nickname","email","avatar_thumbnail_uri"]})
console.error(err); .then(users) {
else {
const allUsers = users.concat(anonymousUsers); const allUsers = users.concat(anonymousUsers);
const strUsers = JSON.stringify({users: allUsers, space_id: spaceId}); const strUsers = JSON.stringify({users: allUsers, space_id: spaceId});
this.state.publish("users", strUsers); this.state.publish("users", strUsers);
}
}.bind(this));
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));
}.bind(this));*/
} }
}; };

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {

View File

@ -1,9 +1,11 @@
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
var config = require('config'); var config = require('config');
const redis = require('../helpers/redis'); const redis = require('../helpers/redis');
// FIXME TODO object.toJSON()
var saveAction = (actionKey, object) => { var saveAction = (actionKey, object) => {
if (object.constructor.modelName == "Space") if (object.constructor.modelName == "Space")
return; return;
@ -13,14 +15,14 @@ var saveAction = (actionKey, object) => {
space: object.space_id || object.space, space: object.space_id || object.space,
user: object.user_id || object.user, user: object.user_id || object.user,
editor_name: object.editor_name, editor_name: object.editor_name,
object: object.toJSON() object: object
}; };
let action = new Action(attr); /*let action = new Action(attr);
action.save(function(err) { action.save(function(err) {
if (err) if (err)
console.error("saved create action err:", err); console.error("saved create action err:", err);
}); });*/
}; };
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
@ -32,21 +34,21 @@ module.exports = (req, res, next) => {
res['distributeCreate'] = function(model, object) { res['distributeCreate'] = function(model, object) {
if (!object) return; if (!object) return;
redis.sendMessage("create", model, object.toJSON(), req.channelId); redis.sendMessage("create", model, object, req.channelId);
this.status(201).json(object.toJSON()); this.status(201).json(object);
saveAction("create", object); saveAction("create", object);
}; };
res['distributeUpdate'] = function(model, object) { res['distributeUpdate'] = function(model, object) {
if (!object) return; if (!object) return;
redis.sendMessage("update", model, object.toJSON(), req.channelId); redis.sendMessage("update", model, object, req.channelId);
this.status(200).json(object.toJSON()); this.status(200).json(object);
saveAction("update", object); saveAction("update", object);
}; };
res['distributeDelete'] = function(model, object) { res['distributeDelete'] = function(model, object) {
if (!object) return; if (!object) return;
redis.sendMessage("delete", model, object.toJSON(), req.channelId); redis.sendMessage("delete", model, object, req.channelId);
this.sendStatus(204); this.sendStatus(204);
saveAction("delete", object); saveAction("delete", object);
}; };

View File

@ -1,22 +1,20 @@
'use strict'; 'use strict';
const db = require('../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
require('../models/schema');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
var artifactId = req.params.artifact_id; var artifactId = req.params.artifact_id;
Artifact.findOne({ db.Artifact.findOne({where: {
"_id": artifactId "_id": artifactId
}, (err, artifact) => { }}).then(artifact => {
if (err) {
res.status(400).json(err);
} else {
if (artifact) { if (artifact) {
req['artifact'] = artifact; req['artifact'] = artifact;
next(); next();
} else { } else {
res.sendStatus(404); res.sendStatus(404);
} }
}
}); });
}; };

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
const config = require('config'); const config = require('config');
const url = require('url'); const url = require('url');
@ -33,13 +33,13 @@ module.exports = (req, res, next) => {
respond(origin, req, res, next); respond(origin, req, res, next);
} else { } else {
Team.getTeamForHost(parsedUrl.hostname, (err, team, subdomain) => { //Team.getTeamForHost(parsedUrl.hostname, (err, team, subdomain) => {
if (team) { //if (team) {
respond(origin, req, res, next); respond(origin, req, res, next);
} else { //} else {
next(); next();
} //}
}); //});
} }
} else { } else {

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
require('../models/schema'); require('../models/db');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {

46
middlewares/session.js Normal file
View File

@ -0,0 +1,46 @@
'use strict';
const db = require('../models/db');
var config = require('config');
module.exports = (req, res, next) => {
const token = req.cookies["sdsession"];
if (token && token != "null" && token != null) {
db.Session.findOne({where: {token: token}})
.then(session => {
if (!session) {
// session not found
next();
}
else db.User.findOne({where: {_id: session.user_id}})
.then(user => {
if (!user) {
res.clearCookie('sdsession');
if (req.accepts("text/html")) {
res.send("Please clear your cookies and try again.");
} else if (req.accepts('application/json')) {
res.status(403).json({
"error": "token_not_found"
});
} else {
res.send("Please clear your cookies and try again.");
}
} else {
req["token"] = token;
req["user"] = user;
next();
}
});
})
.error(err => {
console.error("Session resolve error",err);
next();
});
} else {
next();
}
}

View File

@ -1,35 +0,0 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
const token = req.cookies["sdsession"];
if (token && token != "null" && token !== null) {
User.findOne({
"sessions.token": token
}).populate('team').exec((err, user) => {
if (err) console.error("session.token lookup error:",err);
if (!user) {
res.clearCookie('sdsession');
if (req.accepts("text/html")) {
res.send("Please clear your cookies and try again.");
} else if (req.accepts('application/json')) {
res.status(403).json({
"error": "token_not_found"
});
} else {
res.send("Please clear your cookies and try again.");
}
} else {
req["token"] = token;
req["user"] = user;
next();
}
});
} else {
next();
}
}

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
require('../models/schema'); const db = require('../models/db');
var config = require('config'); var config = require('config');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
@ -19,50 +19,6 @@ module.exports = (req, res, next) => {
} }
}; };
var rolePerUser = (originalSpace, user, cb) => {
originalSpace.path = [];
if (originalSpace._id.equals(req.user.home_folder_id) || (originalSpace.creator && originalSpace.creator._id.equals(req.user._id))) {
cb("admin");
} else {
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
Membership.find({
"space": space._id
}, function(err, parentMemberships) {
var currentMemberships = parentMemberships.concat(allMemberships);
if (space.parent_space_id) {
Space.findOne({
"_id": space.parent_space_id
}, function(err, parentSpace) {
findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
});
} else {
// reached the top
var role = prevRole;
space.memberships = currentMemberships;
if(role == "none"){
if(originalSpace.access_mode == "public") {
role = "viewer";
}
}
currentMemberships.forEach(function(m, i) {
if (m.user && m.user.equals(user._id)) {
role = m.role;
}
});
cb(role);
}
});
};
findMembershipsForSpace(originalSpace, [], "none");
}
};
var finalizeAnonymousLogin = function(space, spaceAuth) { var finalizeAnonymousLogin = function(space, spaceAuth) {
var role = "none"; var role = "none";
@ -77,7 +33,7 @@ module.exports = (req, res, next) => {
} }
if (req.user) { if (req.user) {
rolePerUser(space, req.user, function(newRole) { db.getUserRoleInSpace(space, req.user, function(newRole) {
if (newRole == "admin" && (role == "editor" || role == "viewer")) { if (newRole == "admin" && (role == "editor" || role == "viewer")) {
finalizeReq(space, newRole); finalizeReq(space, newRole);
} else if (newRole == "editor" && (role == "viewer")) { } else if (newRole == "editor" && (role == "viewer")) {
@ -97,16 +53,17 @@ module.exports = (req, res, next) => {
'email': 1 'email': 1
}; };
Space.findOne({ db.Space.findOne({where: {
"_id": spaceId "_id": spaceId
}).populate("creator", userMapping).exec(function(err, space) { }}).then(function(space) {
if (err) {
res.status(400).json(err); //.populate("creator", userMapping)
} else { //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) {
if (req.spacePassword) { if (req.spacePassword) {
if (req.spacePassword === space.password) { if (req.spacePassword === space.password) {
@ -126,6 +83,8 @@ module.exports = (req, res, next) => {
} }
} else { } else {
// space is private
// special permission for screenshot/pdf export from backend // special permission for screenshot/pdf export from backend
if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) { if (req.query['api_token'] && req.query['api_token'] == config.get('phantom_api_secret')) {
finalizeReq(space, "viewer"); finalizeReq(space, "viewer");
@ -133,7 +92,7 @@ module.exports = (req, res, next) => {
} }
if (req.user) { if (req.user) {
rolePerUser(space, req.user, function(role) { db.getUserRoleInSpace(space, req.user, function(role) {
if (role == "none") { if (role == "none") {
finalizeAnonymousLogin(space, req["spaceAuth"]); finalizeAnonymousLogin(space, req["spaceAuth"]);
} else { } else {
@ -155,6 +114,5 @@ module.exports = (req, res, next) => {
"error": "space_not_found" "error": "space_not_found"
}); });
} }
}
}); });
} }

View File

@ -1,33 +0,0 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
let host = req.headers.host;
Team.getTeamForHost(host, (err, team, subdomain) => {
if (subdomain) {
if (!err && team) {
req.subdomainTeam = team;
req.subdomain = subdomain;
next()
} else {
if (req.accepts('text/html')) {
res.status(404).render('not_found', {
title: 'Page Not Found.'
});
} else if (req.accepts('application/json')) {
res.status(404).json({
"error": "not_found"
});
} else {
res.status(404).render('not_found', {
title: 'Page Not Found.'
});
}
}
} else {
next();
}
});
}

View File

@ -1,23 +0,0 @@
'use strict';
require('../models/schema');
var config = require('config');
module.exports = (req, res, next) => {
if (req.user) {
var isAdmin = req.user.team.admins.indexOf(req.user._id) >= 0;
var correctMethod = req.method == "GET" || (req.method == "DELETE" || req.method == "PUT" || req.method == "POST");
if (correctMethod && isAdmin) {
next();
} else {
res.status(403, {
"error": "not authorized"
});
}
} else {
res.status(403, {
"error": "not logged in"
});
}
}

View File

@ -1,31 +0,0 @@
'use strict';
require('../models/schema');
var config = require('config');
var _ = require('underscore');
module.exports = (req, res, next) => {
res.oldRender = res.render;
res.render = function(template, params) {
var team = req.subdomainTeam;
if (team) {
team = _.pick(team.toObject(), ['_id', 'name', 'subdomain', 'avatar_original_uri']);
} else {
team = null;
}
const addParams = {
locale: req.i18n.locale,
config: config,
subdomain_team: team,
user: req.user,
csrf_token: "",
socket_auth: req.token
};
const all = _.extend(params, addParams);
res.oldRender(template, all);
};
next();
}

284
models/db.js Normal file
View File

@ -0,0 +1,284 @@
//'use strict';
//var mongoose = require('mongoose');
//const sqlite3 = require('sqlite3').verbose();
function sequel_log(a,b,c) {
console.log(a);
}
const Sequelize = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'sqlite',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
// SQLite only
storage: 'database.sqlite',
logging: sequel_log,
// http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
operatorsAliases: false
});
var User;
var Session;
var Space;
var Membership;
var Artifact;
var Message;
var Action;
module.exports = {
User: sequelize.define('user', {
_id: {type: Sequelize.STRING, primaryKey: true},
email: Sequelize.STRING,
password_hash: Sequelize.STRING,
nickname: Sequelize.STRING,
avatar_original_uri: Sequelize.STRING,
avatar_thumb_uri: Sequelize.STRING,
confirmation_token: Sequelize.STRING,
password_reset_token: Sequelize.STRING,
home_folder_id: Sequelize.STRING,
prefs_language: Sequelize.STRING,
prefs_email_notifications: Sequelize.STRING,
prefs_email_digest: Sequelize.STRING,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}),
Session: sequelize.define('session', {
token: {type: Sequelize.STRING, primaryKey: true},
user_id: Sequelize.STRING,
expires: Sequelize.DATE,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
device: Sequelize.STRING,
ip: Sequelize.STRING
}),
Space: sequelize.define('space', {
_id: {type: Sequelize.STRING, primaryKey: true},
name: {type: Sequelize.STRING, default: "New Space"},
space_type: {type: Sequelize.STRING, defaultValue: "space"},
creator_id: Sequelize.STRING,
parent_space_id: Sequelize.STRING,
access_mode: {type: Sequelize.STRING, default: "private"}, // "public" || "private"
password: Sequelize.STRING,
edit_hash: Sequelize.STRING,
edit_slug: Sequelize.STRING,
editors_locking: Sequelize.BOOLEAN,
thumbnail_uri: Sequelize.STRING,
width: Sequelize.INTEGER,
height: Sequelize.INTEGER,
background_color: Sequelize.STRING,
background_uri: Sequelize.STRING,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
thumbnail_url: Sequelize.STRING,
thumbnail_updated_at: {type: Sequelize.DATE}
}),
Membership: sequelize.define('membership', {
_id: {type: Sequelize.STRING, primaryKey: true},
space_id: Sequelize.STRING,
user_id: Sequelize.STRING,
role: Sequelize.STRING,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}),
Artifact: sequelize.define('message', {
_id: {type: Sequelize.STRING, primaryKey: true},
space_id: Sequelize.STRING,
user_id: Sequelize.STRING,
editor_name: Sequelize.STRING,
message: Sequelize.TEXT,
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}),
Artifact: sequelize.define('artifact', {
_id: {type: Sequelize.STRING, primaryKey: true},
space_id: Sequelize.STRING,
user_id: Sequelize.STRING,
mime: Sequelize.STRING,
thumbnail_uri: Sequelize.STRING,
last_update_user_id: Sequelize.STRING,
editor_name: Sequelize.STRING,
last_update_editor_name: Sequelize.STRING,
description: Sequelize.TEXT,
state: {type: Sequelize.STRING, default: "idle"},
//linked_to: Sequelize.STRING,
title: Sequelize.STRING,
tags: Sequelize.TEXT,
search_text: Sequelize.STRING,
link_uri: Sequelize.STRING,
play_from: Sequelize.DECIMAL,
play_to: Sequelize.DECIMAL,
x: {type: Sequelize.DECIMAL, default: 0.0},
y: {type: Sequelize.DECIMAL, default: 0.0},
z: {type: Sequelize.DECIMAL, default: 0.0},
r: {type: Sequelize.DECIMAL, default: 0.0},
w: {type: Sequelize.DECIMAL, default: 100},
h: {type: Sequelize.DECIMAL, default: 100},
//control_points: [{
// dx: Number, dy: Number
//}],
control_points: Sequelize.TEXT,
group: Sequelize.STRING,
locked: {type: Sequelize.BOOLEAN, default: false},
payload_uri: Sequelize.STRING,
payload_thumbnail_web_uri: Sequelize.STRING,
payload_thumbnail_medium_uri: Sequelize.STRING,
payload_thumbnail_big_uri: Sequelize.STRING,
payload_size: Sequelize.INTEGER, // file size in bytes
fill_color: {type: Sequelize.STRING, default: "transparent"},
stroke_color: {type: Sequelize.STRING, default: "#000000"},
text_color: Sequelize.STRING,
stroke: {type: Sequelize.DECIMAL, default: 0.0},
stroke_style: {type: Sequelize.STRING, default: "solid"},
alpha: {type: Sequelize.DECIMAL, default: 1.0},
order: {type: Sequelize.INTEGER, default: 0},
crop_x: Sequelize.INTEGER,
crop_y: Sequelize.INTEGER,
crop_w: Sequelize.INTEGER,
crop_h: Sequelize.INTEGER,
shape: Sequelize.STRING,
shape_svg: Sequelize.STRING,
padding_left: Sequelize.INTEGER,
padding_right: Sequelize.INTEGER,
padding_top: Sequelize.INTEGER,
padding_bottom: Sequelize.INTEGER,
margin_left: Sequelize.INTEGER,
margin_right: Sequelize.INTEGER,
margin_top: Sequelize.INTEGER,
margin_bottom: Sequelize.INTEGER,
border_radius: Sequelize.INTEGER,
align: {type: Sequelize.STRING, default: "left"},
valign: {type: Sequelize.STRING, default: "top"},
brightness: Sequelize.DECIMAL,
contrast: Sequelize.DECIMAL,
saturation: Sequelize.DECIMAL,
blur: Sequelize.DECIMAL,
hue: Sequelize.DECIMAL,
opacity: Sequelize.DECIMAL,
payload_alternatives: Sequelize.TEXT,
/*payload_alternatives: [{
mime: String,
payload_uri: String,
payload_thumbnail_web_uri: String,
payload_thumbnail_medium_uri: String,
payload_thumbnail_big_uri: String,
payload_size: Number
}],*/
created_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW},
updated_at: {type: Sequelize.DATE, defaultValue: Sequelize.NOW}
}),
init: function() {
sequelize.sync();
},
getUserRoleInSpace: (originalSpace, user, cb) => {
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)) {
cb("admin");
} else {
var findMembershipsForSpace = function(space, allMemberships, prevRole) {
Membership.findAll({ where: {
"space": space._id
}}).then(function(parentMemberships) {
var currentMemberships = parentMemberships.concat(allMemberships);
if (space.parent_space_id) {
Space.findOne({ where: {
"_id": space.parent_space_id
}}, function(err, parentSpace) {
findMembershipsForSpace(parentSpace, currentMemberships, prevRole);
});
} else {
// reached the top
var role = prevRole;
space.memberships = currentMemberships;
if (role == "none") {
if (originalSpace.access_mode == "public") {
role = "viewer";
}
}
currentMemberships.forEach(function(m, i) {
if (m.user_id && m.user_id == user._id) {
role = m.role;
}
});
cb(role);
}
});
};
findMembershipsForSpace(originalSpace, [], "none");
}
},
spaceToObject: (space) => {
// FIXME TODO
return space;
},
findUserBySessionToken: (token, cb) => {
db.Session.findOne({where: {token: token}})
.then(session => {
if (!session) cb(null, null)
else db.User.findOne({where: {_id: session.user_id}})
.then(user => {
cb(null, user)
})
})
},
unpackArtifact: (a) => {
if (a.control_points) {
a.control_points = JSON.parse(a.control_points);
}
if (a.payload_alternatives) {
a.payload_alternatives = JSON.parse(a.payload_alternatives);
}
return a;
},
packArtifact: (a) => {
if (a.control_points) {
a.control_points = JSON.stringify(a.control_points);
}
if (a.payload_alternatives) {
a.payload_alternatives = JSON.stringify(a.payload_alternatives);
}
return a;
}
}

View File

@ -1,21 +0,0 @@
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
module.exports.domainSchema = mongoose.Schema({
domain: String,
edu: Boolean,
created_at: {
type: Date,
default: Date.now
},
updated_at: {
type: Date,
default: Date.now
}
});
module.exports.domainSchema.index({
domain: 1
});

View File

@ -1,44 +0,0 @@
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
Plan = mongoose.model('Plan', {
key: String,
description: String,
limit_folders: {
type: Number,
default: 200
},
limit_spaces: {
type: Number,
default: 500
},
limit_storage_bytes: {
type: Number,
default: 10737418240
},
plan_type: {
type: String,
default: "org"
},
price: Number,
public: Boolean,
recurring: {
type: String,
default: "month"
},
title: String,
trial_days: Number,
voucher_code: String,
created_at: {
type: Date,
default: Date.now
},
updated_at: {
type: Date,
default: Date.now
}
});
exports.planModel = Plan;

View File

@ -1,12 +0,0 @@
//'use strict';
var mongoose = require('mongoose');
User = mongoose.model('User', require('./user').userSchema);
Action = mongoose.model('Action', require('./action').actionSchema);
Space = mongoose.model('Space', require('./space').spaceSchema);
Artifact = mongoose.model('Artifact', require('./artifact').artifactSchema);
Team = mongoose.model('Team', require('./team').teamSchema);
Message = mongoose.model('Message', require('./message').messageSchema);
Membership = mongoose.model('Membership', require('./membership').membershipSchema);
Domain = mongoose.model('Domain', require('./domain').domainSchema);

View File

@ -1,70 +0,0 @@
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
module.exports.teamSchema = mongoose.Schema({
name: String,
subdomain: String,
creator: {
type: Schema.Types.ObjectId,
ref: 'User'
},
admins: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
invitation_codes: [String],
avatar_thumb_uri: String,
avatar_uri: String,
payment_type: {
type: String,
default: "auto"
},
payment_plan_key: String,
payment_subscription_id: String,
blocked_at: {
type: Date
},
upgraded_at: {
type: Date
},
created_at: {
type: Date,
default: Date.now
},
updated_at: {
type: Date,
default: Date.now
}
});
module.exports.teamSchema.index({
creator: 1
});
module.exports.teamSchema.statics.getTeamForHost = (host, cb) => {
if (host != "127.0.0.1:9666") { //phantomjs check
let subDomainParts = host.split('.');
if (subDomainParts.length > 2) {
const subdomain = subDomainParts[0];
if (subdomain != "www") {
Team.findOne({
subdomain: subdomain
}).exec((err, team) => {
cb(err, team, subdomain)
});
} else {
cb(null, null)
}
} else {
cb(null, null);
}
} else {
cb(null, null);
}
}

View File

@ -53,11 +53,14 @@
"raven": "1.2.0", "raven": "1.2.0",
"request": "2.81.0", "request": "2.81.0",
"sanitize-html": "^1.11.1", "sanitize-html": "^1.11.1",
"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": "0.9.1", "slug": "0.9.1",
"sqlite3": "^4.0.0",
"swig": "1.4.2", "swig": "1.4.2",
"underscore": "1.8.3", "underscore": "1.8.3",
"uuid": "^3.2.1",
"validator": "7.0.0", "validator": "7.0.0",
"weak": "1.0.1", "weak": "1.0.1",
"ws": "2.2.3" "ws": "2.2.3"

View File

@ -36,13 +36,11 @@ SpacedeckAccount = {
save_user_language: function(lang) { save_user_language: function(lang) {
localStorage.lang = lang; localStorage.lang = lang;
if (this.user.preferences) { this.user.prefs_language = lang;
this.user.preferences.language = lang;
this.save_user(function() { this.save_user(function() {
window._spacedeck_location_change = true; window._spacedeck_location_change = true;
location.href="/spaces"; location.href="/spaces";
}.bind(this)); }.bind(this));
}
}, },
save_user: function(on_success) { save_user: function(on_success) {

View File

@ -61,16 +61,16 @@ var SpacedeckBoardArtifacts = {
}, },
artifact_link: function(a) { artifact_link: function(a) {
if (a.meta && a.meta.link_uri) { if (a.link_uri) {
return a.meta.link_uri; return a.link_uri;
} else { } else {
return ""; return "";
} }
}, },
artifact_link_caption: function(a) { artifact_link_caption: function(a) {
if (a.meta && a.meta.link_uri) { if (a.link_uri) {
var parts = a.meta.link_uri.split("/"); var parts = a.link_uri.split("/");
// scheme://domain.foo/... // scheme://domain.foo/...
// 0 1 2 // 0 1 2
if (parts.length>2) { if (parts.length>2) {
@ -102,10 +102,8 @@ var SpacedeckBoardArtifacts = {
if (this.artifact_is_selected(a) && this.editing_artifact_id!=a._id) clzs.push("selected"); if (this.artifact_is_selected(a) && this.editing_artifact_id!=a._id) clzs.push("selected");
if (!a._id) clzs.push("creating"); if (!a._id) clzs.push("creating");
if (a.style) { if (a.align) clzs.push("align-"+a.align);
clzs.push("align-"+a.style.align); if (a.valign) clzs.push("align-"+a.valign);
clzs.push("align-"+a.style.valign);
}
clzs.push("state-"+a.state); clzs.push("state-"+a.state);
@ -123,56 +121,56 @@ var SpacedeckBoardArtifacts = {
artifact_inner_style: function(a) { artifact_inner_style: function(a) {
var styles = []; var styles = [];
if (a.style) { //if (a.style) {
var svg_style = ((a.mime.match("vector") || a.mime.match("shape")) && a.style.shape!="square"); var svg_style = ((a.mime.match("vector") || a.mime.match("shape")) && a.shape!="square");
if (!svg_style) { if (!svg_style) {
if (a.style.stroke) { if (a.stroke) {
styles.push("border-width:"+a.style.stroke+"px"); styles.push("border-width:"+a.stroke+"px");
styles.push("border-style:"+(a.style.stroke_style||"solid")); styles.push("border-style:"+(a.stroke_style||"solid"));
} }
if (a.style.stroke_color) { if (a.stroke_color) {
styles.push("border-color:"+a.style.stroke_color); styles.push("border-color:"+a.stroke_color);
} }
if (a.style.border_radius) { if (a.border_radius) {
styles.push("border-radius:"+a.style.border_radius+"px"); styles.push("border-radius:"+a.border_radius+"px");
} }
} }
if (a.style.fill_color && !svg_style) { if (a.fill_color && !svg_style) {
styles.push("background-color:"+a.style.fill_color); styles.push("background-color:"+a.fill_color);
} }
if (a.style.text_color) { if (a.text_color) {
styles.push("color:"+a.style.text_color); styles.push("color:"+a.text_color);
} }
var filters = []; var filters = [];
if (!isNaN(a.style.brightness) && a.style.brightness != 100) { if (!isNaN(a.brightness) && a.brightness != 100) {
filters.push("brightness("+a.style.brightness+"%)"); filters.push("brightness("+a.brightness+"%)");
} }
if (!isNaN(a.style.contrast) && a.style.contrast != 100) { if (!isNaN(a.contrast) && a.contrast != 100) {
filters.push("contrast("+a.style.contrast+"%)"); filters.push("contrast("+a.contrast+"%)");
} }
if (!isNaN(a.style.opacity) && a.style.opacity != 100) { if (!isNaN(a.opacity) && a.opacity != 100) {
filters.push("opacity("+a.style.opacity+"%)"); filters.push("opacity("+a.opacity+"%)");
} }
if (!isNaN(a.style.hue) && a.style.hue) { if (!isNaN(a.hue) && a.hue) {
filters.push("hue-rotate("+a.style.hue+"deg)"); filters.push("hue-rotate("+a.hue+"deg)");
} }
if (!isNaN(a.style.saturation) && a.style.saturation != 100) { if (!isNaN(a.saturation) && a.saturation != 100) {
filters.push("saturate("+a.style.saturation+"%)"); filters.push("saturate("+a.saturation+"%)");
} }
if (!isNaN(a.style.blur) && a.style.blur) { if (!isNaN(a.blur) && a.blur) {
filters.push("blur("+a.style.blur+"px)"); filters.push("blur("+a.blur+"px)");
} }
if (filters.length) { if (filters.length) {
styles.push("-webkit-filter:"+filters.join(" ")); styles.push("-webkit-filter:"+filters.join(" "));
styles.push("filter:"+filters.join(" ")); styles.push("filter:"+filters.join(" "));
} }
} //}
return styles.join(";"); return styles.join(";");
}, },
@ -180,12 +178,10 @@ var SpacedeckBoardArtifacts = {
artifact_text_cell_style: function(a, for_text_editor) { artifact_text_cell_style: function(a, for_text_editor) {
var styles = []; var styles = [];
if (a.style) { if (a.padding_left) styles.push("padding-left:"+a.padding_left+"px");
if (a.style.padding_left) styles.push("padding-left:"+a.style.padding_left+"px"); if (a.padding_right) styles.push("padding-right:"+a.padding_right+"px");
if (a.style.padding_right) styles.push("padding-right:"+a.style.padding_right+"px"); if (a.padding_top) styles.push("padding-top:"+a.padding_top+"px");
if (a.style.padding_top) styles.push("padding-top:"+a.style.padding_top+"px"); if (a.padding_bottom) styles.push("padding-bottom:"+a.padding_bottom+"px");
if (a.style.padding_bottom) styles.push("padding-bottom:"+a.style.padding_bottom+"px");
}
return styles.join(";"); return styles.join(";");
}, },
@ -194,25 +190,21 @@ var SpacedeckBoardArtifacts = {
var styles = []; var styles = [];
var z = 0; var z = 0;
if (a.board) { z = a.z;
z = a.board.z;
if (z<0) z=0; // fix negative z-index if (z<0) z=0; // fix negative z-index
styles = [ styles = [
"left:" +a.board.x+"px", "left:" +a.x+"px",
"top:" +a.board.y+"px", "top:" +a.y+"px",
"width:" +a.board.w+"px", "width:" +a.w+"px",
"height:"+a.board.h+"px", "height:"+a.h+"px",
"z-index:"+z "z-index:"+z
]; ];
}
if (a.style) { if (a.margin_left) styles.push("margin-left:"+a.margin_left+"px");
if (a.style.margin_left) styles.push("margin-left:"+a.style.margin_left+"px"); if (a.margin_right) styles.push("margin-right:"+a.margin_right+"px");
if (a.style.margin_right) styles.push("margin-right:"+a.style.margin_right+"px"); if (a.margin_top) styles.push("margin-top:"+a.margin_top+"px");
if (a.style.margin_top) styles.push("margin-top:"+a.style.margin_top+"px"); if (a.margin_bottom) styles.push("margin-bottom:"+a.margin_bottom+"px");
if (a.style.margin_bottom) styles.push("margin-bottom:"+a.style.margin_bottom+"px");
}
// FIXME: via class logic? // FIXME: via class logic?
if (a.mime.match("vector")) { if (a.mime.match("vector")) {
@ -241,7 +233,7 @@ var SpacedeckBoardArtifacts = {
artifact_thumbnail_uri: function(a) { artifact_thumbnail_uri: function(a) {
if (a.payload_thumbnail_big_uri && a.board) { if (a.payload_thumbnail_big_uri && a.board) {
if (a.board.w>800) { if (a.w>800) {
return a.payload_thumbnail_big_uri; return a.payload_thumbnail_big_uri;
} }
} }
@ -255,35 +247,35 @@ var SpacedeckBoardArtifacts = {
var type = parts[0]; var type = parts[0];
var provider = parts[1]; var provider = parts[1];
if (!a.meta || !a.meta.link_uri) { if (!a.link_uri) {
console.log("missing meta / link_uri: ",a); console.log("missing meta / link_uri: ",a);
console.log("type/provider: ",type,provider); console.log("type/provider: ",type,provider);
return ("missing metadata: "+a._id); return ("missing metadata: "+a._id);
} }
if (provider=="youtube") { if (provider=="youtube") {
var vid = a.meta.link_uri.match(/(v=|\/)([a-zA-Z0-9\-_]{11})/); var vid = a.link_uri.match(/(v=|\/)([a-zA-Z0-9\-_]{11})/);
if (vid && vid.length>2) { if (vid && vid.length>2) {
var uri = "https://youtube.com/embed/"+vid[2]; var uri = "https://youtube.com/embed/"+vid[2];
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"?showinfo=0&rel=0&controls=0\"></iframe>"; return "<iframe frameborder=0 allowfullscreen src=\""+uri+"?showinfo=0&rel=0&controls=0\"></iframe>";
} else return "Can't resolve: "+a.payload_uri; } else return "Can't resolve: "+a.payload_uri;
} else if (provider=="dailymotion") { } else if (provider=="dailymotion") {
var match = a.meta.link_uri.match(/dailymotion.com\/video\/([^<]*)/); var match = a.link_uri.match(/dailymotion.com\/video\/([^<]*)/);
if (match && match.length>1) { if (match && match.length>1) {
var uri = "https://www.dailymotion.com/embed/video/"+match[1]; var uri = "https://www.dailymotion.com/embed/video/"+match[1];
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>"; return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
} else return "Can't resolve: "+a.payload_uri; } else return "Can't resolve: "+a.payload_uri;
} else if (provider=="vimeo") { } else if (provider=="vimeo") {
var match = a.meta.link_uri.match(/https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/); var match = a.link_uri.match(/https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/);
if (match) { if (match) {
var uri = "https://player.vimeo.com/video/"+match[2]; var uri = "https://player.vimeo.com/video/"+match[2];
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>"; return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
} else return "Can't resolve: "+a.payload_uri; } else return "Can't resolve: "+a.payload_uri;
} else if (provider=="soundcloud") { } else if (provider=="soundcloud") {
return '<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url='+a.meta.link_uri.replace(":", "%3A")+'"></iframe>'; return '<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url='+a.link_uri.replace(":", "%3A")+'"></iframe>';
} else if (provider=="spacedeck") { } else if (provider=="spacedeck") {
@ -299,8 +291,8 @@ var SpacedeckBoardArtifacts = {
if (mtype != "vector" && mtype != "shape") return ""; if (mtype != "vector" && mtype != "shape") return "";
var shape = a.style.shape || ""; var shape = a.shape || "";
var padding = 32 + a.style.stroke*2; var padding = 32 + a.stroke*2;
var path_svg; var path_svg;
var fill = ""; var fill = "";
@ -310,13 +302,13 @@ var SpacedeckBoardArtifacts = {
fill = "fill:none"; fill = "fill:none";
} else { } else {
path_svg = render_vector_shape(a, padding); path_svg = render_vector_shape(a, padding);
fill = "fill:"+a.style.fill_color+";"; fill = "fill:"+a.fill_color+";";
padding = 0; padding = 0;
} }
var margin = padding; var margin = padding;
var svg = "<svg xmlns='http://www.w3.org/2000/svg' width='"+(a.board.w+2*padding)+"' height='"+(a.board.h+2*padding)+"' "; var svg = "<svg xmlns='http://www.w3.org/2000/svg' width='"+(a.w+2*padding)+"' height='"+(a.h+2*padding)+"' ";
svg += "style='margin-left:"+(-margin)+"px;margin-top:"+(-margin)+"px;stroke-width:"+a.style.stroke+";stroke:"+a.style.stroke_color+";"+fill+"'>"; svg += "style='margin-left:"+(-margin)+"px;margin-top:"+(-margin)+"px;stroke-width:"+a.stroke+";stroke:"+a.stroke_color+";"+fill+"'>";
svg += path_svg; svg += path_svg;
svg += "</svg>"; svg += "</svg>";
@ -329,10 +321,10 @@ var SpacedeckBoardArtifacts = {
if (arts.length==0) return null; if (arts.length==0) return null;
r = { r = {
x1: parseInt(_.min(arts.map(function(a){return a.board.x}))), x1: parseInt(_.min(arts.map(function(a){return a.x}))),
y1: parseInt(_.min(arts.map(function(a){return a.board.y}))), y1: parseInt(_.min(arts.map(function(a){return a.y}))),
x2: parseInt(_.max(arts.map(function(a){return a.board.x+a.board.w}))), x2: parseInt(_.max(arts.map(function(a){return a.x+a.w}))),
y2: parseInt(_.max(arts.map(function(a){return a.board.y+a.board.h}))) y2: parseInt(_.max(arts.map(function(a){return a.y+a.h})))
}; };
r.x=r.x1; r.x=r.x1;
r.y=r.y1; r.y=r.y1;
@ -356,7 +348,7 @@ var SpacedeckBoardArtifacts = {
artifacts_in_rect: function(rect) { artifacts_in_rect: function(rect) {
return _.filter(this.active_space_artifacts, function(a) { return _.filter(this.active_space_artifacts, function(a) {
return this.rects_intersecting(a.board, rect); return this.rects_intersecting(a, rect);
}.bind(this)); }.bind(this));
}, },
@ -366,15 +358,15 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a)}.bind(this)); var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a)}.bind(this));
var max_z = _.max(overlapping,function(a){ return a.board.z; }); var max_z = _.max(overlapping,function(a){ return a.z; });
if (max_z.board) { if (max_z.board) {
max_z = max_z.board.z + 1; max_z = max_z.z + 1;
} else { } else {
max_z = 1; max_z = 1;
} }
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { z: max_z }) }; return { z: max_z };
}); });
}, },
@ -384,15 +376,15 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a);}.bind(this)); var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a);}.bind(this));
var min_z = _.min(overlapping,function(a){ return (a.board?a.board.z:0); }); var min_z = _.min(overlapping,function(a){ return a.z; });
if (min_z.board) { if (min_z.board) {
min_z = min_z.board.z - 1; min_z = min_z.z - 1;
} else { } else {
min_z = 0; min_z = 0;
} }
var my_z = _.max(this.selected_artifacts(),function(a){ (a.board?a.board.z:0); }); var my_z = _.max(this.selected_artifacts(),function(a){ return a.z; });
if (my_z.board) { if (my_z.board) {
my_z = my_z.board.z - 1; my_z = my_z.z - 1;
} else { } else {
my_z = 0; my_z = 0;
} }
@ -400,14 +392,14 @@ var SpacedeckBoardArtifacts = {
// TODO: move all other items up in this case? // TODO: move all other items up in this case?
if (min_z < 0) { if (min_z < 0) {
this.update_artifacts(overlapping, function(a) { this.update_artifacts(overlapping, function(a) {
return { board: _.extend(a.board, { z: (my_z + (a.board?a.board.z:0) + 1) }) }; return { z: (my_z + a.z + 1) };
}); });
return; return;
} }
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { z: min_z }) }; return { z: min_z };
}); });
}, },
@ -416,7 +408,7 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { x: rect.x1 }) }; return { x: rect.x1 };
}); });
}, },
@ -425,7 +417,7 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { y: rect.y1 }) }; return { y: rect.y1 };
}); });
}, },
@ -434,7 +426,7 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { x: rect.x2 - a.board.w }) }; return { x: rect.x2 - a.w };
}); });
}, },
@ -443,7 +435,7 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { y: rect.y2 - a.board.h }) }; return { y: rect.y2 - a.h };
}); });
}, },
@ -453,7 +445,7 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
var cx = rect.x1 + (rect.x2-rect.x1)/2; var cx = rect.x1 + (rect.x2-rect.x1)/2;
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { x: cx - a.board.w/2 }) }; return { x: cx - a.w/2 };
}); });
}, },
@ -463,7 +455,7 @@ var SpacedeckBoardArtifacts = {
var rect = this.artifact_selection_rect(); var rect = this.artifact_selection_rect();
var cy = rect.y1 + (rect.y2-rect.y1)/2; var cy = rect.y1 + (rect.y2-rect.y1)/2;
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { y: cy - a.board.h/2 }) }; return { y: cy - a.h/2 };
}); });
}, },
@ -473,11 +465,11 @@ var SpacedeckBoardArtifacts = {
var arts = this.selected_artifacts(); var arts = this.selected_artifacts();
if (arts.length<2) return; if (arts.length<2) return;
var totalw = _.reduce(arts, function(sum, a) { return sum + a.board.w }, 0); var totalw = _.reduce(arts, function(sum, a) { return sum + a.w }, 0);
var avgw = totalw / arts.length; var avgw = totalw / arts.length;
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { w: avgw }) }; return { w: avgw };
}); });
}, },
@ -487,11 +479,11 @@ var SpacedeckBoardArtifacts = {
var arts = this.selected_artifacts(); var arts = this.selected_artifacts();
if (arts.length<2) return; if (arts.length<2) return;
var totalh = _.reduce(arts, function(sum, a) { return sum + a.board.h }, 0); var totalh = _.reduce(arts, function(sum, a) { return sum + a.h }, 0);
var avgh = totalh / arts.length; var avgh = totalh / arts.length;
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { board: _.extend(a.board, { h: avgh }) }; return { h: avgh };
}); });
}, },
@ -506,16 +498,16 @@ var SpacedeckBoardArtifacts = {
var selected = this.selected_artifacts(); var selected = this.selected_artifacts();
if (selected.length<3) return; if (selected.length<3) return;
var sorted = _.sortBy(selected, function(a) { return a.board.x }); var sorted = _.sortBy(selected, function(a) { return a.x });
var startx = sorted[0].board.x + sorted[0].board.w/2; var startx = sorted[0].x + sorted[0].w/2;
var stopx = _.last(sorted).board.x + _.last(sorted).board.w/2; var stopx = _.last(sorted).x + _.last(sorted).w/2;
var step = (stopx-startx)/(sorted.length-1); var step = (stopx-startx)/(sorted.length-1);
for (var i=1; i<sorted.length-1; i++) { for (var i=1; i<sorted.length-1; i++) {
var a = sorted[i]; var a = sorted[i];
var x = startx + step*i - a.board.w/2; var x = startx + step*i - a.w/2;
this.update_artifacts([a],function(a) { this.update_artifacts([a],function(a) {
return { board: _.extend(a.board, {x: x}) } return { x: x }
}); });
} }
}, },
@ -526,16 +518,16 @@ var SpacedeckBoardArtifacts = {
var selected = this.selected_artifacts(); var selected = this.selected_artifacts();
if (selected.length<3) return; if (selected.length<3) return;
var sorted = _.sortBy(selected, function(a) { return a.board.y }); var sorted = _.sortBy(selected, function(a) { return a.y });
var starty = sorted[0].board.y + sorted[0].board.h/2; var starty = sorted[0].y + sorted[0].h/2;
var stopy = _.last(sorted).board.y + _.last(sorted).board.h/2; var stopy = _.last(sorted).y + _.last(sorted).h/2;
var step = (stopy-starty)/(sorted.length-1); var step = (stopy-starty)/(sorted.length-1);
for (var i=1; i<sorted.length-1; i++) { for (var i=1; i<sorted.length-1; i++) {
var a = sorted[i]; var a = sorted[i];
var y = starty + step*i - a.board.h/2; var y = starty + step*i - a.h/2;
this.update_artifacts([a],function(a) { this.update_artifacts([a],function(a) {
return { board: _.extend(a.board, {y: y}) } return { y: y }
}); });
} }
}, },
@ -546,21 +538,21 @@ var SpacedeckBoardArtifacts = {
var selected = this.selected_artifacts(); var selected = this.selected_artifacts();
if (selected.length<3) return; if (selected.length<3) return;
var sorted = _.sortBy(selected, function(a) { return a.board.x }); var sorted = _.sortBy(selected, function(a) { return a.x });
var startx = sorted[0].board.x; var startx = sorted[0].x;
var stopx = _.last(sorted).board.x + _.last(sorted).board.w; var stopx = _.last(sorted).x + _.last(sorted).w;
var range = stopx - startx; var range = stopx - startx;
var totalw = _.reduce(sorted, function(sum, a) { return sum + a.board.w }, 0); var totalw = _.reduce(sorted, function(sum, a) { return sum + a.w }, 0);
var avgs = (range - totalw) / (sorted.length-1); var avgs = (range - totalw) / (sorted.length-1);
var prevend = startx + sorted[0].board.w; var prevend = startx + sorted[0].w;
for (var i=1; i<sorted.length-1; i++) { for (var i=1; i<sorted.length-1; i++) {
var a = sorted[i]; var a = sorted[i];
var x = prevend + avgs; var x = prevend + avgs;
this.update_artifacts([a],function(a) { this.update_artifacts([a],function(a) {
return { board: _.extend(a.board, {x: x}) } return { x: x }
}); });
prevend = x+a.board.w; prevend = x+a.w;
} }
}, },
@ -570,21 +562,21 @@ var SpacedeckBoardArtifacts = {
var selected = this.selected_artifacts(); var selected = this.selected_artifacts();
if (selected.length<3) return; if (selected.length<3) return;
var sorted = _.sortBy(selected, function(a) { return a.board.y }); var sorted = _.sortBy(selected, function(a) { return a.y });
var starty = sorted[0].board.y; var starty = sorted[0].y;
var stopy = _.last(sorted).board.y + _.last(sorted).board.h; var stopy = _.last(sorted).y + _.last(sorted).h;
var range = stopy - starty; var range = stopy - starty;
var totalh = _.reduce(sorted, function(sum, a) { return sum + a.board.h }, 0); var totalh = _.reduce(sorted, function(sum, a) { return sum + a.h }, 0);
var avgs = (range - totalh) / (sorted.length-1); var avgs = (range - totalh) / (sorted.length-1);
var prevend = starty + sorted[0].board.h; var prevend = starty + sorted[0].h;
for (var i=1; i<sorted.length-1; i++) { for (var i=1; i<sorted.length-1; i++) {
var a = sorted[i]; var a = sorted[i];
var y = prevend + avgs; var y = prevend + avgs;
this.update_artifacts([a],function(a) { this.update_artifacts([a],function(a) {
return { board: _.extend(a.board, {y: y}) } return { y: y }
}); });
prevend = y+a.board.h; prevend = y+a.h;
} }
}, },
@ -594,20 +586,20 @@ var SpacedeckBoardArtifacts = {
var selected = this.selected_artifacts(); var selected = this.selected_artifacts();
if (selected.length<2) return; if (selected.length<2) return;
var sorted = _.sortBy(selected, function(a) { return a.board.x+a.board.y*this.active_space.advanced.width }.bind(this)); var sorted = _.sortBy(selected, function(a) { return a.x+a.y*this.active_space.advanced.width }.bind(this));
var minx = sorted[0].board.x; var minx = sorted[0].x;
var miny = sorted[0].board.y; var miny = sorted[0].y;
var sorted = _.sortBy(selected, function(a) { return -Math.max(a.board.w,a.board.h) }.bind(this)); var sorted = _.sortBy(selected, function(a) { return -Math.max(a.w,a.h) }.bind(this));
var blocks = []; var blocks = [];
for (var i=0; i<sorted.length; i++) { for (var i=0; i<sorted.length; i++) {
var a = sorted[i]; var a = sorted[i];
blocks.push({ blocks.push({
w: a.board.w, w: a.w,
h: a.board.h, h: a.h,
a: a a: a
}); });
} }
@ -620,10 +612,10 @@ var SpacedeckBoardArtifacts = {
if (block.fit) { if (block.fit) {
var a = block.a; var a = block.a;
this.update_artifacts([a],function(a) { this.update_artifacts([a],function(a) {
return { board: _.extend(a.board, { return {
x: minx+block.fit.x, x: minx+block.fit.x,
y: miny+block.fit.y y: miny+block.fit.y
}) } }
}); });
} }
} }

View File

@ -369,8 +369,8 @@ var SpacedeckSections = {
// canvas // canvas
this.$watch('active_style.background_color', function (value, mutation) { this.$watch('active_style.background_color', function (value, mutation) {
if (this.active_style.background_color != this.active_space.advanced.background_color) { if (this.active_style.background_color != this.active_space.background_color) {
this.$set("active_space.advanced.background_color",this.active_style.background_color); this.$set("active_space.background_color",this.active_style.background_color);
this.throttled_save_active_space(); this.throttled_save_active_space();
} }
@ -448,7 +448,7 @@ var SpacedeckSections = {
for (var i=0; i<props.length; i++) { for (var i=0; i<props.length; i++) {
var prop = props[i]; var prop = props[i];
this.active_style[prop]=a.style[prop]; this.active_style[prop]=a[prop];
} }
// defaults // defaults
@ -457,10 +457,10 @@ var SpacedeckSections = {
this.active_style.line_height = this.default_style.line_height; this.active_style.line_height = this.default_style.line_height;
this.active_style.letter_spacing = this.default_style.letter_spacing; this.active_style.letter_spacing = this.default_style.letter_spacing;
this.active_style.padding_top = a.style.padding_top || 0; this.active_style.padding_top = a.padding_top || 0;
this.active_style.padding_bottom = a.style.padding_bottom || 0; this.active_style.padding_bottom = a.padding_bottom || 0;
this.active_style.padding_left = a.style.padding_left || 0; this.active_style.padding_left = a.padding_left || 0;
this.active_style.padding_right = a.style.padding_right || 0; this.active_style.padding_right = a.padding_right || 0;
if (this.active_style.padding_top == this.active_style.padding_bottom) { if (this.active_style.padding_top == this.active_style.padding_bottom) {
this.active_style.padding_vert = this.active_style.padding_top; this.active_style.padding_vert = this.active_style.padding_top;
@ -476,10 +476,10 @@ var SpacedeckSections = {
this.active_style.padding = this.active_style.padding_top; this.active_style.padding = this.active_style.padding_top;
} }
this.active_style.margin_top = a.style.margin_top || 0; this.active_style.margin_top = a.margin_top || 0;
this.active_style.margin_bottom = a.style.margin_bottom || 0; this.active_style.margin_bottom = a.margin_bottom || 0;
this.active_style.margin_left = a.style.margin_left || 0; this.active_style.margin_left = a.margin_left || 0;
this.active_style.margin_right = a.style.margin_right || 0; this.active_style.margin_right = a.margin_right || 0;
if (this.active_style.margin_top == this.active_style.margin_bottom) { if (this.active_style.margin_top == this.active_style.margin_bottom) {
this.active_style.margin_vert = this.active_style.margin_top; this.active_style.margin_vert = this.active_style.margin_top;
@ -758,8 +758,8 @@ var SpacedeckSections = {
}, },
resize_minimap: function() { resize_minimap: function() {
if (!this.active_space || !this.active_space.advanced) return; if (!this.active_space) return;
this.minimap_scale = this.active_space.advanced.width/100.0; this.minimap_scale = this.active_space.width/100.0;
}, },
handle_minimap_mouseup: function(evt) { handle_minimap_mouseup: function(evt) {
@ -921,7 +921,7 @@ var SpacedeckSections = {
discover_zones: function() { discover_zones: function() {
this.zones = _.sortBy(_.filter(this.active_space_artifacts, function(a) { return (a.mime=="x-spacedeck/zone") }), this.zones = _.sortBy(_.filter(this.active_space_artifacts, function(a) { return (a.mime=="x-spacedeck/zone") }),
function(z){return z.style.order}); function(z){return z.order});
}, },
artifact_plaintext: function(a) { artifact_plaintext: function(a) {
@ -1015,10 +1015,10 @@ var SpacedeckSections = {
arts = _.filter(arts); // remove any nulls arts = _.filter(arts); // remove any nulls
return { return {
x1: parseInt(_.min(arts.map(function(a){return ((!a.board || !a.board.x)?0:a.board.x)}))), x1: parseInt(_.min(arts.map(function(a){return ((!a || !a.x)?0:a.x)}))),
y1: parseInt(_.min(arts.map(function(a){return ((!a.board || !a.board.y)?0:a.board.y)}))), y1: parseInt(_.min(arts.map(function(a){return ((!a || !a.y)?0:a.y)}))),
x2: parseInt(_.max(arts.map(function(a){return (!a.board?0:a.board.x+a.board.w)}))), x2: parseInt(_.max(arts.map(function(a){return (!a?0:a.x+a.w)}))),
y2: parseInt(_.max(arts.map(function(a){return (!a.board?0:a.board.y+a.board.h)}))) y2: parseInt(_.max(arts.map(function(a){return (!a?0:a.y+a.h)})))
}; };
}, },
@ -1076,7 +1076,7 @@ var SpacedeckSections = {
this.selection_metrics.count=arts.length; this.selection_metrics.count=arts.length;
this.selection_metrics.scribble_selection = false; this.selection_metrics.scribble_selection = false;
if (arts.length == 1 && arts[0].mime == "x-spacedeck/vector") { if (arts.length == 1 && arts[0].mime == "x-spacedeck/vector") {
if (arts[0].style.shape == "scribble") { if (arts[0].shape == "scribble") {
this.selection_metrics.scribble_selection = true; this.selection_metrics.scribble_selection = true;
} }
this.selection_metrics.vector_points = arts[0].control_points; this.selection_metrics.vector_points = arts[0].control_points;
@ -1112,8 +1112,8 @@ var SpacedeckSections = {
fixup_space_size: function() { fixup_space_size: function() {
if (!this.active_space) return; if (!this.active_space) return;
this.active_space.advanced.width =Math.max(this.active_space.advanced.width, window.innerWidth); this.active_space.width =Math.max(this.active_space.width, window.innerWidth);
this.active_space.advanced.height=Math.max(this.active_space.advanced.height, window.innerHeight); this.active_space.height=Math.max(this.active_space.height, window.innerHeight);
}, },
end_transaction: function() { end_transaction: function() {
@ -1125,13 +1125,13 @@ 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;
this.active_space.advanced.width =Math.max(er.x2+100, window.innerWidth); this.active_space.width =Math.max(er.x2+100, window.innerWidth);
this.active_space.advanced.height=Math.max(er.y2+100, window.innerHeight); this.active_space.height=Math.max(er.y2+100, window.innerHeight);
if (this._last_bounds_width != this.active_space.advanced.width || if (this._last_bounds_width != this.active_space.width ||
this._last_bounds_height != this.active_space.advanced.height) { this._last_bounds_height != this.active_space.height) {
this._last_bounds_width = this.active_space.advanced.width; this._last_bounds_width = this.active_space.width;
this._last_bounds_height = this.active_space.advanced.height; this._last_bounds_height = this.active_space.height;
save_space(this.active_space); save_space(this.active_space);
} }
@ -1214,7 +1214,7 @@ var SpacedeckSections = {
// this is a bit hacky, but might be the smartest place to do it // this is a bit hacky, but might be the smartest place to do it
if (a.view && a.view.vector_svg) { if (a.view && a.view.vector_svg) {
a.style.shape_svg = a.view.vector_svg; a.shape_svg = a.view.vector_svg;
} }
window.artifact_save_queue[a._id] = a; window.artifact_save_queue[a._id] = a;
@ -1329,7 +1329,7 @@ var SpacedeckSections = {
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
var c = {}; var c = {};
if (c[prop] != val) { if (a[prop] != val) {
//console.log("set_artifact_prop: ",c,val); //console.log("set_artifact_prop: ",c,val);
c[prop]=val; c[prop]=val;
return c; return c;
@ -1343,11 +1343,11 @@ var SpacedeckSections = {
this.begin_transaction(); this.begin_transaction();
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
var c = {style: a.style||{}}; var c = {};
if (c.style[prop] != val) { if (a[prop] != val) {
//console.log("set_artifact_style_prop: ",c,val); //console.log("set_artifact_style_prop: ",c,val);
c.style[prop]=val; c[prop]=val;
return c; return c;
} }
@ -1419,7 +1419,7 @@ var SpacedeckSections = {
if (this.selected_artifacts().length!=1 && this.opened_dialog!="background") return; if (this.selected_artifacts().length!=1 && this.opened_dialog!="background") return;
if (this.opened_dialog=="background") { if (this.opened_dialog=="background") {
this.active_style[this.color_picker_target] = this.active_space.advanced.background_color; this.active_style[this.color_picker_target] = this.active_space.background_color;
} else { } else {
if (!this.active_style[this.color_picker_target]) { if (!this.active_style[this.color_picker_target]) {
this.active_style[this.color_picker_target] = this.default_style[this.color_picker_target]; this.active_style[this.color_picker_target] = this.default_style[this.color_picker_target];
@ -1478,10 +1478,8 @@ var SpacedeckSections = {
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
return { return {
board: _.extend(a.board, { x: a.x+dx,
x: a.board.x+dx, y: a.y+dy
y: a.board.y+dy
})
}; };
}); });
}, },
@ -1489,7 +1487,7 @@ var SpacedeckSections = {
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
highest_z: function() { highest_z: function() {
var z = _.max(this.active_space_artifacts.map(function(a){return a.board.z||0})); var z = _.max(this.active_space_artifacts.map(function(a){return a.z||0}));
if (z<0) z=0; if (z<0) z=0;
if (z>999) z=999; if (z>999) z=999;
return z; return z;
@ -1574,20 +1572,18 @@ var SpacedeckSections = {
payload_thumbnail_web_uri: url || null, payload_thumbnail_web_uri: url || null,
space_id: space._id, space_id: space._id,
style: {
order: this.active_space_artifacts.length+1, order: this.active_space_artifacts.length+1,
valign: "middle", valign: "middle",
align: "center" align: "center"
//fill_color: "#f8f8f8" //fill_color: "#f8f8f8"
}
}; };
if (mimes[item_type] == "text/html") { if (mimes[item_type] == "text/html") {
new_item.style.padding_left = 10; new_item.padding_left = 10;
new_item.style.padding_top = 10; new_item.padding_top = 10;
new_item.style.padding_right = 10; new_item.padding_right = 10;
new_item.style.padding_bottom = 10; new_item.padding_bottom = 10;
new_item.style.fill_color = "rgba(255,255,255,1)"; new_item.fill_color = "rgba(255,255,255,1)";
new_item.description = "<p>Text</p>"; new_item.description = "<p>Text</p>";
} }
@ -1600,13 +1596,11 @@ var SpacedeckSections = {
z = point.z; z = point.z;
} }
new_item.board = { new_item.x = parseInt(point.x);
x: parseInt(point.x), new_item.y = parseInt(point.y);
y: parseInt(point.y), new_item.z = z;
w: w, new_item.w = w;
h: h, new_item.h = h;
z: z
};
if (this.guest_nickname) { if (this.guest_nickname) {
new_item.editor_name = this.guest_nickname; new_item.editor_name = this.guest_nickname;
@ -1665,7 +1659,7 @@ var SpacedeckSections = {
for (var i=0; i<new_zones.length; i++) { for (var i=0; i<new_zones.length; i++) {
if (new_zones[i]) { if (new_zones[i]) {
if (!new_zones[i].style) new_zones[i].style = {}; if (!new_zones[i].style) new_zones[i].style = {};
new_zones[i].style.order = i; new_zones[i].order = i;
save_artifact(new_zones[i]); save_artifact(new_zones[i]);
} }
} }
@ -1679,7 +1673,7 @@ var SpacedeckSections = {
for (var i=0; i<new_zones.length; i++) { for (var i=0; i<new_zones.length; i++) {
if (new_zones[i]) { if (new_zones[i]) {
if (!new_zones[i].style) new_zones[i].style = {}; if (!new_zones[i].style) new_zones[i].style = {};
new_zones[i].style.order = i; new_zones[i].order = i;
save_artifact(new_zones[i]); save_artifact(new_zones[i]);
} }
} }
@ -1695,17 +1689,13 @@ var SpacedeckSections = {
space_id: this.active_space._id, space_id: this.active_space._id,
mime: "x-spacedeck/zone", mime: "x-spacedeck/zone",
description: "Zone "+(this.zones.length+1), description: "Zone "+(this.zones.length+1),
board: {
x: point.x, x: point.x,
y: point.y, y: point.y,
w: w, w: w,
h: h, h: h,
z: 0 z: 0,
},
style: {
valign: "middle", valign: "middle",
align: "center" align: "center"
}
}; };
if (this.guest_nickname) { if (this.guest_nickname) {
@ -1735,14 +1725,11 @@ var SpacedeckSections = {
space_id: this.active_space._id, space_id: this.active_space._id,
mime: "x-spacedeck/shape", mime: "x-spacedeck/shape",
description: "Text", description: "Text",
board: {
x: point.x, x: point.x,
y: point.y, y: point.y,
z: point.z, z: point.z,
w: w, w: w,
h: h h: h,
},
style: {
stroke_color: "#ffffff", stroke_color: "#ffffff",
text_color: "#ffffff", text_color: "#ffffff",
stroke: 0, stroke: 0,
@ -1750,7 +1737,6 @@ var SpacedeckSections = {
shape: shape_type, shape: shape_type,
valign: "middle", valign: "middle",
align: "center" align: "center"
}
}; };
if (this.guest_nickname) { if (this.guest_nickname) {
@ -1829,18 +1815,14 @@ var SpacedeckSections = {
state: "uploading", state: "uploading",
payload_thumbnail_medium_uri: null, payload_thumbnail_medium_uri: null,
payload_thumbnail_web_uri: null, payload_thumbnail_web_uri: null,
board: {
x: point.x, x: point.x,
y: point.y, y: point.y,
w: w, w: w,
h: h, h: h,
z: point.z z: point.z,
},
style: {
order: this.active_space_artifacts.length+1, order: this.active_space_artifacts.length+1,
fill_color: fill fill_color: fill
} }
}
this.update_board_artifact_viewmodel(a); this.update_board_artifact_viewmodel(a);
@ -1864,7 +1846,11 @@ var SpacedeckSections = {
a.payload_thumbnail_big_uri = updated_a.payload_thumbnail_big_uri; a.payload_thumbnail_big_uri = updated_a.payload_thumbnail_big_uri;
a.payload_alternatives = updated_a.payload_alternatives; a.payload_alternatives = updated_a.payload_alternatives;
a.mime = updated_a.mime; a.mime = updated_a.mime;
a.board = updated_a.board; a.x = updated_a.x;
a.y = updated_a.y;
a.w = updated_a.w;
a.h = updated_a.h;
a.z = updated_a.z;
a.state = updated_a.state; a.state = updated_a.state;
this.update_board_artifact_viewmodel(a); this.update_board_artifact_viewmodel(a);
@ -2002,26 +1988,26 @@ var SpacedeckSections = {
clear_formatting_walk: function(el,cmd,arg1,arg2) { clear_formatting_walk: function(el,cmd,arg1,arg2) {
if (el && el.style) { if (el && el.style) {
if (cmd == "preciseFontSize") { if (cmd == "preciseFontSize") {
el.style.fontSize = null; el.fontSize = null;
} else if (cmd == "letterSpacing") { } else if (cmd == "letterSpacing") {
el.style.letterSpacing = null; el.letterSpacing = null;
} else if (cmd == "lineHeight") { } else if (cmd == "lineHeight") {
el.style.lineHeight = null; el.lineHeight = null;
} else if (cmd == "fontName") { } else if (cmd == "fontName") {
el.style.fontFamily = null; el.fontFamily = null;
} else if (cmd == "fontWeight") { } else if (cmd == "fontWeight") {
el.style.fontWeight = null; el.fontWeight = null;
el.style.fontStyle = null; el.fontStyle = null;
} else if (cmd == "bold") { } else if (cmd == "bold") {
el.style.fontWeight = null; el.fontWeight = null;
} else if (cmd == "italic") { } else if (cmd == "italic") {
el.style.fontStyle = null; el.fontStyle = null;
} else if (cmd == "underline") { } else if (cmd == "underline") {
el.style.textDecoration = null; el.textDecoration = null;
} else if (cmd == "strikeThrough") { } else if (cmd == "strikeThrough") {
el.style.textDecoration = null; el.textDecoration = null;
} else if (cmd == "forecolor") { } else if (cmd == "forecolor") {
el.style.color = null; el.color = null;
} }
} }
@ -2108,6 +2094,9 @@ var SpacedeckSections = {
if (a.description!=dom.innerHTML) { if (a.description!=dom.innerHTML) {
a.description = dom.innerHTML; a.description = dom.innerHTML;
console.log("new DOM:",dom.innerHTML);
this.update_board_artifact_viewmodel(a); this.update_board_artifact_viewmodel(a);
this.queue_artifact_for_save(a); this.queue_artifact_for_save(a);
@ -2141,10 +2130,7 @@ var SpacedeckSections = {
remove_link_from_selected_artifacts: function() { remove_link_from_selected_artifacts: function() {
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
var meta = a.meta || {}; return {link_uri: ""};
delete meta.link_uri;
return {meta: meta};
}); });
}, },
@ -2160,9 +2146,7 @@ var SpacedeckSections = {
var insert_link_url = prompt("URL:",def); var insert_link_url = prompt("URL:",def);
this.update_selected_artifacts(function(a) { this.update_selected_artifacts(function(a) {
var meta = a.meta || {}; var update = {link_uri: insert_link_url};
meta.link_uri = insert_link_url;
var update = {meta: meta};
if (a.payload_uri && a.payload_uri.match("webgrabber")) { if (a.payload_uri && a.payload_uri.match("webgrabber")) {
var enc_uri = encodeURIComponent(btoa(insert_link_url)); var enc_uri = encodeURIComponent(btoa(insert_link_url));
@ -2185,11 +2169,10 @@ var SpacedeckSections = {
delete copy["$index"]; delete copy["$index"];
delete copy["_id"]; delete copy["_id"];
if (dx) copy.board.x += dx; if (dx) copy.x += dx;
if (dy) copy.board.y += dy; if (dy) copy.y += dy;
if (!copy.style) copy.style = {}; copy.order = this.active_space_artifacts.length+1;
copy.style.order = this.active_space_artifacts.length+1;
if (this.guest_nickname) { if (this.guest_nickname) {
copy.editor_name = this.guest_nickname; copy.editor_name = this.guest_nickname;
@ -2334,16 +2317,16 @@ var SpacedeckSections = {
if (parsed[i].mime) { if (parsed[i].mime) {
var z = this.highest_z()+1; var z = this.highest_z()+1;
if (parsed.length==1) { if (parsed.length==1) {
var w = parsed[i].board.w; var w = parsed[i].w;
var h = parsed[i].board.h; var h = parsed[i].h;
var point = this.find_place_for_item(w,h); var point = this.find_place_for_item(w,h);
parsed[i].board.x = point.x; parsed[i].x = point.x;
parsed[i].board.y = point.y; parsed[i].y = point.y;
parsed[i].board.z = point.z; parsed[i].z = point.z;
} else { } else {
parsed[i].board.x = parsed[i].board.x+50; parsed[i].x = parsed[i].x+50;
parsed[i].board.y = parsed[i].board.y+50; parsed[i].y = parsed[i].y+50;
parsed[i].board.y = parsed[i].board.z+z; parsed[i].y = parsed[i].z+z;
} }
this.clone_artifact(parsed[i], 0,0, function(a) { this.clone_artifact(parsed[i], 0,0, function(a) {
this.multi_select([a]); this.multi_select([a]);
@ -2373,13 +2356,11 @@ var SpacedeckSections = {
var h = 300; var h = 300;
var point = this.find_place_for_item(w,h); var point = this.find_place_for_item(w,h);
new_item.board = { new_item.x = point.x;
x: point.x, new_item.y = point.y;
y: point.y, new_item.w = w;
w: w, new_item.h = h;
h: h, new_item.z = point.z;
z: point.z
};
if (this.guest_nickname) { if (this.guest_nickname) {
new_item.editor_name = this.guest_nickname; new_item.editor_name = this.guest_nickname;
@ -2402,17 +2383,13 @@ var SpacedeckSections = {
mime: "image/png", mime: "image/png",
description: url, description: url,
state: "uploading", state: "uploading",
board: {
x: point.x, x: point.x,
y: point.y, y: point.y,
w: 200, w: 200,
h: 200, h: 200,
z: z z: z,
},
style: {
order: this.active_space_artifacts.length order: this.active_space_artifacts.length
} }
}
var metadata = parse_link(url) var metadata = parse_link(url)
@ -2473,16 +2450,12 @@ var SpacedeckSections = {
payload_thumbnail_medium_uri: metadata.thumbnail_url, payload_thumbnail_medium_uri: metadata.thumbnail_url,
payload_thumbnail_web_uri: metadata.thumbnail_url, payload_thumbnail_web_uri: metadata.thumbnail_url,
state: "idle", state: "idle",
meta: {
title: metadata.title, title: metadata.title,
link_uri: metadata.url || url link_uri: metadata.url || url,
},
board: {
x: point.x - w/2, x: point.x - w/2,
y: point.y - h/2, y: point.y - h/2,
w: w, w: w,
h: h h: h
}
}); });
if (this.guest_nickname) { if (this.guest_nickname) {
@ -2591,7 +2564,7 @@ var SpacedeckSections = {
}, },
remove_section_background: function() { remove_section_background: function() {
this.active_space.advanced.background_uri = null; this.active_space.background_uri = null;
save_space(this.active_space); save_space(this.active_space);
}, },
@ -2652,8 +2625,8 @@ var SpacedeckSections = {
this.bounds_zoom = this.viewport_zoom; this.bounds_zoom = this.viewport_zoom;
var eff_w = this.active_space.advanced.width*this.viewport_zoom; var eff_w = this.active_space.width*this.viewport_zoom;
var eff_h = this.active_space.advanced.height*this.viewport_zoom; var eff_h = this.active_space.height*this.viewport_zoom;
if (window.innerWidth>eff_w) { if (window.innerWidth>eff_w) {
// horizontal centering // horizontal centering
@ -2846,8 +2819,8 @@ var SpacedeckSections = {
var el = $("#space")[0]; var el = $("#space")[0];
var eff_w = this.active_space.advanced.width*this.viewport_zoom; var eff_w = this.active_space.width*this.viewport_zoom;
var eff_h = this.active_space.advanced.height*this.viewport_zoom; var eff_h = this.active_space.height*this.viewport_zoom;
var sx = el.scrollLeft; var sx = el.scrollLeft;
var sy = el.scrollTop; var sy = el.scrollTop;
@ -2980,9 +2953,9 @@ var SpacedeckSections = {
var w = 300; var w = 300;
var h = 200; var h = 200;
if (parsed.board && parsed.board.w && parsed.board.h) { if (parsed.board && parsed.w && parsed.h) {
w = parsed.board.w; w = parsed.w;
h = parsed.board.h; h = parsed.h;
} }
var point = this.cursor_point_to_space(evt); var point = this.cursor_point_to_space(evt);

View File

@ -283,9 +283,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();

View File

@ -331,7 +331,7 @@ function setup_whiteboard_directives() {
var $scope = this.vm.$root; var $scope = this.vm.$root;
return _.filter($scope.active_space_artifacts, function(a) { return _.filter($scope.active_space_artifacts, function(a) {
return this.rects_intersecting(a.board, rect); return this.rects_intersecting(a, rect);
}.bind(this)); }.bind(this));
}, },
@ -439,15 +439,15 @@ function setup_whiteboard_directives() {
dists = $scope.unselected_artifacts().map(function(a){ dists = $scope.unselected_artifacts().map(function(a){
var r = this.rect_to_points(a.board); var r = this.rect_to_points(a);
var xd1 = Math.abs(r[0].x-x); var xd1 = Math.abs(r[0].x-x);
var xd2 = Math.abs(r[1].x-x); var xd2 = Math.abs(r[1].x-x);
var xd3 = Math.abs(r[0].x+a.board.w/2 - x); var xd3 = Math.abs(r[0].x+a.w/2 - x);
var yd1 = Math.abs(r[0].y-y); var yd1 = Math.abs(r[0].y-y);
var yd2 = Math.abs(r[2].y-y); var yd2 = Math.abs(r[2].y-y);
var yd3 = Math.abs(r[0].y+a.board.h/2 - y); var yd3 = Math.abs(r[0].y+a.h/2 - y);
if (!snap_middle) { if (!snap_middle) {
if (xd2<xd1) { if (xd2<xd1) {
@ -469,10 +469,10 @@ function setup_whiteboard_directives() {
if (snap_middle) { if (snap_middle) {
var xd = xd3; var xd = xd3;
var sx = r[0].x+a.board.w/2; var sx = r[0].x+a.w/2;
var yd = yd3; var yd = yd3;
var sy = r[0].y+a.board.h/2; var sy = r[0].y+a.h/2;
} }
return [[xd,sx],[yd,sy]]; return [[xd,sx],[yd,sy]];
@ -531,18 +531,14 @@ function setup_whiteboard_directives() {
mime: "x-spacedeck/vector", mime: "x-spacedeck/vector",
description: "", description: "",
control_points: [{dx:0,dy:0}], control_points: [{dx:0,dy:0}],
board: {
x: point.x, x: point.x,
y: point.y, y: point.y,
z: z, z: z,
w: 64, w: 64,
h: 64 h: 64,
},
style: {
stroke_color: "#000000", stroke_color: "#000000",
stroke: 2, stroke: 2,
shape: "scribble" shape: "scribble"
}
}; };
$scope.save_artifact(a, function(saved_a) { $scope.save_artifact(a, function(saved_a) {
@ -572,18 +568,14 @@ function setup_whiteboard_directives() {
mime: "x-spacedeck/vector", mime: "x-spacedeck/vector",
description: "", description: "",
control_points: [{dx:0,dy:0},{dx:0,dy:0},{dx:0,dy:0}], control_points: [{dx:0,dy:0},{dx:0,dy:0},{dx:0,dy:0}],
board: {
x: point.x, x: point.x,
y: point.y, y: point.y,
z: z, z: z,
w: 64, w: 64,
h: 64 h: 64,
},
style: {
stroke_color: "#000000", stroke_color: "#000000",
stroke: 2, stroke: 2,
shape: "arrow" shape: "arrow"
}
}; };
$scope.save_artifact(a, function(saved_a) { $scope.save_artifact(a, function(saved_a) {
@ -612,18 +604,14 @@ function setup_whiteboard_directives() {
mime: "x-spacedeck/vector", mime: "x-spacedeck/vector",
description: "", description: "",
control_points: [{dx:0,dy:0},{dx:0,dy:0}], control_points: [{dx:0,dy:0},{dx:0,dy:0}],
board: {
x: point.x, x: point.x,
y: point.y, y: point.y,
z: z, z: z,
w: 64, w: 64,
h: 64 h: 64,
},
style: {
stroke_color: "#000000", stroke_color: "#000000",
stroke: 2, stroke: 2,
shape: "line" shape: "line"
}
}; };
$scope.save_artifact(a, function(saved_a) { $scope.save_artifact(a, function(saved_a) {
@ -675,11 +663,11 @@ function setup_whiteboard_directives() {
if (_.include(["text","placeholder"],$scope.artifact_major_type(ars[i]))) { if (_.include(["text","placeholder"],$scope.artifact_major_type(ars[i]))) {
// some types of artifact need a minimum size // some types of artifact need a minimum size
if (ars[i].board.w<10) { if (ars[i].w<10) {
ars[i].board.w = 10; ars[i].w = 10;
} }
if (ars[i].board.h<10) { if (ars[i].h<10) {
ars[i].board.h = 10; ars[i].h = 10;
} }
} }
@ -827,10 +815,8 @@ function setup_whiteboard_directives() {
if (old_a) { if (old_a) {
return { return {
board: _.extend(a.board, { x: old_a.x + dx - snap_dx,
x: old_a.board.x + dx - snap_dx, y: old_a.y + dy - snap_dy
y: old_a.board.y + dy - snap_dy
})
}; };
} else { } else {
// deleted? // deleted?
@ -865,26 +851,24 @@ 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);
var x1 = origin_x + ((old_a.board.x - origin_x) * scale_x); var x1 = origin_x + ((old_a.x - origin_x) * scale_x);
var y1 = origin_y + ((old_a.board.y - origin_y) * scale_y); var y1 = origin_y + ((old_a.y - origin_y) * scale_y);
var x2 = origin_x + (((old_a.board.x + old_a.board.w) - origin_x) * scale_x); var x2 = origin_x + (((old_a.x + old_a.w) - origin_x) * scale_x);
var y2 = origin_y + (((old_a.board.y + old_a.board.h) - origin_y) * scale_y); var y2 = origin_y + (((old_a.y + old_a.h) - origin_y) * scale_y);
if (x1>x2) { var t = x1; x1 = x2; x2 = t; } if (x1>x2) { var t = x1; x1 = x2; x2 = t; }
if (y1>y2) { var t = y1; y1 = y2; y2 = t; } if (y1>y2) { var t = y1; y1 = y2; y2 = t; }
return { return {
board: _.extend(a.board, {
x: x1, x: x1,
y: y1, y: y1,
w: x2 - x1, w: x2 - x1,
h: y2 - y1 h: y2 - y1
})
}; };
}.bind(this)); }.bind(this));
@ -902,18 +886,17 @@ function setup_whiteboard_directives() {
var old_a = $scope.find_artifact_before_transaction(a); var old_a = $scope.find_artifact_before_transaction(a);
var control_points = _.cloneDeep(old_a.control_points); var control_points = _.cloneDeep(old_a.control_points);
var board = _.clone(old_a.board);
var cp = control_points[$scope.selected_control_point_idx]; var cp = control_points[$scope.selected_control_point_idx];
var snapped = _this.snap_point(board.x+cp.dx+dx, board.y+cp.dy+dy); var snapped = _this.snap_point(old_a.x+cp.dx+dx, old_a.y+cp.dy+dy);
dx = snapped.snapx[1]-(board.x+cp.dx); dx = snapped.snapx[1]-(old_a.x+cp.dx);
dy = snapped.snapy[1]-(board.y+cp.dy); dy = snapped.snapy[1]-(old_a.y+cp.dy);
cp.dx += dx; cp.dx += dx;
cp.dy += dy; cp.dy += dy;
// special case for arrow's 3rd point // special case for arrow's 3rd point
if (a.style.shape == "arrow" && $scope.selected_control_point_idx!=2) { if (a.shape == "arrow" && $scope.selected_control_point_idx!=2) {
/*control_points[2].dx += dx/2; /*control_points[2].dx += dx/2;
control_points[2].dy += dy/2; */ control_points[2].dy += dy/2; */
@ -921,7 +904,7 @@ function setup_whiteboard_directives() {
control_points[2].dy = (control_points[0].dy+control_points[1].dy)/2; control_points[2].dy = (control_points[0].dy+control_points[1].dy)/2;
} }
return _this.normalize_control_points(control_points, board); return _this.normalize_control_points(control_points, old_a);
}); });
} else if (this.mouse_state == "scribble") { } else if (this.mouse_state == "scribble") {
@ -930,16 +913,14 @@ function setup_whiteboard_directives() {
var old_a = a; var old_a = a;
var control_points = _.cloneDeep(old_a.control_points); var control_points = _.cloneDeep(old_a.control_points);
var board = _.clone(old_a.board);
var offset = this.offset_point_in_wrapper({x:cursor.x,y:cursor.y}); var offset = this.offset_point_in_wrapper({x:cursor.x,y:cursor.y});
control_points.push({ control_points.push({
dx: offset.x-board.x, dx: offset.x-old_a.x,
dy: offset.y-board.y dy: offset.y-old_a.y
}); });
return this.normalize_control_points(simplify_scribble_points(control_points), board); return this.normalize_control_points(simplify_scribble_points(control_points), old_a);
}.bind(this)); }.bind(this));
var arts = $scope.selected_artifacts(); var arts = $scope.selected_artifacts();
@ -959,7 +940,7 @@ function setup_whiteboard_directives() {
} }
}, },
normalize_control_points: function(control_points, board) { normalize_control_points: function(control_points, artifact) {
var x1 = _.min(control_points,"dx").dx; var x1 = _.min(control_points,"dx").dx;
var y1 = _.min(control_points,"dy").dy; var y1 = _.min(control_points,"dy").dy;
var x2 = _.max(control_points,"dx").dx; var x2 = _.max(control_points,"dx").dx;
@ -981,19 +962,15 @@ function setup_whiteboard_directives() {
var bshiftx = 0; var bshiftx = 0;
var bshifty = 0; var bshifty = 0;
if (board.w < 0) bshiftx = -board.w; if (artifact.w < 0) bshiftx = -artifact.w;
if (board.h < 0) bshifty = -board.h; if (artifact.h < 0) bshifty = -artifact.h;
var shifted_board = {
x: board.x + bshiftx - shiftx,
y: board.y + bshifty - shifty,
w: w,
h: h,
z: board.z
};
return { return {
board: shifted_board, x: artifact.x + bshiftx - shiftx,
y: artifact.y + bshifty - shifty,
w: w,
h: h,
z: artifact.z,
control_points: shifted_cps control_points: shifted_cps
}; };
} }

View File

@ -21,7 +21,7 @@ function vec2_angle(v) {
} }
function render_vector_drawing(a, padding) { function render_vector_drawing(a, padding) {
var shape = a.style.shape || ""; var shape = a.shape || "";
var path = []; var path = [];
var p = a.control_points[0]; var p = a.control_points[0];
@ -48,8 +48,8 @@ function render_vector_drawing(a, padding) {
var d = "M" + (cps.dx + padding) + "," + (cps.dy + padding) + " Q" + (scaledMiddlePoint.dx + padding) + "," + (scaledMiddlePoint.dy + padding) + " " + (cpe.dx + padding) + "," + (cpe.dy + padding); var d = "M" + (cps.dx + padding) + "," + (cps.dy + padding) + " Q" + (scaledMiddlePoint.dx + padding) + "," + (scaledMiddlePoint.dy + padding) + " " + (cpe.dx + padding) + "," + (cpe.dy + padding);
var tip = "<defs><marker id='ae" + markerId + "' refX=\"0.1\" refY=\"3\" markerWidth=\"3\" markerHeight=\"6\" orient=\"auto\">"; var tip = "<defs><marker id='ae" + markerId + "' refX=\"0.1\" refY=\"3\" markerWidth=\"3\" markerHeight=\"6\" orient=\"auto\">";
tip += "<path d=\"M-3,0 V6 L3,3 Z\" fill=\""+a.style.stroke_color+"\" stroke-width=\"0\"/></marker></defs>"; tip += "<path d=\"M-3,0 V6 L3,3 Z\" fill=\""+a.stroke_color+"\" stroke-width=\"0\"/></marker></defs>";
var svg = tip + "<path d='" + d + "' style='stroke-width:" + a.style.stroke + ";' marker-end='url(#ae" + markerId + ")'/>"; var svg = tip + "<path d='" + d + "' style='stroke-width:" + a.stroke + ";' marker-end='url(#ae" + markerId + ")'/>";
return svg; return svg;
} }
@ -237,11 +237,11 @@ function render_vector_rect(xradius,yradius,offset) {
} }
function render_vector_shape(a) { function render_vector_shape(a) {
var stroke = parseInt(a.style.stroke) + 4; var stroke = parseInt(a.stroke) + 4;
var offset = stroke / 2; var offset = stroke / 2;
var xr = (a.board.w-stroke) / 2; var xr = (a.w-stroke) / 2;
var yr = (a.board.h-stroke) / 2; var yr = (a.h-stroke) / 2;
var shape_renderers = { var shape_renderers = {
ellipse: function() { return render_vector_ellipse(xr, yr, offset); }, ellipse: function() { return render_vector_ellipse(xr, yr, offset); },
@ -258,7 +258,7 @@ function render_vector_shape(a) {
cloud: function() { return render_vector_cloud(xr, yr, offset); }, cloud: function() { return render_vector_cloud(xr, yr, offset); },
} }
var render_func = shape_renderers[a.style.shape]; var render_func = shape_renderers[a.shape];
if (!render_func) return ""; if (!render_func) return "";

View File

@ -1,20 +1,16 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); require('../../models/db');
var fs = require('fs'); var fs = require('fs');
var _ = require("underscore"); var _ = require("underscore");
var mongoose = require("mongoose");
var async = require('async'); var async = require('async');
var archiver = require('archiver');
var request = require('request'); var request = require('request');
var url = require("url"); var url = require("url");
var path = require("path"); var path = require("path");
var crypto = require('crypto'); var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob'); var glob = require('glob');
var gm = require('gm');
var express = require('express'); var express = require('express');
var router = express.Router(); var router = express.Router();

View File

@ -1,10 +1,10 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); const db = require('../../models/db');
var bcrypt = require('bcryptjs'); var bcrypt = require('bcryptjs');
var crypo = require('crypto'); var crypto = require('crypto');
var URL = require('url').URL; var URL = require('url').URL;
var express = require('express'); var express = require('express');
@ -12,68 +12,64 @@ var router = express.Router();
router.post('/', function(req, res) { router.post('/', function(req, res) {
var data = req.body; var data = req.body;
if (data.email && data.password) { if (!data.email || !data.password) {
res.status(400).json({});
return;
}
var email = req.body.email.toLowerCase(); var email = req.body.email.toLowerCase();
var password = req.body["password"]; var password = req.body["password"];
User.find({email: email, account_type: "email"}, (function (err, users) { db.User.findOne({where: {email: email}})
if (err) { .error(err => {
res.status(400).json({"error":"session.users"}); res.sendStatus(404);
} else { //res.status(400).json({"error":"session.users"});
})
if (users.length == 1) { .then(user => {
var user = users[0]; console.log("!!! user: ",user.password_hash);
if (bcrypt.compareSync(password, user.password_hash)) { if (bcrypt.compareSync(password, user.password_hash)) {
crypo.randomBytes(48, function(ex, buf) { crypto.randomBytes(48, function(ex, buf) {
var token = buf.toString('hex'); var token = buf.toString('hex');
console.log("!!! token: ",token);
var session = { var session = {
user_id: user._id,
token: token, token: token,
ip: req.ip, ip: req.ip,
device: "web", device: "web",
created_at: new Date() created_at: new Date()
}; };
if (!user.sessions) db.Session.create(session)
user.sessions = []; .error(err => {
console.error("Error creating Session:",err);
user.sessions.push(session); res.sendStatus(500);
})
user.save(function(err, result) { .then(() => {
if (err) console.error("Error saving user:",err);
var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : "localhost"; var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : "localhost";
res.cookie('sdsession', token, { domain: domain, httpOnly: true }); res.cookie('sdsession', token, { domain: domain, httpOnly: true });
res.status(201).json(session); res.status(201).json(session);
}); });
}); });
}else{ } else {
res.sendStatus(403); res.sendStatus(403);
} }
} else { });
res.sendStatus(404);
}
}
}));
} else {
res.status(400).json({});
}
}); });
router.delete('/current', function(req, res, next) { router.delete('/current', function(req, res, next) {
if (req.user) { if (req.user) {
var user = req.user; /*var user = req.user;
var newSessions = user.sessions.filter( function(session){ var newSessions = user.sessions.filter( function(session){
return session.token != req.token; return session.token != req.token;
}); });*/
user.sessions = newSessions; //user.sessions = newSessions;
user.save(function(err, result) { //user.save(function(err, result) {
var domain = new URL(config.get('endpoint')).hostname; var domain = new URL(config.get('endpoint')).hostname;
res.clearCookie('sdsession', { domain: domain }); res.clearCookie('sdsession', { domain: domain });
res.sendStatus(204); res.sendStatus(204);
}); //});
} else { } else {
res.sendStatus(404); res.sendStatus(404);
} }

View File

@ -1,7 +1,10 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
var payloadConverter = require('../../helpers/artifact_converter'); var payloadConverter = require('../../helpers/artifact_converter');
var redis = require('../../helpers/redis'); var redis = require('../../helpers/redis');
@ -9,13 +12,11 @@ var redis = require('../../helpers/redis');
var async = require('async'); var async = require('async');
var fs = require('fs'); var fs = require('fs');
var _ = require("underscore"); var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver'); var archiver = require('archiver');
var request = require('request'); var request = require('request');
var url = require("url"); var url = require("url");
var path = require("path"); var path = require("path");
var crypto = require('crypto'); var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob'); var glob = require('glob');
var gm = require('gm'); var gm = require('gm');
@ -46,15 +47,24 @@ var roleMapping = {
// ARTIFACTS // ARTIFACTS
router.get('/', (req, res) => { router.get('/', (req, res) => {
Artifact.find({ db.Artifact.findAll({where: {
space_id: req.space._id space_id: req.space._id
}).exec((err, artifacts) => { }}).then(artifacts => {
async.map(artifacts, (a, cb) => { async.map(artifacts, (a, cb) => {
a = a.toObject(); //a = a.toObject(); TODO
if (a.control_points) {
a.control_points = JSON.parse(a.control_points);
}
if (a.payload_alternatives) {
a.payload_alternatives = JSON.parse(a.payload_alternatives);
}
if (a.user_id) { if (a.user_id) {
User.findOne({ // FIXME JOIN
/*User.findOne({where: {
"_id": a.user_id "_id": a.user_id
}).select({ }}).select({
"_id": 1, "_id": 1,
"nickname": 1, "nickname": 1,
"email": 1 "email": 1
@ -63,7 +73,8 @@ router.get('/', (req, res) => {
a['user'] = user.toObject(); a['user'] = user.toObject();
} }
cb(err, a); cb(err, a);
}); });*/
cb(null, a);
} else { } else {
cb(null, a); cb(null, a);
} }
@ -81,9 +92,8 @@ router.post('/', function(req, res, next) {
attrs['space_id'] = req.space._id; attrs['space_id'] = req.space._id;
var artifact = new Artifact(attrs); var artifact = attrs;
artifact._id = uuidv4();
artifact.created_from_ip = req['real_ip'];
if (req.user) { if (req.user) {
artifact.user_id = req.user._id; artifact.user_id = req.user._id;
@ -92,23 +102,18 @@ router.post('/', function(req, res, next) {
artifact.last_update_editor_name = req.editor_name; artifact.last_update_editor_name = req.editor_name;
} }
if (req.spaceRole == "editor"  ||  req.spaceRole == "admin") { db.packArtifact(artifact);
artifact.save(function(err) {
if (err) res.status(400).json(err); if (req.spaceRole == "editor" || req.spaceRole == "admin") {
else { db.Artifact.create(artifact).then(() => {
Space.update({ //if (err) res.status(400).json(err);
_id: req.space._id db.unpackArtifact(artifact);
}, { db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
"$set": {
updated_at: new Date()
}
});
res.distributeCreate("Artifact", artifact); res.distributeCreate("Artifact", artifact);
}
}); });
} else { } else {
res.status(401).json({ res.status(401).json({
"error": "no access" "error": "Access denied"
}); });
} }
}); });
@ -118,6 +123,8 @@ router.post('/:artifact_id/payload', function(req, res, next) {
var a = req.artifact; var a = req.artifact;
var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, ''); var fileName = (req.query.filename || "upload.bin").replace(/[^a-zA-Z0-9_\-\.]/g, '');
// FIXME TODO use portable tmp dir
var localFilePath = "/tmp/" + fileName; var localFilePath = "/tmp/" + fileName;
var writeStream = fs.createWriteStream(localFilePath); var writeStream = fs.createWriteStream(localFilePath);
var stream = req.pipe(writeStream); var stream = req.pipe(writeStream);
@ -132,13 +139,7 @@ router.post('/:artifact_id/payload', function(req, res, next) {
payloadConverter.convert(a, fileName, localFilePath, function(error, artifact) { payloadConverter.convert(a, fileName, localFilePath, function(error, artifact) {
if (error) res.status(400).json(error); if (error) res.status(400).json(error);
else { else {
Space.update({ db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id}});
_id: req.space._id
}, {
"$set": {
updated_at: new Date()
}
});
res.distributeUpdate("Artifact", artifact); res.distributeUpdate("Artifact", artifact);
} }
}, progress_callback); }, progress_callback);
@ -162,41 +163,22 @@ router.put('/:artifact_id', function(req, res, next) {
newAttr.last_update_editor_name = req.editor_name; newAttr.last_update_editor_name = req.editor_name;
} }
Artifact.findOneAndUpdate({ db.packArtifact(newAttr);
db.Artifact.update(newAttr, { where: {
"_id": a._id "_id": a._id
}, { }}).then(rows => {
"$set": newAttr db.unpackArtifact(newAttr);
}, { db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} });
"new": true res.distributeUpdate("Artifact", newAttr);
}, function(err, artifact) {
if (err) res.status(400).json(err);
else {
Space.update({
_id: req.space._id
}, {
"$set": {
updated_at: new Date()
}
});
res.distributeUpdate("Artifact", artifact);
}
}); });
}); });
router.delete('/:artifact_id', function(req, res, next) { router.delete('/:artifact_id', function(req, res, next) {
var artifact = req.artifact; var artifact = req.artifact;
artifact.remove(function(err) { db.Artifact.destroy({where: { "_id": artifact._id}}).then(() => {
if (err) res.status(400).json(err); db.Space.update({ updated_at: new Date() }, {where: {_id: req.space._id} });
else {
Space.update({
_id: req.space._id
}, {
"$set": {
updated_at: new Date()
}
});
res.distributeDelete("Artifact", artifact); res.distributeDelete("Artifact", artifact);
}
}); });
}); });

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); require('../../models/db');
var async = require('async'); var async = require('async');
var fs = require('fs'); var fs = require('fs');
@ -40,6 +40,12 @@ var roleMapping = {
}; };
router.get('/', function(req, res, next) { router.get('/', function(req, res, next) {
res.status(200).json([]);
return;
// FIXME TODO
var showActionForSpaces = function(err, spaceIds) { var showActionForSpaces = function(err, spaceIds) {
var userMapping = { var userMapping = {
'_id': 1, '_id': 1,

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); require('../../models/db');
var redis = require('../../helpers/redis'); var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
@ -337,28 +337,5 @@ router.get('/html', function(req, res) {
}); });
}); });
router.get('/path', (req, res) => {
// build up a breadcrumb trail (path)
var path = [];
var buildPath = (space) => {
if (space.parent_space_id) {
Space.findOne({
"_id": space.parent_space_id
}, (err, parentSpace) => {
if (space._id == parentSpace._id) {
console.log("error: circular parent reference for space " + space._id);
res.send("error: circular reference");
} else {
path.push(parentSpace);
buildPath(parentSpace);
}
});
} else {
// reached the top
res.json(path.reverse());
}
}
buildPath(req.space);
});
module.exports = router; module.exports = router;

View File

@ -1,25 +1,19 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
var redis = require('../../helpers/redis'); var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader');
var space_render = require('../../helpers/space-render');
var phantom = require('../../helpers/phantom');
var async = require('async'); var async = require('async');
var fs = require('fs'); var fs = require('fs');
var _ = require("underscore"); var _ = require("underscore");
var mongoose = require("mongoose");
var archiver = require('archiver');
var request = require('request'); var request = require('request');
var url = require("url"); var url = require("url");
var path = require("path"); var path = require("path");
var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob'); var glob = require('glob');
var gm = require('gm');
var express = require('express'); var express = require('express');
var router = express.Router({mergeParams: true}); var router = express.Router({mergeParams: true});
@ -46,12 +40,12 @@ var roleMapping = {
} }
router.get('/', function(req, res, next) { router.get('/', function(req, res, next) {
Membership db.Membership
.find({ .findAll({where: {
space: req.space._id space_id: req.space._id
}) }})
.populate("user") //.populate("user")
.exec(function(err, memberships) { .then(memberships => {
res.status(200).json(memberships); res.status(200).json(memberships);
}); });
}); });

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); require('../../models/db');
var redis = require('../../helpers/redis'); var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');

View File

@ -1,6 +1,9 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); const db = require('../../models/db');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const uuidv4 = require('uuid/v4');
var redis = require('../../helpers/redis'); var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
@ -15,12 +18,10 @@ var fs = require('fs');
var async = require('async'); var async = require('async');
var _ = require("underscore"); var _ = require("underscore");
var mongoose = require("mongoose"); var mongoose = require("mongoose");
var archiver = require('archiver');
var request = require('request'); var request = require('request');
var url = require("url"); var url = require("url");
var path = require("path"); var path = require("path");
var crypto = require('crypto'); var crypto = require('crypto');
var qr = require('qr-image');
var glob = require('glob'); var glob = require('glob');
var gm = require('gm'); var gm = require('gm');
const exec = require('child_process'); const exec = require('child_process');
@ -48,15 +49,14 @@ router.get('/', function(req, res, next) {
}); });
} else { } else {
if (req.query.writablefolders) { if (req.query.writablefolders) {
Membership.find({ db.Membership.find({where: {
user: req.user._id user_id: req.user._id
}, (err, memberships) => { }}, (memberships) => {
var validMemberships = memberships.filter((m) => { var validMemberships = memberships.filter((m) => {
if (!m.space || (m.space == "undefined")) if (!m.space_id || (m.space_id == "undefined"))
return false; return false;
else return true;
return mongoose.Types.ObjectId.isValid(m.space.toString());
}); });
var editorMemberships = validMemberships.filter((m) => { var editorMemberships = validMemberships.filter((m) => {
@ -64,7 +64,7 @@ router.get('/', function(req, res, next) {
}); });
var spaceIds = editorMemberships.map(function(m) { var spaceIds = editorMemberships.map(function(m) {
return new mongoose.Types.ObjectId(m.space); return m.space_id;
}); });
var q = { var q = {
@ -158,7 +158,7 @@ router.get('/', function(req, res, next) {
.populate('creator', userMapping) .populate('creator', userMapping)
.exec(function(err, space) { .exec(function(err, space) {
if (space) { if (space) {
Space.roleInSpace(space, req.user, function(err, role) { db.getUserRoleInSpace(space, req.user, function(role) {
if (role == "none") { if (role == "none") {
if(space.access_mode == "public") { if(space.access_mode == "public") {
@ -185,41 +185,42 @@ router.get('/', function(req, res, next) {
}); });
} else { } else {
Membership.find({
user: req.user._id console.log("!!!!!!!!!! spaces lookup");
}, function(err, memberships) {
db.Membership.findAll({ where: {
user_id: req.user._id
}}).then(memberships => {
if (!memberships) memberships = [];
var validMemberships = memberships.filter(function(m) { var validMemberships = memberships.filter(function(m) {
if (!m.space || (m.space == "undefined")) if (!m.space_id || (m.space_id == "undefined"))
return false; return false;
else
return mongoose.Types.ObjectId.isValid(m.space.toString());
}); });
var spaceIds = validMemberships.map(function(m) { var spaceIds = validMemberships.map(function(m) {
return new mongoose.Types.ObjectId(m.space); return m.space_id;
}); });
var q = { var q = {
"$or": [{ [Op.or]: [{
"creator": req.user._id, "creator_id": req.user._id,
"parent_space_id": req.user.home_folder_id "parent_space_id": req.user.home_folder_id
}, { }, {
"_id": { "_id": {
"$in": spaceIds [Op.in]: spaceIds
}, },
"creator": { "creator_id": {
"$ne": req.user._id [Op.ne]: req.user._id
} }
}] }]
}; };
Space db.Space
.find(q) .findAll({where: q})
.populate('creator', userMapping) .then(function(spaces) {
.exec(function(err, spaces) {
if (err) console.error(err);
var updatedSpaces = spaces.map(function(s) { var updatedSpaces = spaces.map(function(s) {
var spaceObj = s.toObject(); var spaceObj = db.spaceToObject(s);
return spaceObj; return spaceObj;
}); });
res.status(200).json(spaces); res.status(200).json(spaces);
@ -229,47 +230,46 @@ router.get('/', function(req, res, next) {
} }
}); });
// create a space
router.post('/', function(req, res, next) { router.post('/', function(req, res, next) {
if (req.user) { if (req.user) {
var attrs = req.body; var attrs = req.body;
var createSpace = () => { var createSpace = () => {
attrs._id = uuidv4();
attrs.creator = req.user; 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);
var space = new Space(attrs); db.Space.create(attrs).then(createdSpace => {
space.save(function(err, createdSpace) { //if (err) res.sendStatus(400);
if (err) res.sendStatus(400);
else { console.log("!!!!!!!!!! createdSpace:",createdSpace);
var membership = new Membership({
user: req.user, var membership = {
space: createdSpace, _id: uuidv4(),
user_id: req.user._id,
space_id: attrs._id,
role: "admin" role: "admin"
}); };
membership.save(function(err, createdTeam) {
if (err) { db.Membership.create(membership).then(() => {
res.status(400).json(err);
} else {
res.status(201).json(createdSpace); res.status(201).json(createdSpace);
}
}); });
}
}); });
} }
if (attrs.parent_space_id) { if (attrs.parent_space_id) {
Space.findOne({ db.Space.findOne({ where: {
"_id": attrs.parent_space_id "_id": attrs.parent_space_id
}).populate('creator', userMapping).exec((err, parentSpace) => { }}).then(parentSpace => {
if (parentSpace) { if (parentSpace) {
Space.roleInSpace(parentSpace, req.user, (err, role) => { db.getUserRoleInSpace(parentSpace, req.user, (role) => {
if ((role == "editor") || (role == "admin")) { if ((role == "editor") || (role == "admin")) {
createSpace(); createSpace();
} else { } else {
res.status(403).json({ res.status(403).json({
"error": "not editor in parent Space" "error": "not editor in parent Space. role: "+role
}); });
} }
}); });
@ -292,6 +292,30 @@ router.get('/:id', function(req, res, next) {
res.status(200).json(req.space); res.status(200).json(req.space);
}); });
router.get('/:id/path', (req, res) => {
// build up a breadcrumb trail (path)
var path = [];
var buildPath = (space) => {
if (space.parent_space_id) {
db.Space.findOne({ where: {
"_id": space.parent_space_id
}}).then(parentSpace => {
if (space._id == parentSpace._id) {
console.error("error: circular parent reference for space " + space._id);
res.send("error: circular reference");
} else {
path.push(parentSpace);
buildPath(parentSpace);
}
});
} else {
// reached the top
res.json(path.reverse());
}
}
buildPath(req.space);
});
router.put('/:id', function(req, res) { router.put('/:id', function(req, res) {
var space = req.space; var space = req.space;
var newAttr = req.body; var newAttr = req.body;
@ -308,17 +332,10 @@ router.put('/:id', function(req, res) {
delete newAttr['editor_name']; delete newAttr['editor_name'];
delete newAttr['creator']; delete newAttr['creator'];
Space.findOneAndUpdate({ db.Space.update(newAttr, {where: {
"_id": space._id "_id": space._id
}, { }}).then(space => {
"$set": newAttr
}, {
"new": true
}, function(err, space) {
if (err) res.status(400).json(err);
else {
res.distributeUpdate("Space", space); res.distributeUpdate("Space", space);
}
}); });
}); });
@ -339,7 +356,7 @@ router.post('/:id/background', function(req, res, next) {
if (adv.background_uri) { if (adv.background_uri) {
var oldPath = url.parse(req.space.thumbnail_url).pathname; var oldPath = url.parse(req.space.thumbnail_url).pathname;
uploader.removeFile(oldPath, function(err) { uploader.removeFile(oldPath, function(err) {
console.log("removed old bg error:", err); console.error("removed old bg error:", err);
}); });
} }
@ -390,10 +407,10 @@ router.post('/:id/duplicate', (req, res, next) => {
}).populate('creator', userMapping).exec((err, parentSpace) => { }).populate('creator', userMapping).exec((err, parentSpace) => {
if (!parentSpace) { if (!parentSpace) {
res.status(404).json({ res.status(404).json({
"error": "parent space not found for dupicate" "error": "parent space not found for duplicate"
}); });
} else { } else {
Space.roleInSpace(parentSpace, req.user, (err, role) => { db.getUserRoleInSpace(parentSpace, req.user, (role) => {
if (role == "admin" ||  role == "editor") { if (role == "admin" ||  role == "editor") {
handleDuplicateSpaceRequest(req, res, parentSpace); handleDuplicateSpaceRequest(req, res, parentSpace);
} else { } else {
@ -449,6 +466,7 @@ router.post('/:id/artifacts-pdf', function(req, res, next) {
fs.mkdir(outputFolder, function(db) { fs.mkdir(outputFolder, function(db) {
var images = outputFolder + "/" + rawName + "-page-%03d.jpeg"; 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) { exec.execFile("gs", ["-sDEVICE=jpeg", "-dDownScaleFactor=4", "-dDOINTERPOLATE", "-dNOPAUSE", "-dJPEGQ=80", "-dBATCH", "-sOutputFile=" + images, "-r250", "-f", localFilePath], {}, function(error, stdout, stderr) {
if (error === null) { if (error === null) {
@ -532,6 +550,7 @@ router.post('/:id/artifacts-pdf', function(req, res, next) {
}, function(err, artifacts) { }, function(err, artifacts) {
// FIXME not portable
exec.execFile("rm", ["-r", outputFolder], function(err) { exec.execFile("rm", ["-r", outputFolder], function(err) {
res.status(201).json(_.flatten(artifacts)); res.status(201).json(_.flatten(artifacts));
@ -551,6 +570,7 @@ router.post('/:id/artifacts-pdf', function(req, res, next) {
}); });
} else { } else {
console.error("error:", error); console.error("error:", error);
// FIXME not portable
exec.execFile("rm", ["-r", outputFolder], function(err) { exec.execFile("rm", ["-r", outputFolder], function(err) {
fs.unlink(localFilePath); fs.unlink(localFilePath);
res.status(400).json({}); res.status(400).json({});

View File

@ -1,265 +0,0 @@
"use strict";
var config = require('config');
require('../../models/schema');
var redis = require('../../helpers/redis');
var mailer = require('../../helpers/mailer');
var fs = require('fs');
var _ = require('underscore');
var crypto = require('crypto');
var bcrypt = require('bcryptjs');
var express = require('express');
var router = express.Router();
var userMapping = { '_id': 1, 'nickname': 1, 'email': 1};
router.get('/:id', (req, res) => {
res.status(200).json(req.user.team);
});
router.put('/:id', (req, res) => {
var team = req.user.team;
if (!team) {
res.status(400).json({"error": "user in no team"});
} else {
var newAttr = req.body;
newAttr.updated_at = new Date();
delete newAttr['_id'];
if(newAttr['subdomain']) {
newAttr['subdomain'] = newAttr['subdomain'].toLowerCase();
}
const new_subdomain = newAttr['subdomain'];
var forbidden_subdomains = [];
function updateTeam() {
Team.findOneAndUpdate({"_id": team._id}, {"$set": newAttr}, {"new": true}, (err, team) => {
if (err) res.status(400).json(err);
else {
res.status(200).json(team);
}
});
}
var isForbidden = forbidden_subdomains.indexOf(new_subdomain) > -1;
if (isForbidden) {
res.bad_request("subdomain not valid");
} else {
if (new_subdomain) {
Team.findOne({"domain": new_subdomain}).exec((err, team) => {
if(team) {
res.bad_request("subdomain already used");
} else {
updateTeam()
}
});
} else {
updateTeam()
}
}
}
});
router.get('/:id/memberships', (req, res) => {
User
.find({team: req.user.team})
.populate("team")
.exec(function(err, users){
if (err) res.status(400).json(err);
else {
res.status(200).json(users);
}
});
});
router.post('/:id/memberships', (req, res, next) => {
if (req.body.email) {
const email = req.body.email.toLowerCase();
const team = req.user.team;
User.findOne({"email": email}).populate('team').exec((err, user) => {
if (user) {
const code = crypto.randomBytes(64).toString('hex').substring(0,7);
team.invitation_codes.push(code);
team.save((err) => {
if (err){ res.status(400).json(err); }
else {
mailer.sendMail(email, req.i18n.__("team_invite_membership_subject", team.name), req.i18n.__("team_invite_membership_body", team.name), { action: {
link: config.endpoint + "/teams/" + req.user.team._id + "/join?code=" + code,
name: req.i18n.__("team_invite_membership_action"),
teamname: team.name
}});
res.status(201).json(user);
}
});
} else {
// complete new user
const password = crypto.randomBytes(64).toString('hex').substring(0,7);
const confirmation_token = crypto.randomBytes(64).toString('hex').substring(0,7);
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, (err, hash) => {
crypto.randomBytes(16, (ex, buf) => {
const token = buf.toString('hex');
var u = new User({
email: email,
account_type: "email",
nickname: email,
team: team._id,
password_hash: hash,
payment_plan_key: team.payment_plan_key,
confirmation_token: confirmation_token,
preferences: {
language: req.i18n.locale
}
});
u.save((err) => {
if(err) res.sendStatus(400);
else {
var homeSpace = new Space({
name: req.i18n.__("home"),
space_type: "folder",
creator: u
});
homeSpace.save((err, homeSpace) => {
if (err) res.sendStatus(400);
else {
u.home_folder_id = homeSpace._id;
u.save((err) => {
User.find({"_id": {"$in": team.admins }}).exec((err, admins) => {
admins.forEach((admin) => {
var i18n = req.i18n;
if(admin.preferences && admin.preferences.language){
i18n.setLocale(admin.preferences.language || "en");
}
mailer.sendMail(admin.email, i18n.__("team_invite_membership_subject", team.name), i18n.__("team_invite_admin_body", email, team.name, password), { teamname: team.name });
});
});
mailer.sendMail(email, req.i18n.__("team_invite_membership_subject", team.name), req.i18n.__("team_invite_user_body", team.name, password), { action: {
link: config.endpoint + "/users/byteam/" + req.user.team._id + "/join?confirmation_token=" + confirmation_token,
name: req.i18n.__("team_invite_membership_action")
}, teamname: team.name });
if (err) res.status(400).json(err);
else{
res.status(201).json(u)
}
});
}
});
}
});
});
});
});
}
});
} else {
res.status(400).json({"error": "email missing"});
}
});
router.put('/:id/memberships/:user_id', (req, res) => {
User.findOne({_id: req.params.user_id}, (err,mem) => {
if (err) res.sendStatus(400);
else {
if(user.team._id == req.user.team._id){
user['team'] = req.user.team._id;
user.save((err) => {
res.sendStatus(204);
});
} else {
res.sendStatus(403);
}
}
});
});
router.get('/:id/memberships/:user_id/promote', (req, res) => {
User.findOne({_id: req.params.user_id}, (err,user) => {
if (err) res.sendStatus(400);
else {
if (user.team.toString() == req.user.team._id.toString()) {
var team = req.user.team;
var adminIndex = team.admins.indexOf(user._id);
if (adminIndex == -1) {
team.admins.push(user._id);
team.save((err, team) => {
res.status(204).json(team);
});
} else {
res.status(400).json({"error": "already admin"});
}
} else {
res.status(403).json({"error": "team id not correct"});
}
}
});
});
router.get('/:id/memberships/:user_id/demote', (req, res, next) => {
User.findOne({_id: req.params.user_id}, (err,user) => {
if (err) res.sendStatus(400);
else {
if (user.team.toString() == req.user.team._id.toString()) {
const team = req.user.team;
const adminIndex = team.admins.indexOf(user._id);
if(adminIndex > -1) {
team.admins.splice(adminIndex,1);
team.save((err, team) => {
res.status(204).json(team);
});
} else {
res.sendStatus(404);
}
} else {
res.sendStatus(403);
}
}
});
});
router.delete('/:id/memberships/:user_id', (req, res) => {
User.findOne({_id: req.params.user_id}).populate('team').exec((err,user) => {
if (err) res.sendStatus(400);
else {
const currentUserId = req.user._id.toString();
const team = req.user.team;
const isAdmin = (req.user.team.admins.filter( mem => {
return mem == currentUserId;
}).length == 1)
if (isAdmin) {
user.team = null;
user.payment_plan_key = "free";
user.save( err => {
const adminIndex = team.admins.indexOf(user._id);
if(adminIndex > -1) {
team.admins.splice(adminIndex,1);
team.save((err, team) => {
console.log("admin removed");
});
}
res.sendStatus(204);
});
} else {
res.status(403).json({"error": "not admin"});
}
}
});
});
module.exports = router;

View File

@ -1,14 +1,15 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); const db = require('../../models/db');
const uuidv4 = require('uuid/v4');
var mailer = require('../../helpers/mailer'); var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader'); var uploader = require('../../helpers/uploader');
var importer = require('../../helpers/importer'); var importer = require('../../helpers/importer');
var bcrypt = require('bcryptjs'); var bcrypt = require('bcryptjs');
var crypo = require('crypto'); var crypto = require('crypto');
var swig = require('swig'); var swig = require('swig');
var async = require('async'); var async = require('async');
var _ = require('underscore'); var _ = require('underscore');
@ -30,49 +31,66 @@ router.get('/current', function(req, res, next) {
} }
}); });
// create user
router.post('/', function(req, res) { router.post('/', function(req, res) {
if (req.body["email"] && req.body["password"]) { if (!req.body["email"] || !req.body["password"]) {
res.status(400).json({"error":"email or password missing"});
return;
}
var email = req.body["email"].toLowerCase(); var email = req.body["email"].toLowerCase();
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"];
if (password_confirmation == password) { if (password_confirmation != password) {
if (validator.isEmail(email)) { res.status(400).json({"error":"password_confirmation"});
return;
}
if (!validator.isEmail(email)) {
res.status(400).json({"error":"email_invalid"});
return;
}
var createUser = function() { var createUser = function() {
bcrypt.genSalt(10, function(err, salt) { bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) { bcrypt.hash(password, salt, function(err, hash) {
crypto.randomBytes(16, function(ex, buf) {
crypo.randomBytes(16, function(ex, buf) {
var token = buf.toString('hex'); var token = buf.toString('hex');
var u = new User({ var u = {
_id: uuidv4(),
email: email, email: email,
account_type: "email", account_type: "email",
nickname: nickname, nickname: nickname,
password_hash: hash, password_hash: hash,
preferences: { prefs_language: req.i18n.locale,
language: req.i18n.locale
},
confirmation_token: token confirmation_token: token
}); };
u.save(function (err) { db.User.create(u)
if (err) res.sendStatus(400); .error(err => {
else { res.sendStatus(400);
var homeSpace = new Space({ })
.then(u => {
console.log("!!! created user:", u);
var homeSpace = {
_id: uuidv4(),
name: req.i18n.__("home"), name: req.i18n.__("home"),
space_type: "folder", space_type: "folder",
creator: u creator_id: u._id
}); };
homeSpace.save((err, homeSpace) => { db.Space.create(homeSpace)
if (err) res.sendStatus(400); .error(err => {
else { res.sendStatus(400);
})
.then(homeSpace => {
u.home_folder_id = homeSpace._id; u.home_folder_id = homeSpace._id;
u.save((err) => { u.save()
.then(() => {
res.status(201).json({});
mailer.sendMail(u.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), { mailer.sendMail(u.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), {
action: { action: {
@ -80,181 +98,32 @@ router.post('/', function(req, res) {
name: req.i18n.__("confirm_action") name: req.i18n.__("confirm_action")
} }
}); });
})
if (err) res.status(400).json(err); .error(err => {
else { res.status(400).json(err);
res.status(201).json({});
}
}); });
} })
});
}
}); });
}); });
}); });
}); });
}; };
User.find({email: email}, (function (err, users) { console.log("!!! hello !!!");
if (err) {
res.status(400).json({"error":"password_confirmation"});
} else {
if (users.length === 0) { db.User.findAll({where: {email: email}})
var domain = email.slice(email.lastIndexOf('@')+1); .then(users => {
if (users.length == 0) {
Domain.findOne({domain: domain}, function(err, domain) { //var domain = email.slice(email.lastIndexOf('@')+1);
if(domain){
if(domain.edu) {
createUser(); createUser();
} else {
res.status(400).json({"error":"domain_blocked"});
}
} else {
createUser();
}
});
} else { } else {
res.status(400).json({"error":"user_email_already_used"}); res.status(400).json({"error":"user_email_already_used"});
} }
} })
}));
} else {
res.status(400).json({"error":"email_invalid"});
}
} else {
res.status(400).json({"error":"password_confirmation"});
}
} else {
res.status(400).json({"error":"email or password missing"});
}
}); });
router.get('/oauth2callback/url', function(req, res) { router.get('/current', function(req, res, next) {
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2(
config.google_access,
config.google_secret,
config.endpoint + "/login"
);
var url = oauth2Client.generateAuthUrl({
access_type: 'online',
scope: "email"
});
res.status(200).json({"url":url});
});
router.get('/loginorsignupviagoogle', function(req, res) {
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var plus = google.plus('v1');
var oauth2Client = new OAuth2(
config.google_access,
config.google_secret,
config.endpoint + "/login"
);
var loginUser = function(user, cb) {
crypo.randomBytes(48, function(ex, buf) {
var token = buf.toString('hex');
var session = {
token: token,
created_at: new Date()
};
if(!user.sessions)
user.sessions = [];
user.sessions.push(session);
user.save(function(err, user) {
cb(session);
});
});
};
var code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
if (err) res.status(400).json(err);
else {
var apiUrl = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=" + tokens.access_token;
var finalizeLogin = function(session){
res.cookie('sdsession', session.token, { httpOnly: true });
res.status(201).json(session);
};
request.get(apiUrl, function(error, response, body) {
if (error) res.status(400).json(error);
else {
const data = JSON.parse(body);
const email = data.email;
const name = data.name;
User.findOne({email: email}, function (err, user) {
if (user) {
// login new google user
if (user.account_type == "google") {
// just login
loginUser(user, (session) => {
finalizeLogin(session);
});
} else {
res.status(400).json({"error":"user_email_already_used"});
}
} else {
const u = new User({
email: email,
account_type: "google",
nickname: name,
avatar_thumb_uri: body.picture,
preferences: {
language: req.i18n.locale
},
confirmed_at: new Date()
});
u.save(function (err) {
if (err) res.status(400).json(err);
else {
var homeSpace = new Space({
name: req.i18n.__("home"),
space_type: "folder",
creator: u
});
homeSpace.save(function(err, homeSpace) {
if (err) res.status(400).json(err);
else {
u.home_folder_id = homeSpace._id;
u.save(function(err){
if (err) res.sendStatus(400);
else {
mailer.sendMail(u.email, req.i18n.__("welcome_subject"), req.i18n.__("welcome_body"), {});
loginUser(u, function(session) {
finalizeLogin(session);
});
}
});
}
});
}
});
}
});
}
});
}
});
});
router.get('/ ', function(req, res, next) {
if (req.user) { if (req.user) {
console.log(req.user.team);
res.status(200).json(req.user); res.status(200).json(req.user);
} else { } else {
res.status(401).json({"error":"user_not_found"}); res.status(401).json({"error":"user_not_found"});
@ -406,7 +275,7 @@ router.post('/password_reset_requests', (req, res, next) => {
} else { } else {
if (user) { if (user) {
if(user.account_type == "email") { if(user.account_type == "email") {
crypo.randomBytes(16, (ex, buf) => { crypto.randomBytes(16, (ex, buf) => {
user.password_reset_token = buf.toString('hex'); user.password_reset_token = buf.toString('hex');
user.save((err, updatedUser) => { user.save((err, updatedUser) => {
if (err) res.status(400).json(err); if (err) res.status(400).json(err);

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
var config = require('config'); var config = require('config');
require('../../models/schema'); require('../../models/db');
var fs = require('fs'); var fs = require('fs');
var phantom = require('node-phantom-simple'); var phantom = require('node-phantom-simple');

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
const config = require('config'); const config = require('config');
require('../models/schema'); require('../models/db');
const redis = require('../helpers/redis'); const redis = require('../helpers/redis');
const express = require('express'); const express = require('express');
@ -95,10 +95,6 @@ router.get('/logout', (req, res) => {
res.render('spacedeck'); res.render('spacedeck');
}); });
router.get('/users/oauth2callback', (req, res) => {
res.render('spacedeck');
});
router.get('/contact', (req, res) => { router.get('/contact', (req, res) => {
res.render('public/contact'); res.render('public/contact');
}); });
@ -185,107 +181,6 @@ router.get('/spaces/:id', (req, res) => {
} else res.render('spacedeck', { title: 'Space' }); } else res.render('spacedeck', { title: 'Space' });
}); });
router.get('/users/byteam/:team_id/join', (req, res) => {
if (!req.user) {
const q = {confirmation_token: req.query.confirmation_token, account_type: "email", team: req.params.team_id};
User.findOne(q, (err, user) => {
if (err) {
res.status(400).json({"error":"session.users"});
} else {
if (user) {
crypto.randomBytes(48, function(ex, buf) {
const token = buf.toString('hex');
var session = {
token: token,
ip: req.ip,
device: "web",
created_at: new Date()
};
if (!user.sessions)
user.sessions = [];
user.sessions.push(session);
user.confirmed_at = new Date();
user.confirmation_token = null;
user.save(function(err, result) {
// FIXME
const secure = process.env.NODE_ENV == "production" || process.env.NODE_ENV == "staging";
const domain = (process.env.NODE_ENV == "production") ? ".spacedeck.com" : ".spacedecklocal.de";
res.cookie('sdsession', token, { domain: domain, httpOnly: true, secure: secure});
res.redirect("/spaces");
});
});
} else {
res.status(404).json({"error": "not found"});
}
}
});
} else {
res.redirect("/spaces");
}
});
router.get('/teams/:id/join', function(req, res, next) {
if (req.user) {
if (!req.user.team) {
Team.findOne({"_id": req.params.id}, function(err, team) {
if (team) {
const idx = team.invitation_codes.indexOf(req.query.code);
if (idx >= 0) {
const u = req.user;
u.team = team;
if(!u.confirmed_at) {
u.confirmed_at = new Date();
}
u.payment_plan_key = team.payment_plan_key;
u.save(function(err) {
if (err) res.status(400).json(err);
else {
team.invitation_condes = team.invitation_codes.slice(idx);
team.save(function(err) {
team.invitation_codes = null;
var finish = function(team, users) {
User.find({"_id": {"$in": team.admins}}).exec((err, admins) => {
if(admins) {
admins.forEach((admin) => {
mailer.sendMail(
admin.email,
req.i18n.__("team_new_member_subject", team.name),
req.i18n.__("team_new_member_body", u.email, team.name)
);
});
}
});
}
User.find({team: team}, function(err, users) {
finish(team, users);
res.redirect("/spaces");
});
});
}
});
} else {
res.redirect("/spaces?error=team_code_notfound");
}
} else {
res.redirect("/spaces?error=team_notfound");
}
});
} else {
res.redirect("/spaces?error=team_already");
}
} else res.redirect("/login");
});
router.get('/qrcode/:id', function(req, res) { router.get('/qrcode/:id', function(req, res) {
Space.findOne({"_id": req.params.id}).exec(function(err, space) { Space.findOne({"_id": req.params.id}).exec(function(err, space) {
if (space) { if (space) {

View File

@ -198,7 +198,7 @@
<div id="space" v-cloak <div id="space" v-cloak
v-if="active_view == 'space' && active_space_loaded" v-if="active_view == 'space' && active_space_loaded"
class="section board active mouse-{{mouse_state}} tool-{{active_tool}}" class="section board active mouse-{{mouse_state}} tool-{{active_tool}}"
v-bind:style="{'background-color': active_space.advanced.background_color}" v-bind:style="{'background-color': active_space.background_color}"
v-sd-droppable="handle_data_drop;active_space" v-sd-droppable="handle_data_drop;active_space"
v-sd-whiteboard v-sd-whiteboard
v-on:scroll="handle_scroll" v-on:scroll="handle_scroll"
@ -206,16 +206,16 @@
<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.advanced.width*bounds_zoom + 'px', height: active_space.advanced.height*bounds_zoom + 'px', 'background-color': active_space.advanced.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="{
transform: 'scale('+viewport_zoom+')', transform: 'scale('+viewport_zoom+')',
'transform-origin': '0 0', 'transform-origin': '0 0',
width: active_space.advanced.width + 'px', width: active_space.width + 'px',
height: active_space.advanced.height + 'px', height: active_space.height + 'px',
'background-image': (active_space.advanced.background_uri)?'url(' + active_space.advanced.background_uri + ')':'', 'background-image': (active_space.background_uri)?'url(' + active_space.background_uri + ')':'',
'background-color': ''+active_space.advanced.background_color, 'background-color': ''+active_space.background_color,
'margin-left': bounds_margin_horiz + 'px', 'margin-left': bounds_margin_horiz + 'px',
'margin-top': bounds_margin_vert + 'px'}" > 'margin-top': bounds_margin_vert + 'px'}" >
@ -331,7 +331,7 @@
<source v-bind:src="a.payload_uri" v-bind:type="a.mime" v-if="a.payload_uri"/> <source v-bind:src="a.payload_uri" v-bind:type="a.mime" v-if="a.payload_uri"/>
</audio> </audio>
<div class="timeline" v-show="a.board.h>=64 && a.board.w>=170" v-bind:style="{'background-image': 'url(' + a.payload_thumbnail_web_uri +')'}"> <div class="timeline" v-show="a.h>=64 && a.w>=170" v-bind:style="{'background-image': 'url(' + a.payload_thumbnail_web_uri +')'}">
<div class="tl-current-time" v-bind:style="{width: a.player_view.current_time_float*100 + '%'}"></div> <div class="tl-current-time" v-bind:style="{width: a.player_view.current_time_float*100 + '%'}"></div>
<div class="tl-inpoint" v-bind:style="{left: a.player_view.inpoint_float*100 + '%'}" v-if="a.player_view.inpoint_float>0.0"></div> <div class="tl-inpoint" v-bind:style="{left: a.player_view.inpoint_float*100 + '%'}" v-if="a.player_view.inpoint_float>0.0"></div>
<div class="tl-outpoint" v-bind:style="{left: a.player_view.outpoint_float*100 + '%'}"></div> <div class="tl-outpoint" v-bind:style="{left: a.player_view.outpoint_float*100 + '%'}"></div>
@ -352,13 +352,13 @@
<span class="icon icon-controls-stop"></span> <span class="icon icon-controls-stop"></span>
</span> </span>
<span class="tl-title" v-show="a.board.w>=400">{{a.view.filename}}</span> <span class="tl-title" v-show="a.w>=400">{{a.view.filename}}</span>
<span class="tl-times" class="btn-group"> <span class="tl-times" class="btn-group">
<span class="btn btn-md btn-transparent no-p">{{a.player_view.current_time_string}}</span> <span class="btn btn-md btn-transparent no-p">{{a.player_view.current_time_string}}</span>
<span class="btn btn-md btn-transparent no-p" v-show="a.board.w>=170"> / {{a.player_view.total_time_string}}</span> <span class="btn btn-md btn-transparent no-p" v-show="a.w>=170"> / {{a.player_view.total_time_string}}</span>
</span> </span>
<span v-show="logged_in && a.board.w>=310"> <span v-show="logged_in && a.w>=310">
<a class="btn btn-xs btn-round btn-icon set-inpoint" title="Set Inpoint at Playhead"> <a class="btn btn-xs btn-round btn-icon set-inpoint" title="Set Inpoint at Playhead">
<span class="icon icon-edge-left"></span> <span class="icon icon-edge-left"></span>
</a> </a>
@ -464,7 +464,7 @@
<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.advanced.width/minimap_scale)+'px', height: ''+(active_space.advanced.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)"
v-on:touchstart="handle_minimap_mousedown($event)" v-on:touchstart="handle_minimap_mousedown($event)"
@ -473,7 +473,7 @@
v-on:mouseleave="handle_minimap_mouseup($event)" v-on:mouseleave="handle_minimap_mouseup($event)"
v-on:touchend="handle_minimap_mouseup($event)" v-on:touchend="handle_minimap_mouseup($event)"
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.board.x/minimap_scale)+ 'px', top: ''+(a.board.y/minimap_scale) + 'px', width: ''+(a.board.w/minimap_scale)+ 'px', height: ''+(a.board.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>

View File

@ -79,14 +79,14 @@
</div--> </div-->
<div class="" v-show="background_mode=='image'" v-if="active_space"> <div class="" v-show="background_mode=='image'" v-if="active_space">
<div class="background-image" v-bind:style="{height: '233px', 'background-image':'url('+active_space.advanced.background_uri+')', 'margin': '6px', 'border-radius': '3px'}" v-if="active_space.advanced.background_uri && !space_background_uploading"> <div class="background-image" v-bind:style="{height: '233px', 'background-image':'url('+active_space.background_uri+')', 'margin': '6px', 'border-radius': '3px'}" v-if="active_space.background_uri && !space_background_uploading">
</div> </div>
<div class="progress state-processing" v-if="space_background_uploading"> <div class="progress state-processing" v-if="space_background_uploading">
<div class="spinner"></div> <div class="spinner"></div>
</div> </div>
<div class="dialog-section no-b adapt" v-if="!active_space.advanced.background_uri && !space_background_uploading" v-on:touchstart="handle_touch_select_background_image()"> <div class="dialog-section no-b adapt" v-if="!active_space.background_uri && !space_background_uploading" v-on:touchstart="handle_touch_select_background_image()">
<label class="btn btn-xxl btn-transparent btn-icon"> <label class="btn btn-xxl btn-transparent btn-icon">
<span class="icon icon-picture-upload"></span> <span class="icon icon-picture-upload"></span>
<input id="background-uploader" type="file" accept="image/*" v-on:change="handle_section_background_upload($event)"> <input id="background-uploader" type="file" accept="image/*" v-on:change="handle_section_background_upload($event)">
@ -94,9 +94,9 @@
<p>[[__("upload_background_caption")]]</p> <p>[[__("upload_background_caption")]]</p>
</div> </div>
<div class="dialog-section no-p no-flex" v-if="active_space.advanced.background_uri"> <div class="dialog-section no-p no-flex" v-if="active_space.background_uri">
<div class="btn-cluster"> <div class="btn-cluster">
<label class="btn btn-transparent btn-block text-center" v-if="active_space.advanced.background_uri" v-on:touchstart="handle_touch_select_background_image()"> <label class="btn btn-transparent btn-block text-center" v-if="active_space.background_uri" v-on:touchstart="handle_touch_select_background_image()">
<input id="background-uploader" type="file" accept="image/*" v-on:chang="handle_section_background_upload($event)"> <input id="background-uploader" type="file" accept="image/*" v-on:chang="handle_section_background_upload($event)">
<span class="icon icon-picture-upload"></span> <span class="icon icon-picture-upload"></span>
<!-- Upload --> <!-- Upload -->