spacedeck-open/public/javascripts/spacedeck_spaces.js

926 lines
27 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
SpacedeckSpaces
This module contains functions dealing with Spaces UI.
*/
var SpacedeckSpaces = {
data: {
active_space: {advanced:{}},
active_space_loaded: false,
active_space_role: "viewer",
active_space_version_dirty: true,
active_space_messages: [],
active_space_memberships: [],
active_folder_history_items: [],
active_space_users: [],
active_space_artifacts: [],
active_space_path: [],
access_settings_space: null,
access_settings_memberships: [],
pending_pdf_files: [],
meta_visible: false,
meta_unseen: 0,
present_mode: false,
space_editing_title: false,
create_space_title: "",
folder_reverse: 1,
embedded: false,
remix_cta: "Create Reply",
publish_cta: "Publish",
remix_copying: true,
remix_style: "",
guest_signup_enabled: false,
space_embed_html: "",
share_base: location.origin,
share_base_url: location.origin+"/spaces/",
share_base_url_enc: encodeURIComponent(location.origin+"/spaces/"),
social_bar: true,
can_add_comment: false,
space_info_section: "access",
editors_section: "list",
selected_member: null,
invite_member_role: 'viewer',
invite_email_error: null,
invite_email: "",
invite_message: "",
active_join_link: "",
join_link_role: "viewer",
mouse_state: "idle",
// folders
active_folder: null,
folder_sorting: "updated_at",
folder_spaces_filter: null,
active_path_length : 0,
space_comment: "",
folder_spaces_search: "",
// map of artifact IDs to medium rich text editor objects
medium_for_object: {},
},
methods: {
search_spaces: function() {
var query = this.folder_spaces_search;
load_spaces_search(query, function(spaces) {
this.active_profile_spaces = spaces;
}.bind(this));
},
guest_logout: function() {
if ("localStorage" in window && localStorage) {
delete localStorage['guest_nickname'];
}
this.guest_nickname = "";
location.reload();
},
ask_guestname: function(dft, cb) {
smoke.prompt(__('what_is_your_name', "Spacedeck") , function(content) {
if (!content || (content.length === 0)) {
this.ask_guestname(dft, cb);
} else {
this.guest_nickname = content;
if ("localStorage" in window && localStorage) {
try {
localStorage['guest_nickname'] = this.guest_nickname;
} catch(e) {
console.error(e);
}
}
if (cb) cb();
}
}.bind(this), {value: dft || "Guest "+parseInt(10000*Math.random()), ok: __("ok"), cancel: __("cancel")});
},
load_space: function(space_id, on_success, on_error) {
console.log("load space: ", space_id);
this.folder_spaces_filter="";
this.folder_spaces_search="";
space_auth = get_query_param("spaceAuth");
var userReady = function() {
if (get_query_param("embedded")) {
this.embedded = true;
this.guest_signup_enabled = true;
if (get_query_param("publish_cta")) {
this.publish_cta = get_query_param("publish_cta");
}
if (get_query_param("nosocial")) {
this.social_bar = false;
}
}
if (get_query_param("confirm") && this.logged_in) {
var token = get_query_param("confirm");
confirm_user(this.user, token, function() {
this.redirect_to("/spaces/"+space_id+"?show_access=1");
}.bind(this), function() {
alert("An error occured confirming your email with the given token.");
});
return;
}
this.close_dropdown();
this.active_space_loaded = false;
this.viewport_zoom = 1;
this.viewport_zoom_percent = 100;
this.loading_space_id = space_id;
this.present_mode = false;
this.active_space_is_readonly = true;
this.opened_dialog = "none";
this.open_space_dialog = "none";
this.selected_artifacts_dict = {};
this.update_selection_metrics();
this.can_add_comment = false;
var is_home = false;
if (this.user) {
is_home = (space_id == this.user.home_folder_id);
}
document.title = "Loading… | Spacedeck";
load_space(space_id, function(space, role) {
document.title = space.name;
this.active_space_role = role || "viewer"; //via req header from backend
this.space_embed_html = "<iframe width=\"1024\" height=\"768\" seamless src=\""+ENV.webEndpoint+"/spaces/"+space._id+"?embedded=1\"></iframe>";
if (!is_home) {
load_members(space, function(members) {
this.active_space_memberships = members;
}.bind(this));
}
console.log("[websocket] auth start");
if (space.space_type == "folder") {
this.active_space = {advanced:{}};
document.title = "Spacedeck";
load_spaces(space_id, is_home, function(spaces) {
space.children = spaces;
this.loading_space_id = null;
this.active_profile_spaces = space.children;
this.active_folder = space;
this.access_settings_space = space;
this.auth_websocket(this.active_folder);
this.load_space_path(this.active_folder);
if (is_home) {
this.root_folder = space;
}
load_history(space, function(history) {
console.log("loaded digest", history);
this.active_folder_history_items = history;
this.meta_unseen = 0;
if ("localStorage" in window && localStorage) {
var last_seen = parseInt(localStorage[this.meta_last_seen_key()], 10);
} else {
var last_seen = 0;
}
for (var i=0; i<history.length; i++) {
var item = history[i];
var t = new Date(item.last_action).getTime();
var my_own = false;
if (item.users.length==1 && item.users[0]=="you") {
my_own = true;
}
if (t>last_seen && !my_own) this.meta_unseen++;
}
}.bind(this));
this.active_view = "folders";
}.bind(this));
if ("localStorage" in window) {
var key = "folder_sorting_"+space_id;
var key2 = "folder_reverse_"+space_id;
if (localStorage[key] && localStorage[key2]) {
this.folder_sorting = localStorage[key];
this.folder_reverse = parseInt(localStorage[key2]);
console.log("loaded folder sorting: ",this.folder_sorting,this.folder_reverse);
}
}
// legacy fix
if (this.folder_sorting == "opened_at") {
this.folder_sorting = "name";
}
} else if (space.space_type == "space") {
this.artifacts = [];
this.loading_space_id = null;
document.title = space.name;
if (space_auth || this.logged_in) {
this.can_add_comment = true;
}
this.setup_watches();
load_artifacts(space._id, function(artifacts) {
// FIXME: how to cleanly handle this error?
if (!artifacts) {
artifacts = [];
}
// FIXME: remove kludge
for (var i=0; i<artifacts.length; i++) {
this.update_board_artifact_viewmodel(artifacts[i]);
}
this.active_space_artifacts = artifacts;
this.$set("active_space", space);
this.active_space = space;
this.auth_websocket(this.active_space);
this.active_view = "space";
this.fixup_space_size();
if (space._id != this.active_space._id) {
this.present_mode = true;
this.active_space_is_readonly = true;
} else {
this.active_space_is_readonly = false;
}
this.discover_zones();
window.setTimeout(function() {
this.zoom_to_fit();
}.bind(this),10);
if (on_success) {
on_success();
}
this.active_space_loaded = true;
this.extract_properties_from_selection(); // populates zones etc
load_comments(space._id, function(messages) {
if (!messages) messages = [];
this.active_space_messages = messages;
this.refresh_space_comments();
}.bind(this), function(xhr) { console.error(xhr); });
}.bind(this));
if (this.active_space_role == "editor" || this.active_space_role == "admin") {
this.present_mode = false;
this.active_space_is_readonly = false;
}
// FIXME
this.active_join_link = "";
this.join_link_role = "viewer";
// FIXME
if (this.active_space_role == "admin") {
this.space_info_section="access";
} else if (this.active_space_role == "editor") {
//this.space_info_section="versions";
} else {
this.space_info_section="info";
}
}
}.bind(this), function(xhr) {
if (on_error) {
return on_error(xhr);
}
if (xhr.status == 403) {
if (!this.logged_in) {
this.redirect_to("/login?space_id="+space_id);
} else {
this.redirect_to("/");
}
} else {
this.redirect_to("/not_found");
console.error(xhr);
}
}.bind(this));
}.bind(this);
var default_guest = "";
if (("localStorage" in window && localStorage) && localStorage['guest_nickname']) {
this.guest_nickname = localStorage['guest_nickname'];
default_guest = this.guest_nickname;
userReady();
}
if (space_auth) {
if (this.guest_nickname) {
userReady();
} else {
this.ask_guestname(default_guest, function() {
userReady();
});
}
} else {
this.guest_nickname = "";
userReady();
}
},
refresh_space_comments: function() {
this.meta_unseen = 0;
var messages = this.active_space_messages;
var last_seen = 0;
if ("localStorage" in window && localStorage) {
last_seen = parseInt(localStorage[this.meta_last_seen_key()], 10);
}
for (var i=0; i<messages.length; i++) {
var item = messages[i];
var t = new Date(item.updated_at).getTime();
var my_own = false;
if (this.user && this.user._id!=item.user_id && !item.editor_name) {
my_own = true;
}
if (t>last_seen && !my_own) this.meta_unseen++;
}
},
go_to_next_space: function() {
var space_ids = this.active_folder.children.map(function(s){return s._id});
var idx = space_ids.indexOf(this.active_space._id);
console.log("index: ",idx);
var cur_idx = idx;
var done = false;
while (!done) {
var next = this.active_folder.children[(idx+1)%space_ids.length];
if (next.space_type == "folder") {
done = false;
idx++;
} else {
done = true;
}
if (cur_idx == idx) done = true; // wraparound
}
this.load_space(next._id);
},
go_to_previous_space: function() {
var space_ids = this.active_folder.children.map(function(s){return s._id});
var idx = space_ids.indexOf(this.active_space._id);
console.log("index: ",idx);
var cur_idx = idx;
var done = false;
while (!done) {
var idx = (idx<1?space_ids.length:idx)-1;
var prev = this.active_folder.children[idx];
if (prev.space_type == "folder") {
done = false;
idx--;
} else {
done = true;
}
if (cur_idx == idx) done = true; // wraparound
}
this.load_space(prev._id);
},
filtered_folder_children: function(type){
var type = type || "space";
return _.filter(this.active_folder.children, function(s){
return s.space_type == type;
})
},
load_space_path: function(space) {
if (!space) return [];
load_space_path(space._id, function(path) {
this.active_space_path = path;
}.bind(this), function() { console.log("could not load folder path")});
},
is_active_space_role: function(rolename) {
if(!this.active_space) return false;
return this.active_space_role == rolename;
},
create_space: function(space_type) {
if (!this.active_folder) return;
this.close_modal();
this.folder_spaces_filter="";
if (!this.active_folder.children) {
this.active_folder.children = [];
}
if (!space_type) space_type = "space";
var s = {
name: space_type == "space" ? __("untitled_space") : __("untitled_folder") ,
artifacts: [],
space_type: space_type,
parent_space_id: this.active_folder._id
};
if (this.create_space_title.length) {
s.name = this.create_space_title;
}
save_space(s, function(saved_space) {
this.active_folder.children.push(saved_space);
if (space_type != "folder") {
this.redirect_to("/"+saved_space.space_type+"s/"+saved_space._id, function(succ) {
});
} else {
this.rename_folder(saved_space);
}
}.bind(this), function(xhr) {
alert("Error: Could not create Space ("+xhr.status+").");
}.bind(this));
},
save_space: function(s) {
save_space(s);
},
create_space_version: function() {
if (!this.is_pro(this.user)) {
// pro feature
smoke.confirm(__("spacedeck_pro_ad_versions"), function(confirmed) {
if (confirmed) this.show_upgrade_modal();
}.bind(this));
return;
}
this.version_saving = true;
this.present_mode = false;
var s = this.active_space.draft_space;
console.log("create_space_version:", s);
duplicate_space(s, null, function(new_version_space) {
load_spaces(this.active_space._id, false, function(space) {
this.version_saving = false;
this.activate_space_version(space, space.draft_space);
alert("Version saved.");
}.bind(this));
}.bind(this), function(xhr){
console.error(xhr);
}.bind(this));
},
finalize_folder_profile_edit: function() {
save_space(this.active_folder, function(saved_space) {
this.close_modal();
}.bind(this));
},
finalize_space_profile_edit: function() {
save_space(this.active_space, function(saved_space) {
this.close_modal();
}.bind(this));
},
delete_space: function(space) {
smoke.confirm("Really delete "+space.name+"?", function(confirmed) {
if (!confirmed) return;
var idx = this.active_folder.children.indexOf(space);
delete_space(space, function() {
if (space.parent_space_id){
this.redirect_to("/folders/"+space.parent_space_id, function(succ) {});
} else {
this.redirect_to("/spaces", function(succ) {});
}
this.close_modal();
this.active_folder.children.splice(idx,1);
}.bind(this));
}.bind(this));
},
duplicate_space: function(space) {
duplicate_space(space, null, function(new_space) {
//alert("Space duplicated.");
this.active_folder.children.push(new_space);
}.bind(this), function(xhr){
console.error(xhr);
}.bind(this));
},
remove_avatar: function(space) {
remove_avatar_file("space", space, function(s) {
this.active_space = s;
}.bind(this));
},
rename_space: function(space) {
this.close_dropdown();
if (space.space_type == "folder") return this.rename_folder(space);
smoke.prompt(__("new_space_title"), function(title) {
if (title && title.length) {
space.name = title;
save_space(space);
}
}.bind(this), {value: space.name});
},
rename_folder: function(folder) {
this.close_dropdown();
smoke.prompt(__("new_folder_title"), function(title) {
if (title && title.length) {
folder.name = title;
save_space(folder);
}
}.bind(this), {value: folder.name});
},
edit_space_title: function() {
this.close_dropdown();
if (this.active_space_role=="editor" || this.active_space_role=="admin") {
this.space_editing_title = true;
$("#space-title").focus();
}
},
save_space_title: function(name) {
this.active_space.name = name;
save_space(this.active_space, function() {
this.space_editing_title = false;
}.bind(this));
},
save_space_keydown: function($event) {
if ($event) {
if ($event.keyCode != 13) {
this.space_editing_title = true;
return;
}
$event.preventDefault();
$event.stopPropagation();
$event.target.blur();
}
save_space(this.active_space, function(updated_space) {
this.active_space.edit_slug = updated_space.edit_slug;
this.space_editing_title = false;
}.bind(this));
},
save_space_description: function($event) {
$event.preventDefault();
$event.stopPropagation();
var val = $event.target.innerText;
$event.target.blur();
this.active_space.description = val;
save_space(this.active_space);
},
save_space_domain: function($event) {
$event.preventDefault();
$event.stopPropagation();
var val = $event.target.innerText;
$event.target.blur();
this.active_space.domain = val;
save_space(this.active_space);
},
download_space: function() {
smoke.quiz(__("download_space"), function(e, test) {
if (e == "PDF") {
this.download_space_as_pdf(this.active_space);
} else if (e == "ZIP") {
this.download_space_as_zip(this.active_space);
}
}.bind(this), {
button_1: "PDF",
button_2: "ZIP",
button_cancel:__("cancel")
});
},
download_space_as_png: function(space) {
window.open(ENV.apiEndpoint + "/api/spaces/" + space._id + "/png");
},
download_space_as_pdf: function(space) {
this.global_spinner = true;
get_resource("/spaces/" + space._id + "/pdf", function(o) {
this.global_spinner = false;
location.href = o.url;
}.bind(this), function(xhr) {
this.global_spinner = false;
alert("PDF export problem (" + xhr.status + ").");
}.bind(this));
},
download_space_as_zip: function(space) {
this.global_spinner = true;
get_resource("/spaces/" + space._id + "/zip", function(o) {
this.global_spinner = false;
location.href = o.url;
}.bind(this), function(xhr) {
this.global_spinner = false;
alert("ZIP export problem (" + xhr.status + ").");
}.bind(this));
},
download_space_as_list: function(space) {
this.global_spinner = true;
location.href = "/api/spaces/" + space._id + "/list";
},
toggle_follow_mode: function() {
this.deselect();
this.follow_mode = !this.follow_mode;
},
toggle_present_mode: function() {
this.deselect();
this.present_mode = !this.present_mode;
if (this.present_mode) {
//this.go_to_first_zone();
}
},
meta_last_seen_key: function() {
var seen_key = "meta-seen-";
if (this.active_view == 'space') {
if (!this.active_space) return "invalid";
seen_key += this.active_space._id;
} else if (this.active_view == 'folders') {
if (!this.active_folder) return "invalid";
seen_key += this.active_folder._id;
}
return seen_key;
},
toggle_meta: function() {
this.meta_visible = !this.meta_visible;
if (this.meta_visible) {
var seen_key = this.meta_last_seen_key();
if ("localStorage" in window && localStorage) {
localStorage[seen_key] = new Date().getTime();
console.log("seen_key: ",seen_key,localStorage[seen_key]);
this.meta_last_seen = localStorage[seen_key];
}
this.meta_unseen = 0;
}
},
toggle_space_access_mode: function() {
this.access_settings_space.access_mode = (this.access_settings_space.access_mode=="public")?"private":"public";
save_space(this.access_settings_space);
},
save_space_access_mode: function(evt) {
// FIXME really bad that i have to do this manually. why is the
// value not already updated when v-on="change" is fired?!
this.access_settings_space.access_mode = evt.currentTarget.value;
save_space(this.access_settings_space);
},
save_space_editors_locking: function(evt) {
// FIXME same issue as above
this.access_settings_space.editors_locking = evt.currentTarget.checked;
save_space(this.access_settings_space);
},
create_join_link: function() {
create_join_link(this.active_space._id, this.join_link_role, function(result) {
this.active_join_link = "https://"+location.host+"/invitations/"+result.code+"/accept";
}.bind(this));
},
delete_join_link: function() {
get_join_link(this.active_space._id, function(result) {
if (result && result.length) {
delete_join_link(result[result.length-1]._id, function() {
this.active_join_link = "";
}.bind(this));
}
}.bind(this));
},
invite_member: function(space, emails_text, txt, role) {
this.invite_email_error = null;
var emails = emails_text.split(",");
var displayed_success = false;
_.each(emails, function(email) {
email = email.trim();
if (!validateEmail(email)) {
this.invite_email_error = "Please enter a valid address."
return;
}
var m = {
email_invited: email,
personal_message: txt,
role: role
}
create_membership(space, m, function(m) {
this.access_settings_memberships.push(m);
console.log("membership created:", m);
this.editors_section="list";
if (!displayed_success) {
displayed_success = true;
smoke.alert("Invitation(s) sent.");
this.invite_email = "";
this.invite_message = "";
}
}.bind(this), function(xhr){
try {
var res = JSON.parse(xhr.response);
alert("Error: "+res.error);
} catch (e) {
console.error(e, xhr);
}
}.bind(this));
}.bind(this));
},
update_member: function(space, m, role) {
m.role = role;
save_membership(space, m, function() {
}.bind(this), function(xhr) {
try {
var res = JSON.parse(xhr.response);
alert("Error: "+res.error);
} catch (e) {
console.error(e, xhr);
}
}.bind(this));
},
// revoke
remove_member: function(space, m) {
delete_membership(space, m, function() {
this.access_settings_memberships.splice(this.access_settings_memberships.indexOf(m), 1);
}.bind(this), function(xhr) {
try {
var res = JSON.parse(xhr.response);
alert("Error: "+res.error);
} catch (e) {
console.error(e, xhr);
}
}.bind(this));
},
history_back: function() {
window.history.back();
},
create_space_comment: function(comment) {
if (!comment.length) return;
var data = {
space: this.active_space._id,
message: comment,
editor_name: this.guest_nickname,
user: this.user
};
save_comment(this.active_space._id, data, function(comment) {
console.log("comment saved: ",comment.created_at);
this.active_space_messages.push(comment);
this.space_comment = "";
}.bind(this), function(xhr){
console.error(xhr);
}.bind(this));
},
remove_space_comment: function(comment) {
delete_comment(this.active_space._id, comment._id, function() {
console.log("comment id:",comment._id);
this.active_space_messages = _.filter(this.active_space_messages, function(c){return c._id!=comment._id;});
}.bind(this), function(xhr){
console.error(xhr);
}.bind(this));
},
set_folder_sorting: function(key,reverse) {
this.folder_sorting = key;
this.folder_reverse = reverse?-1:1;
console.log(key, reverse);
if ("localStorage" in window) {
localStorage["folder_sorting_"+this.active_folder._id] = this.folder_sorting;
localStorage["folder_reverse_"+this.active_folder._id] = this.folder_reverse;
}
},
activate_space_info_section: function(id) {
this.space_info_section = id;
this.editors_section = "list";
if (id == "versions") {
// load space versions
load_spaces(this.active_space._id, null, function(space_with_children) {
this.active_space.children = space_with_children.children;
console.log("loaded: ",space_with_children);
}.bind(this));
} else if (id == "info") {
// load replies
}
},
handle_folder_drop: function(evt, dest) {
try {
var source = JSON.parse(evt.dataTransfer.getData("application/json"));
} catch (e) {
return;
}
if (!source || !source._id || !source.parent_space_id || !dest._id) return;
if (source._id==dest._id) return;
if (dest.space_type!="folder") {
alert("Spaces can only be moved into folders.");
return;
}
source.parent_space_id = dest._id;
save_space(source, function() {
var idx = _.findIndex(this.active_folder.children, function(s) { return s._id == source._id;});
if (idx>=0) {
this.active_folder.children.splice(idx,1);
console.log("spliced: ",idx);
}
}.bind(this));
},
activate_access: function() {
this.activate_modal("access");
//this.meta_visible = false;
if (this.active_space._id) {
this.access_settings_space = this.active_space;
} else if (this.active_folder && this.active_folder._id) {
this.access_settings_space = this.active_folder;
} else {
return;
}
this.access_settings_memberships = this.active_space_memberships;
},
close_access: function() {
this.close_modal();
},
show_offline_help: function() {
smoke.confirm(__('was_offline'), function(confirmed) {
if (!confirmed) return;
location.reload();
});
}
}
}