cryptpad/www/common/toolbar3.js

1002 lines
38 KiB
JavaScript
Raw Normal View History

2017-08-17 12:55:44 +00:00
define([
'jquery',
'/customize/application_config.js',
'/api/config',
], function ($, Config, ApiConfig) {
var Messages = {};
var Cryptpad;
2017-08-17 16:28:05 +00:00
var Common;
2017-08-17 12:55:44 +00:00
var Bar = {
constants: {},
};
var SPINNER_DISAPPEAR_TIME = 1000;
// Toolbar parts
2017-09-04 13:09:54 +00:00
var TOOLBAR_CLS = Bar.constants.toolbar = 'cp-toolbar';
var TOP_CLS = Bar.constants.top = 'cp-toolbar-top';
var LEFTSIDE_CLS = Bar.constants.leftside = 'cp-toolbar-leftside';
var RIGHTSIDE_CLS = Bar.constants.rightside = 'cp-toolbar-rightside';
var DRAWER_CLS = Bar.constants.drawer = 'cp-toolbar-drawer-content';
var HISTORY_CLS = Bar.constants.history = 'cp-toolbar-history';
2017-08-17 12:55:44 +00:00
// Userlist
2017-09-04 13:09:54 +00:00
var USERLIST_CLS = Bar.constants.userlist = "cp-toolbar-users";
var EDITSHARE_CLS = Bar.constants.editShare = "cp-toolbar-share-edit";
var VIEWSHARE_CLS = Bar.constants.viewShare = "cp-toolbar-share-view";
var SHARE_CLS = Bar.constants.viewShare = "cp-toolbar-share";
2017-08-17 12:55:44 +00:00
// Top parts
2017-09-04 13:09:54 +00:00
var USER_CLS = Bar.constants.userAdmin = "cp-toolbar-user";
var SPINNER_CLS = Bar.constants.spinner = 'cp-toolbar-spinner';
var LIMIT_CLS = Bar.constants.limit = 'cp-toolbar-limit';
var TITLE_CLS = Bar.constants.title = "cp-toolbar-title";
var NEWPAD_CLS = Bar.constants.newpad = "cp-toolbar-new";
var LINK_CLS = Bar.constants.link = "cp-toolbar-link";
2017-08-17 12:55:44 +00:00
// User admin menu
2017-09-04 13:09:54 +00:00
var USERADMIN_CLS = Bar.constants.user = 'cp-toolbar-user-dropdown';
var USERNAME_CLS = Bar.constants.username = 'cp-toolbar-user-name';
/*var READONLY_CLS = */Bar.constants.readonly = 'cp-toolbar-readonly';
var USERBUTTON_CLS = Bar.constants.changeUsername = "cp-toolbar-user-rename";
2017-08-17 12:55:44 +00:00
// Create the toolbar element
var uid = function () {
2017-09-04 13:09:54 +00:00
return 'cp-toolbar-uid-' + String(Math.random()).substring(2);
2017-08-17 12:55:44 +00:00
};
var createRealtimeToolbar = function (config) {
if (!config.$container) { return; }
var $container = config.$container;
var isEmbed = Bar.isEmbed = config.metadataMgr.getPrivateData().isEmbed;
if (isEmbed) {
$container.hide();
}
2017-08-17 12:55:44 +00:00
var $toolbar = $('<div>', {
'class': TOOLBAR_CLS,
id: uid(),
});
var $topContainer = $('<div>', {'class': TOP_CLS});
2017-09-04 13:09:54 +00:00
$('<span>', {'class': 'cp-toolbar-top-filler'}).appendTo($topContainer);
2017-08-17 12:55:44 +00:00
var $userContainer = $('<span>', {
'class': USER_CLS
}).appendTo($topContainer);
$('<span>', {'class': LIMIT_CLS}).hide().appendTo($userContainer);
2017-09-04 13:09:54 +00:00
$('<span>', {'class': NEWPAD_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
$('<span>', {'class': USERADMIN_CLS + ' cp-dropdown-container'}).hide().appendTo($userContainer);
2017-08-17 12:55:44 +00:00
$toolbar.append($topContainer)
.append($('<div>', {'class': LEFTSIDE_CLS}))
.append($('<div>', {'class': RIGHTSIDE_CLS}))
.append($('<div>', {'class': HISTORY_CLS}));
var $rightside = $toolbar.find('.'+RIGHTSIDE_CLS);
if (!config.hideDrawer) {
var $drawerContent = $('<div>', {
2017-09-04 13:09:54 +00:00
'class': DRAWER_CLS,
2017-08-17 12:55:44 +00:00
'tabindex': 1
}).appendTo($rightside).hide();
2017-09-04 13:09:54 +00:00
var $drawer = Common.createButton('more', true).appendTo($rightside);
2017-08-17 12:55:44 +00:00
$drawer.click(function () {
$drawerContent.toggle();
2017-09-04 13:09:54 +00:00
$drawer.removeClass('cp-toolbar-button-active');
2017-08-17 12:55:44 +00:00
if ($drawerContent.is(':visible')) {
2017-09-04 13:09:54 +00:00
$drawer.addClass('cp-toolbar-button-active');
2017-08-17 12:55:44 +00:00
$drawerContent.focus();
}
});
var onBlur = function (e) {
if (e.relatedTarget) {
2017-09-04 13:09:54 +00:00
if ($(e.relatedTarget).is('.cp-toolbar-drawer-button')) { return; }
2017-08-17 12:55:44 +00:00
if ($(e.relatedTarget).parents('.'+DRAWER_CLS).length) {
$(e.relatedTarget).blur(onBlur);
return;
}
}
2017-09-04 13:09:54 +00:00
$drawer.removeClass('cp-toolbar-button-active');
2017-08-17 12:55:44 +00:00
$drawerContent.hide();
};
$drawerContent.blur(onBlur);
2017-08-21 13:20:38 +00:00
}
2017-08-17 12:55:44 +00:00
// The 'notitle' class removes the line added for the title with a small screen
if (!config.title || typeof config.title !== "object") {
2017-09-04 13:09:54 +00:00
$toolbar.addClass('cp-toolbar-notitle');
2017-08-17 12:55:44 +00:00
}
$container.prepend($toolbar);
$container.on('drop dragover', function (e) {
e.preventDefault();
e.stopPropagation();
});
return $toolbar;
};
// Userlist elements
var getOtherUsers = function(config) {
//var userList = config.userList.getUserlist();
2017-08-18 08:47:30 +00:00
var userData = config.metadataMgr.getMetadata().users;
2017-08-17 12:55:44 +00:00
var i = 0; // duplicates counter
var list = [];
// Display only one time each user (if he is connected in multiple tabs)
var uids = [];
Object.keys(userData).forEach(function(user) {
2017-08-17 12:55:44 +00:00
//if (user !== userNetfluxId) {
var data = userData[user] || {};
var userId = data.uid;
if (!userId) { return; }
//data.netfluxId = user;
2017-08-17 12:55:44 +00:00
if (uids.indexOf(userId) === -1) {// && (!myUid || userId !== myUid)) {
uids.push(userId);
list.push(data);
} else { i++; }
//}
});
return {
list: list,
duplicates: i
};
};
2017-08-17 12:55:44 +00:00
var avatars = {};
var updateUserList = function (toolbar, config) {
// Make sure the elements are displayed
var $userButtons = toolbar.userlist;
var $userlistContent = toolbar.userlistContent;
2017-08-18 08:47:30 +00:00
var metadataMgr = config.metadataMgr;
var userData = metadataMgr.getMetadata().users;
var viewers = metadataMgr.getViewers();
var priv = metadataMgr.getPrivateData();
var origin = priv.origin;
var friends = priv.friends;
var user = metadataMgr.getUserData();
2017-08-17 12:55:44 +00:00
// If we are using old pads (readonly unavailable), only editing users are in userList.
// With new pads, we also have readonly users in userList, so we have to intersect with
// the userData to have only the editing users. We can't use userData directly since it
// may contain data about users that have already left the channel.
//userList = config.readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData));
2017-08-17 12:55:44 +00:00
// Names of editing users
var others = getOtherUsers(config);
var editUsersNames = others.list;
var duplicates = others.duplicates; // Number of duplicates
editUsersNames.sort(function (a, b) {
var na = a.name || Messages.anonymous;
var nb = b.name || Messages.anonymous;
return na.toLowerCase() > nb.toLowerCase();
});
var numberOfEditUsers = Object.keys(userData).length - duplicates;
var numberOfViewUsers = viewers;
2017-08-17 12:55:44 +00:00
// Update the userlist
var $editUsers = $userlistContent.find('.' + USERLIST_CLS).html('');
Cryptpad.clearTooltips();
2017-09-04 13:09:54 +00:00
var $editUsersList = $('<div>', {'class': 'cp-toolbar-userlist-others'});
2017-08-17 12:55:44 +00:00
// Editors
var pendingFriends = Common.getPendingFriends();
2017-08-17 12:55:44 +00:00
editUsersNames.forEach(function (data) {
var name = data.name || Messages.anonymous;
2017-09-04 13:09:54 +00:00
var $span = $('<span>', {'class': 'cp-avatar'});
var $rightCol = $('<span>', {'class': 'cp-toolbar-userlist-rightcol'});
var $nameSpan = $('<span>', {'class': 'cp-toolbar-userlist-name'}).text(name).appendTo($rightCol);
var isMe = data.curvePublic === user.curvePublic;
if (Common.isLoggedIn() && data.curvePublic) {
2017-08-17 12:55:44 +00:00
if (isMe) {
$span.attr('title', Messages._getKey('userlist_thisIsYou', [
name
]));
$nameSpan.text(name);
} else if (!friends[data.curvePublic]) {
2017-08-17 12:55:44 +00:00
if (pendingFriends.indexOf(data.netfluxId) !== -1) {
2017-09-04 13:09:54 +00:00
$('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending)
2017-08-17 12:55:44 +00:00
.appendTo($rightCol);
} else {
$('<span>', {
2017-09-04 13:09:54 +00:00
'class': 'fa fa-user-plus cp-toolbar-userlist-friend',
2017-08-17 12:55:44 +00:00
'title': Messages._getKey('userlist_addAsFriendTitle', [
name
])
}).appendTo($rightCol).click(function (e) {
e.stopPropagation();
Common.sendFriendRequest(data.netfluxId);
2017-08-17 12:55:44 +00:00
});
}
}
}
2017-08-17 12:55:44 +00:00
if (data.profile) {
2017-09-04 13:09:54 +00:00
$span.addClass('cp-userlist-clickable');
2017-08-17 12:55:44 +00:00
$span.click(function () {
2017-08-22 16:18:48 +00:00
window.open(origin+'/profile/#' + data.profile);
2017-08-17 12:55:44 +00:00
});
}
if (data.avatar && avatars[data.avatar]) {
$span.append(avatars[data.avatar]);
$span.append($rightCol);
} else {
2017-08-21 10:01:38 +00:00
Common.displayAvatar(Common, $span, data.avatar, name, function ($img) {
2017-08-17 12:55:44 +00:00
if (data.avatar && $img) {
avatars[data.avatar] = $img[0].outerHTML;
}
$span.append($rightCol);
});
}
$span.data('uid', data.uid);
$editUsersList.append($span);
});
$editUsers.append($editUsersList);
// Viewers
if (numberOfViewUsers > 0) {
2017-09-04 13:09:54 +00:00
var viewText = '<div class="cp-toolbar-userlist-viewer">';
2017-08-17 12:55:44 +00:00
var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
viewText += numberOfViewUsers + ' ' + viewerText + '</div>';
$editUsers.append(viewText);
}
// Update the buttons
var fa_editusers = '<span class="fa fa-users"></span>';
var fa_viewusers = '<span class="fa fa-eye"></span>';
var $spansmall = $('<span>').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + fa_viewusers + ' ' + numberOfViewUsers);
2017-09-04 13:09:54 +00:00
$userButtons.find('.cp-dropdown-button-title').html('').append($spansmall);
2017-08-17 12:55:44 +00:00
};
var initUserList = function (toolbar, config) {
// TODO clean comments
2017-08-18 08:47:30 +00:00
if (config.metadataMgr) { /* && config.userList.list && config.userList.userNetfluxId) {*/
2017-08-17 12:55:44 +00:00
//var userList = config.userList.list;
//userList.change.push
2017-08-18 08:47:30 +00:00
var metadataMgr = config.metadataMgr;
2017-08-17 12:55:44 +00:00
metadataMgr.onChange(function () {
if (metadataMgr.isConnected()) {toolbar.connected = true;}
2017-08-17 12:55:44 +00:00
if (!toolbar.connected) { return; }
//if (config.userList.data) {
updateUserList(toolbar, config);
//}
});
}
};
// Create sub-elements
var createUserList = function (toolbar, config) {
if (!config.metadataMgr) {
2017-08-18 08:47:30 +00:00
throw new Error("You must provide a `metadataMgr` to display the userlist");
2017-08-17 12:55:44 +00:00
}
2017-09-04 13:09:54 +00:00
var $content = $('<div>', {'class': 'cp-toolbar-userlist-drawer'});
2017-08-17 12:55:44 +00:00
$content.on('drop dragover', function (e) {
e.preventDefault();
e.stopPropagation();
});
2017-09-04 13:09:54 +00:00
var $closeIcon = $('<span>', {"class": "fa fa-window-close cp-toolbar-userlist-drawer-close"}).appendTo($content);
2017-08-17 12:55:44 +00:00
$('<h2>').text(Messages.users).appendTo($content);
$('<p>', {'class': USERLIST_CLS}).appendTo($content);
toolbar.userlistContent = $content;
2017-09-04 13:09:54 +00:00
var $container = $('<span>', {id: 'cp-toolbar-userlist-drawer-open', title: Messages.userListButton});
2017-08-17 12:55:44 +00:00
var $button = $('<button>').appendTo($container);
2017-09-04 13:09:54 +00:00
$('<span>',{'class': 'cp-dropdown-button-title'}).appendTo($button);
2017-08-17 12:55:44 +00:00
toolbar.$leftside.prepend($container);
if (config.$contentContainer) {
config.$contentContainer.prepend($content);
}
var $ck = config.$container.find('.cke_toolbox_main');
var mobile = $('body').width() <= 600;
var hide = function () {
$content.hide();
2017-09-04 13:09:54 +00:00
$button.removeClass('cp-toolbar-button-active');
2017-08-17 12:55:44 +00:00
$ck.css({
'padding-left': '',
});
};
var show = function () {
if (Bar.isEmbed) { $content.hide(); return; }
2017-08-17 12:55:44 +00:00
$content.show();
if (mobile) {
$ck.hide();
}
2017-09-04 13:09:54 +00:00
$button.addClass('cp-toolbar-button-active');
2017-08-17 12:55:44 +00:00
$ck.css({
'padding-left': '175px',
});
var h = $ck.is(':visible') ? -$ck.height() : 0;
$content.css('margin-top', h+'px');
};
$(window).on('cryptpad-ck-toolbar', function () {
if (mobile && $ck.is(':visible')) { return void hide(); }
if ($content.is(':visible')) { return void show(); }
hide();
});
$(window).on('resize', function () {
mobile = $('body').width() <= 600;
var h = $ck.is(':visible') ? -$ck.height() : 0;
$content.css('margin-top', h+'px');
});
$closeIcon.click(function () {
2017-08-18 09:07:42 +00:00
//Cryptpad.setAttribute('userlist-drawer', false); TODO iframe
2017-08-17 12:55:44 +00:00
hide();
});
$button.click(function () {
var visible = $content.is(':visible');
if (visible) { hide(); }
else { show(); }
visible = !visible;
// TODO iframe
//Cryptpad.setAttribute('userlist-drawer', visible);
2017-08-18 09:07:42 +00:00
Common.feedback(visible?'USERLIST_SHOW': 'USERLIST_HIDE');
2017-08-17 12:55:44 +00:00
});
2017-08-18 09:07:42 +00:00
show();
2017-08-17 12:55:44 +00:00
// TODO iframe
/*Cryptpad.getAttribute('userlist-drawer', function (err, val) {
if (val === false || mobile) { return void hide(); }
show();
});*/
return $container;
};
var createShare = function (toolbar, config) {
if (!config.metadataMgr) {
throw new Error("You must provide a `metadataMgr` to display the userlist");
2017-08-17 12:55:44 +00:00
}
var metadataMgr = config.metadataMgr;
2017-08-18 16:43:04 +00:00
var origin = config.metadataMgr.getPrivateData().origin;
var pathname = config.metadataMgr.getPrivateData().pathname;
var hashes = metadataMgr.getPrivateData().availableHashes;
var readOnly = metadataMgr.getPrivateData().readOnly;
2017-08-18 16:43:04 +00:00
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
var options = [];
2017-08-17 12:55:44 +00:00
if (hashes.editHash) {
options.push({
tag: 'a',
2017-09-04 13:09:54 +00:00
attributes: {title: Messages.editShareTitle, 'class': 'cp-toolbar-share-edit-copy'},
content: '<span class="fa fa-users"></span> ' + Messages.editShare
});
if (readOnly) {
// We're in view mode, display the "open editing link" button
2017-08-17 12:55:44 +00:00
options.push({
tag: 'a',
attributes: {
title: Messages.editOpenTitle,
2017-09-04 13:09:54 +00:00
'class': 'cp-toolbar-share-edit-open',
2017-08-18 16:43:04 +00:00
href: origin + pathname + '#' + hashes.editHash,
target: '_blank'
},
content: '<span class="fa fa-users"></span> ' + Messages.editOpen
2017-08-17 12:55:44 +00:00
});
}
options.push({tag: 'hr'});
}
if (hashes.viewHash) {
options.push({
tag: 'a',
2017-09-04 13:09:54 +00:00
attributes: {title: Messages.viewShareTitle, 'class': 'cp-toolbar-share-view-copy'},
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
});
if (!readOnly) {
// We're in edit mode, display the "open readonly" button
2017-08-17 12:55:44 +00:00
options.push({
tag: 'a',
attributes: {
title: Messages.viewOpenTitle,
2017-09-04 13:09:54 +00:00
'class': 'cp-toolbar-share-view-open',
2017-08-18 16:43:04 +00:00
href: origin + pathname + '#' + hashes.viewHash,
target: '_blank'
},
content: '<span class="fa fa-eye"></span> ' + Messages.viewOpen
2017-08-17 12:55:44 +00:00
});
}
}
var dropdownConfigShare = {
text: $('<div>').append($shareIcon).html(),
options: options,
feedback: 'SHARE_MENU',
};
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
2017-09-04 13:09:54 +00:00
$shareBlock.find('.cp-dropdown-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
$shareBlock.addClass('cp-toolbar-share-button');
$shareBlock.find('button').attr('title', Messages.shareButton);
if (hashes.editHash) {
2017-09-04 13:09:54 +00:00
$shareBlock.find('a.cp-toolbar-share-edit-copy').click(function () {
2017-08-18 16:43:04 +00:00
/*Common.storeLinkToClipboard(false, function (err) {
if (!err) { Cryptpad.log(Messages.shareSuccess); }
2017-08-18 16:43:04 +00:00
});*/
var url = origin + pathname + '#' + hashes.editHash;
var success = Cryptpad.Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); }
});
}
if (hashes.viewHash) {
2017-09-04 13:09:54 +00:00
$shareBlock.find('a.cp-toolbar-share-view-copy').click(function () {
2017-08-18 16:43:04 +00:00
/*Common.storeLinkToClipboard(true, function (err) {
if (!err) { Cryptpad.log(Messages.shareSuccess); }
2017-08-18 16:43:04 +00:00
});*/
var url = origin + pathname + '#' + hashes.viewHash;
var success = Cryptpad.Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); }
});
}
2017-08-17 12:55:44 +00:00
toolbar.$leftside.append($shareBlock);
toolbar.share = $shareBlock;
2017-08-17 12:55:44 +00:00
return "Loading share button";
};
var createFileShare = function (toolbar) {
if (true) { throw new Error('TODO: Update createFileShare to add "embed" and work in secure iframes'); }
2017-08-17 12:55:44 +00:00
if (!window.location.hash) {
throw new Error("Unable to display the share button: hash required in the URL");
}
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
var $button = $('<button>', {'title': Messages.shareButton}).append($shareIcon);
2017-09-04 13:09:54 +00:00
$button.addClass('cp-toolbar-share-button');
2017-08-17 12:55:44 +00:00
$button.click(function () {
var url = window.location.href;
var success = Cryptpad.Clipboard.copy(url);
if (success) { Cryptpad.log(Messages.shareSuccess); }
});
toolbar.$leftside.append($button);
return $button;
};
var createTitle = function (toolbar, config) {
var $titleContainer = $('<span>', {
'class': TITLE_CLS
}).appendTo(toolbar.$top);
2017-09-04 13:09:54 +00:00
var $hoverable = $('<span>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);
2017-08-17 12:55:44 +00:00
if (typeof config.title !== "object") {
console.error("config.title", config);
throw new Error("config.title is not an object");
}
2017-08-17 17:01:03 +00:00
var updateTitle = config.title.updateTitle;
2017-08-17 12:55:44 +00:00
var placeholder = config.title.defaultName;
var suggestName = config.title.suggestName;
// Buttons
var $text = $('<span>', {
2017-09-04 13:09:54 +00:00
'class': 'cp-toolbar-title-value'
2017-08-17 12:55:44 +00:00
}).appendTo($hoverable);
var $pencilIcon = $('<span>', {
2017-09-04 13:09:54 +00:00
'class': 'cp-toolbar-title-edit',
2017-08-17 12:55:44 +00:00
'title': Messages.clickToEdit
});
var $saveIcon = $('<span>', {
2017-09-04 13:09:54 +00:00
'class': 'cp-toolbar-title-save',
2017-08-17 12:55:44 +00:00
'title': Messages.saveTitle
}).hide();
if (config.readOnly === 1) {
2017-09-04 13:09:54 +00:00
$titleContainer.append($('<span>', {'class': 'cp-toolbar-title-readonly'})
2017-08-17 12:55:44 +00:00
.text('('+Messages.readonly+')'));
}
if (config.readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; }
var $input = $('<input>', {
type: 'text',
placeholder: placeholder
}).appendTo($hoverable).hide();
if (config.readOnly !== 1) {
$text.attr("title", Messages.clickToEdit);
2017-09-04 13:09:54 +00:00
$text.addClass("cp-toolbar-title-editable");
2017-08-17 12:55:44 +00:00
var $icon = $('<span>', {
2017-09-04 13:09:54 +00:00
'class': 'fa fa-pencil cp-toolbar-title-icon-readonly',
2017-08-17 12:55:44 +00:00
style: 'font-family: FontAwesome;'
});
$pencilIcon.append($icon).appendTo($hoverable);
var $icon2 = $('<span>', {
2017-09-04 13:09:54 +00:00
'class': 'fa fa-check cp-toolbar-title-icon-readonly',
2017-08-17 12:55:44 +00:00
style: 'font-family: FontAwesome;'
});
$saveIcon.append($icon2).appendTo($hoverable);
}
// Events
$input.on('mousedown', function (e) {
if (!$input.is(":focus")) {
$input.focus();
}
e.stopPropagation();
return true;
});
var save = function () {
var name = $input.val().trim();
if (name === "") {
name = $input.attr('placeholder');
}
2017-08-23 09:04:44 +00:00
updateTitle(name, function (err/*, newtitle*/) {
2017-08-17 12:55:44 +00:00
if (err) { return console.error(err); }
2017-08-17 17:01:03 +00:00
//$text.text(newtitle);
2017-08-17 12:55:44 +00:00
$input.hide();
$text.show();
$pencilIcon.show();
$saveIcon.hide();
});
};
$input.on('keyup', function (e) {
if (e.which === 13 && toolbar.connected === true) {
save();
} else if (e.which === 27) {
$input.hide();
$text.show();
$pencilIcon.show();
$saveIcon.hide();
//$pencilIcon.css('display', '');
} else if (e.which === 32) {
e.stopPropagation();
}
});
$saveIcon.click(save);
var displayInput = function () {
if (toolbar.connected === false) { return; }
$input.width(Math.max($text.width(), 300)+'px');
$text.hide();
//$pencilIcon.css('display', 'none');
var inputVal = suggestName() || "";
$input.val(inputVal);
$input.show();
$input.focus();
$pencilIcon.hide();
$saveIcon.show();
};
$text.on('click', displayInput);
$pencilIcon.on('click', displayInput);
return $titleContainer;
};
var createPageTitle = function (toolbar, config) {
if (config.title || !config.pageTitle) { return; }
var $titleContainer = $('<span>', {
'class': TITLE_CLS
}).appendTo(toolbar.$top);
toolbar.$top.find('.filler').hide();
2017-09-04 13:09:54 +00:00
var $hoverable = $('<span>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);
2017-08-17 12:55:44 +00:00
// Buttons
$('<span>', {
2017-09-04 13:09:54 +00:00
'class': 'cp-toolbar-title-value cp-toolbar-title-value-page'
2017-08-17 12:55:44 +00:00
}).appendTo($hoverable).text(config.pageTitle);
};
2017-08-28 12:18:33 +00:00
var createLinkToMain = function (toolbar, config) {
2017-08-17 12:55:44 +00:00
var $linkContainer = $('<span>', {
2017-09-04 13:09:54 +00:00
'class': LINK_CLS
2017-08-17 12:55:44 +00:00
}).appendTo(toolbar.$top);
// We need to override the "a" tag action here because it is inside the iframe!
var inDrive = /^\/drive/;
2017-08-28 12:18:33 +00:00
var origin = config.metadataMgr.getPrivateData().origin;
2017-08-29 08:37:19 +00:00
var href = inDrive.test(origin) ? origin+'/index.html' : origin+'/drive/';
2017-08-17 12:55:44 +00:00
var buttonTitle = inDrive ? Messages.header_homeTitle : Messages.header_logoTitle;
var $aTag = $('<a>', {
href: href,
title: buttonTitle,
2017-09-04 13:09:54 +00:00
'class': "cp-toolbar-link-logo"
2017-08-17 12:55:44 +00:00
}).append($('<img>', {
2017-08-22 13:50:10 +00:00
src: '/customize/images/logo_white.png?' + ApiConfig.requireConf.urlArgs
2017-08-17 12:55:44 +00:00
}));
var onClick = function (e) {
e.preventDefault();
if (e.ctrlKey) {
window.open(href);
return;
}
2017-08-28 12:18:33 +00:00
window.top.location = href;
2017-08-17 12:55:44 +00:00
};
var onContext = function (e) { e.stopPropagation(); };
$aTag.click(onClick).contextmenu(onContext);
$linkContainer.append($aTag);
return $linkContainer;
};
var typing = -1;
var kickSpinner = function (toolbar, config, local) {
if (!toolbar.spinner) { return; }
var $spin = toolbar.spinner;
if (typing === -1) {
typing = 1;
$spin.text(Messages.typing);
$spin.interval = window.setInterval(function () {
var dots = Array(typing+1).join('.');
$spin.text(Messages.typing + dots);
typing++;
if (typing > 3) { typing = 0; }
}, 500);
}
var onSynced = function () {
if ($spin.timeout) { clearTimeout($spin.timeout); }
$spin.timeout = setTimeout(function () {
window.clearInterval($spin.interval);
typing = -1;
$spin.text(Messages.saved);
}, local ? 0 : SPINNER_DISAPPEAR_TIME);
};
if (Cryptpad) {
Cryptpad.whenRealtimeSyncs(config.realtime, onSynced);
return;
}
onSynced();
};
var ks = function (toolbar, config, local) {
return function () {
if (toolbar.connected) { kickSpinner(toolbar, config, local); }
};
};
var createSpinner = function (toolbar, config) {
var $spin = $('<span>', {'class': SPINNER_CLS}).appendTo(toolbar.$leftside);
$spin.text(Messages.synchronizing);
if (config.realtime) {
config.realtime.onPatch(ks(toolbar, config));
config.realtime.onMessage(ks(toolbar, config, true));
}
// without this, users in read-only mode say 'synchronizing' until they
// receive a patch.
if (Cryptpad) {
typing = 0;
Cryptpad.whenRealtimeSyncs(config.realtime, function () {
kickSpinner(toolbar, config);
});
}
return $spin;
};
var createLimit = function (toolbar) {
if (!Config.enablePinning) { return; }
var $limitIcon = $('<span>', {'class': 'fa fa-exclamation-triangle'});
var $limit = toolbar.$userAdmin.find('.'+LIMIT_CLS).attr({
'title': Messages.pinLimitReached
}).append($limitIcon).hide();
var todo = function (e, overLimit) {
if (e) { return void console.error("Unable to get the pinned usage"); }
if (overLimit) {
var key = 'pinLimitReachedAlert';
if (ApiConfig.noSubscriptionButton === true) {
key = 'pinLimitReachedAlertNoAccounts';
}
$limit.show().click(function () {
Cryptpad.alert(Messages._getKey(key, [encodeURIComponent(window.location.hostname)]), null, true);
});
}
};
2017-08-21 10:24:33 +00:00
Common.isOverPinLimit(todo);
2017-08-17 12:55:44 +00:00
return $limit;
};
var createNewPad = function (toolbar, config) {
2017-08-17 12:55:44 +00:00
var $newPad = toolbar.$top.find('.'+NEWPAD_CLS).show();
var origin = config.metadataMgr.getPrivateData().origin;
2017-08-17 12:55:44 +00:00
var pads_options = [];
Config.availablePadTypes.forEach(function (p) {
if (p === 'drive') { return; }
if (!Common.isLoggedIn() && Config.registeredOnlyTypes &&
2017-08-17 12:55:44 +00:00
Config.registeredOnlyTypes.indexOf(p) !== -1) { return; }
pads_options.push({
tag: 'a',
attributes: {
'target': '_blank',
'href': origin + '/' + p + '/',
2017-08-17 12:55:44 +00:00
},
content: $('<div>').append(Cryptpad.getIcon(p)).html() + Messages.type[p]
});
});
var dropdownConfig = {
text: '', // Button initial text
options: pads_options, // Entries displayed in the menu
container: $newPad,
left: true,
feedback: /drive/.test(window.location.pathname)?
'DRIVE_NEWPAD': 'NEWPAD',
};
var $newPadBlock = Cryptpad.createDropdown(dropdownConfig);
$newPadBlock.find('button').attr('title', Messages.newButtonTitle);
$newPadBlock.find('button').addClass('fa fa-th');
return $newPadBlock;
};
var createUserAdmin = function (toolbar, config) {
2017-08-18 09:07:42 +00:00
if (!config.metadataMgr) {
throw new Error("You must provide a `metadataMgr` to display the user menu");
}
var metadataMgr = config.metadataMgr;
2017-08-17 12:55:44 +00:00
var $userAdmin = toolbar.$userAdmin.find('.'+USERADMIN_CLS).show();
var userMenuCfg = {
$initBlock: $userAdmin,
metadataMgr: metadataMgr,
Common: Common
2017-08-17 12:55:44 +00:00
};
2017-08-17 15:26:20 +00:00
if (!config.hideDisplayName) {
2017-08-17 12:55:44 +00:00
$.extend(true, userMenuCfg, {
displayNameCls: USERNAME_CLS,
changeNameButtonCls: USERBUTTON_CLS,
});
}
if (config.readOnly !== 1) {
userMenuCfg.displayName = 1;
userMenuCfg.displayChangeName = 1;
}
Common.createUserAdminMenu(userMenuCfg);
2017-08-17 12:55:44 +00:00
$userAdmin.find('button').attr('title', Messages.userAccountButton);
2017-08-18 08:47:30 +00:00
var $userButton = toolbar.$userNameButton = $userAdmin.find('a.' + USERBUTTON_CLS);
2017-08-17 12:55:44 +00:00
$userButton.click(function (e) {
e.preventDefault();
e.stopPropagation();
var myData = metadataMgr.getMetadata().users[metadataMgr.getNetfluxId()];
var lastName = myData.name;
Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
if (newName === null && typeof(lastName) === "string") { return; }
if (newName === null) { newName = ''; }
else { Common.feedback('NAME_CHANGED'); }
Common.setDisplayName(newName, function (err) {
if (err) {
console.log("Couldn't set username");
console.error(err);
return;
}
//Cryptpad.changeDisplayName(newName, true); Already done?
2017-08-17 12:55:44 +00:00
});
});
2017-08-18 08:47:30 +00:00
});
2017-08-17 12:55:44 +00:00
return $userAdmin;
};
// Events
var initClickEvents = function (toolbar, config) {
var removeDropdowns = function () {
2017-08-18 16:43:04 +00:00
window.setTimeout(function () {
2017-09-04 13:09:54 +00:00
toolbar.$toolbar.find('.cp-dropdown-content').hide();
2017-08-18 16:43:04 +00:00
});
2017-08-17 12:55:44 +00:00
};
var cancelEditTitle = function (e) {
// Now we want to apply the title even if we click somewhere else
if ($(e.target).parents('.' + TITLE_CLS).length || !toolbar.title) {
return;
}
var $title = toolbar.title;
if (!$title.find('input').is(':visible')) { return; }
// Press enter
var ev = $.Event("keyup");
ev.which = 13;
$title.find('input').trigger(ev);
};
// Click in the main window
var w = config.ifrw || window;
$(w).on('click', removeDropdowns);
$(w).on('click', cancelEditTitle);
// Click in iframes
try {
if (w.$ && w.$('iframe').length) {
config.ifrw.$('iframe').each(function (i, el) {
$(el.contentWindow).on('click', removeDropdowns);
$(el.contentWindow).on('click', cancelEditTitle);
});
}
} catch (e) {
// empty try catch in case this iframe is problematic
}
};
// Notifications
var initNotifications = function (toolbar, config) {
// Display notifications when users are joining/leaving the session
var oldUserData;
2017-08-18 08:47:30 +00:00
if (!config.metadataMgr) { return; }
var metadataMgr = config.metadataMgr;
var userNetfluxId = metadataMgr.getNetfluxId();
if (typeof Cryptpad !== "undefined") {
2017-08-17 12:55:44 +00:00
var notify = function(type, name, oldname) {
// type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
if (typeof name === "undefined") { return; }
name = name || Messages.anonymous;
switch(type) {
case 1:
Cryptpad.log(Messages._getKey("notifyJoined", [name]));
break;
case 0:
oldname = (!oldname) ? Messages.anonymous : oldname;
2017-08-17 12:55:44 +00:00
Cryptpad.log(Messages._getKey("notifyRenamed", [oldname, name]));
break;
case -1:
Cryptpad.log(Messages._getKey("notifyLeft", [name]));
break;
default:
console.log("Invalid type of notification");
break;
}
};
var userPresent = function (id, user, data) {
if (!(user && user.uid)) {
console.log('no uid');
return 0;
}
if (!data) {
console.log('no data');
return 0;
}
var count = 0;
Object.keys(data).forEach(function (k) {
if (data[k] && data[k].uid === user.uid) { count++; }
});
return count;
};
2017-08-18 08:47:30 +00:00
metadataMgr.onChange(function () {
var newdata = metadataMgr.getMetadata().users;
var netfluxIds = Object.keys(newdata);
2017-08-17 12:55:44 +00:00
// Notify for disconnected users
if (typeof oldUserData !== "undefined") {
for (var u in oldUserData) {
// if a user's uid is still present after having left, don't notify
2017-08-18 08:47:30 +00:00
if (netfluxIds.indexOf(u) === -1) {
2017-08-17 12:55:44 +00:00
var temp = JSON.parse(JSON.stringify(oldUserData[u]));
delete oldUserData[u];
if (temp && newdata[userNetfluxId] && temp.uid === newdata[userNetfluxId].uid) { return; }
2017-08-17 12:55:44 +00:00
if (userPresent(u, temp, newdata || oldUserData) < 1) {
notify(-1, temp.name);
}
}
}
}
// Update the "oldUserData" object and notify for new users and names changed
if (typeof newdata === "undefined") { return; }
if (typeof oldUserData === "undefined") {
oldUserData = JSON.parse(JSON.stringify(newdata));
return;
}
if (config.readOnly === 0 && !oldUserData[userNetfluxId]) {
oldUserData = JSON.parse(JSON.stringify(newdata));
return;
}
for (var k in newdata) {
2017-08-18 08:47:30 +00:00
if (k !== userNetfluxId && netfluxIds.indexOf(k) !== -1) {
2017-08-17 12:55:44 +00:00
if (typeof oldUserData[k] === "undefined") {
// if the same uid is already present in the userdata, don't notify
if (!userPresent(k, newdata[k], oldUserData)) {
notify(1, newdata[k].name);
}
} else if (oldUserData[k].name !== newdata[k].name) {
notify(0, newdata[k].name, oldUserData[k].name);
}
}
}
oldUserData = JSON.parse(JSON.stringify(newdata));
});
}
};
// Main
Bar.create = function (cfg) {
var config = cfg || {};
Cryptpad = config.common;
2017-08-17 16:28:05 +00:00
Common = config.sfCommon;
2017-08-17 12:55:44 +00:00
Messages = Cryptpad.Messages;
config.readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
config.displayed = config.displayed || [];
var toolbar = {};
toolbar.connected = false;
toolbar.firstConnection = true;
var $toolbar = toolbar.$toolbar = createRealtimeToolbar(config);
toolbar.$leftside = $toolbar.find('.'+Bar.constants.leftside);
toolbar.$rightside = $toolbar.find('.'+Bar.constants.rightside);
toolbar.$drawer = $toolbar.find('.'+Bar.constants.drawer);
toolbar.$top = $toolbar.find('.'+Bar.constants.top);
toolbar.$history = $toolbar.find('.'+Bar.constants.history);
toolbar.$userAdmin = $toolbar.find('.'+Bar.constants.userAdmin);
// Create the subelements
var tb = {};
tb['userlist'] = createUserList;
2017-08-21 09:08:27 +00:00
tb['share'] = createShare;
tb['fileshare'] = createFileShare;//TODO
2017-08-17 12:55:44 +00:00
tb['title'] = createTitle;
tb['pageTitle'] = createPageTitle;//TODO
2017-08-17 12:55:44 +00:00
tb['lag'] = $.noop;
tb['spinner'] = createSpinner;
tb['state'] = $.noop;
tb['limit'] = createLimit; // TODO
2017-08-17 12:55:44 +00:00
tb['upgrade'] = $.noop;
tb['newpad'] = createNewPad;
tb['useradmin'] = createUserAdmin;
var addElement = toolbar.addElement = function (arr, additionnalCfg, init) {
if (typeof additionnalCfg === "object") { $.extend(true, config, additionnalCfg); }
arr.forEach(function (el) {
if (typeof el !== "string" || !el.trim()) { return; }
if (typeof tb[el] === "function") {
if (!init && config.displayed.indexOf(el) !== -1) { return; } // Already done
toolbar[el] = tb[el](toolbar, config);
if (!init) { config.displayed.push(el); }
}
});
};
addElement(config.displayed, {}, true);
initUserList(toolbar, config);
toolbar['linkToMain'] = createLinkToMain(toolbar, config);
if (!config.realtime) { toolbar.connected = true; }
initClickEvents(toolbar, config);
initNotifications(toolbar, config);
var failed = toolbar.failed = function () {
toolbar.connected = false;
if (toolbar.spinner) {
toolbar.spinner.text(Messages.disconnected);
}
//checkLag(toolbar, config);
};
2017-08-23 09:04:44 +00:00
toolbar.reconnecting = function (/*userId*/) {
2017-08-18 08:47:30 +00:00
//if (config.metadataMgr) { config.userList.userNetfluxId = userId; } TODO
2017-08-17 12:55:44 +00:00
toolbar.connected = false;
if (toolbar.spinner) {
toolbar.spinner.text(Messages.reconnecting);
}
//checkLag(toolbar, config);
};
// On log out, remove permanently the realtime elements of the toolbar
Cryptpad.onLogout(function () {
failed();
if (toolbar.useradmin) { toolbar.useradmin.hide(); }
if (toolbar.userlist) { toolbar.userlist.hide(); }
});
return toolbar;
};
return Bar;
});