Separate UI elements from cryptpad-common
parent
354c63bd64
commit
dc207393fd
@ -1,865 +0,0 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/customize/application_config.js',
|
||||
'/api/config'
|
||||
], function ($, Config, ApiConfig) {
|
||||
|
||||
var urlArgs = ApiConfig.requireConf.urlArgs;
|
||||
|
||||
var Messages = {};
|
||||
|
||||
var Bar = {
|
||||
constants: {},
|
||||
};
|
||||
|
||||
/** Id of the div containing the user list. */
|
||||
var USER_LIST_CLS = Bar.constants.userlist = 'cryptpad-user-list';
|
||||
|
||||
/** Id of the div containing the lag info. */
|
||||
var LAG_ELEM_CLS = Bar.constants.lag = 'cryptpad-lag';
|
||||
|
||||
var LIMIT_ELEM_CLS = Bar.constants.lag = 'cryptpad-limit';
|
||||
|
||||
/** The toolbar class which contains the user list, debug link and lag. */
|
||||
var TOOLBAR_CLS = Bar.constants.toolbar = 'cryptpad-toolbar';
|
||||
|
||||
var TOP_CLS = Bar.constants.top = 'cryptpad-toolbar-top';
|
||||
var LEFTSIDE_CLS = Bar.constants.leftside = 'cryptpad-toolbar-leftside';
|
||||
var RIGHTSIDE_CLS = Bar.constants.rightside = 'cryptpad-toolbar-rightside';
|
||||
var HISTORY_CLS = Bar.constants.history = 'cryptpad-toolbar-history';
|
||||
|
||||
var SPINNER_CLS = Bar.constants.spinner = 'cryptpad-spinner';
|
||||
|
||||
var STATE_CLS = Bar.constants.state = 'cryptpad-state';
|
||||
|
||||
var USERNAME_CLS = Bar.constants.username = 'cryptpad-toolbar-username';
|
||||
|
||||
var READONLY_CLS = Bar.constants.readonly = 'cryptpad-readonly';
|
||||
|
||||
var USERBUTTONS_CONTAINER_CLS = Bar.constants.userButtonsContainer = "cryptpad-userbuttons-container";
|
||||
var USERLIST_CLS = Bar.constants.userlist = "cryptpad-dropdown-users";
|
||||
var EDITSHARE_CLS = Bar.constants.editShare = "cryptpad-dropdown-editShare";
|
||||
var VIEWSHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-viewShare";
|
||||
var SHARE_CLS = Bar.constants.viewShare = "cryptpad-dropdown-share";
|
||||
var DROPDOWN_CONTAINER_CLS = Bar.constants.dropdownContainer = "cryptpad-dropdown-container";
|
||||
var DROPDOWN_CLS = Bar.constants.dropdown = "cryptpad-dropdown";
|
||||
var TITLE_CLS = Bar.constants.title = "cryptpad-title";
|
||||
var USER_CLS = Bar.constants.userAdmin = "cryptpad-user";
|
||||
var USERBUTTON_CLS = Bar.constants.changeUsername = "cryptpad-change-username";
|
||||
|
||||
var SPINNER_DISAPPEAR_TIME = 3000;
|
||||
|
||||
var uid = function () {
|
||||
return 'cryptpad-uid-' + String(Math.random()).substring(2);
|
||||
};
|
||||
|
||||
var $style;
|
||||
|
||||
var connected = false;
|
||||
var firstConnection = true;
|
||||
var lagErrors = 0;
|
||||
|
||||
var styleToolbar = function ($container, href, version) {
|
||||
href = href || '/customize/toolbar.css' + (version?('?' + version): '');
|
||||
|
||||
$.ajax({
|
||||
url: href,
|
||||
dataType: 'text',
|
||||
success: function (data) {
|
||||
$container.append($('<style>').text(data));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
var createRealtimeToolbar = function ($container, config) {
|
||||
var $toolbar = $('<div>', {
|
||||
'class': TOOLBAR_CLS,
|
||||
id: uid(),
|
||||
})
|
||||
.append($('<div>', {'class': TOP_CLS}))
|
||||
.append($('<div>', {'class': LEFTSIDE_CLS}))
|
||||
.append($('<div>', {'class': RIGHTSIDE_CLS}))
|
||||
.append($('<div>', {'class': HISTORY_CLS}));
|
||||
|
||||
// The 'notitle' class removes the line added for the title with a small screen
|
||||
if (!config || typeof config !== "object") {
|
||||
$toolbar.addClass('notitle');
|
||||
}
|
||||
|
||||
$container.prepend($toolbar);
|
||||
|
||||
if (ApiConfig && ApiConfig.requireConf && ApiConfig.requireConf.urlArgs) {
|
||||
styleToolbar($container, undefined, ApiConfig.requireConf.urlArgs);
|
||||
} else {
|
||||
styleToolbar($container);
|
||||
}
|
||||
return $toolbar;
|
||||
};
|
||||
|
||||
var createSpinner = function ($container, config) {
|
||||
if (config.displayed.indexOf('spinner') !== -1) {
|
||||
var $spin = $('<span>', {'class':SPINNER_CLS});
|
||||
var $spinner = $('<span>', {
|
||||
id: uid(),
|
||||
'class': 'spin fa fa-spinner fa-pulse',
|
||||
}).appendTo($spin).hide();
|
||||
$('<span>', {
|
||||
id: uid(),
|
||||
'class': 'synced fa fa-check',
|
||||
title: Messages.synced
|
||||
}).appendTo($spin);
|
||||
$container.prepend($spin);
|
||||
return $spin[0];
|
||||
}
|
||||
};
|
||||
|
||||
var kickSpinner = function (Cryptpad, realtime, local, spinnerElement) {
|
||||
if (!spinnerElement) { return; }
|
||||
$(spinnerElement).find('.spin').show();
|
||||
$(spinnerElement).find('.synced').hide();
|
||||
var onSynced = function () {
|
||||
if (spinnerElement.timeout) { clearTimeout(spinnerElement.timeout); }
|
||||
spinnerElement.timeout = setTimeout(function () {
|
||||
$(spinnerElement).find('.spin').hide();
|
||||
$(spinnerElement).find('.synced').show();
|
||||
}, local ? 0 : SPINNER_DISAPPEAR_TIME);
|
||||
};
|
||||
if (Cryptpad) {
|
||||
Cryptpad.whenRealtimeSyncs(realtime, onSynced);
|
||||
return;
|
||||
}
|
||||
onSynced();
|
||||
};
|
||||
|
||||
var createUserButtons = function ($userlistElement, config, readOnly, Cryptpad) {
|
||||
// User list button
|
||||
if (config.displayed.indexOf('userlist') !== -1) {
|
||||
if (!config.userData) {
|
||||
throw new Error("You must provide a `userData` object to display the userlist");
|
||||
}
|
||||
var dropdownConfig = {
|
||||
options: [{
|
||||
tag: 'p',
|
||||
attributes: {'class': USERLIST_CLS},
|
||||
}] // Entries displayed in the menu
|
||||
};
|
||||
var $block = Cryptpad.createDropdown(dropdownConfig);
|
||||
$block.attr('id', 'userButtons');
|
||||
$userlistElement.append($block);
|
||||
}
|
||||
|
||||
// Share button
|
||||
if (config.displayed.indexOf('share') !== -1) {
|
||||
var secret = Cryptpad.find(config, ['share', 'secret']);
|
||||
var channel = Cryptpad.find(config, ['share', 'channel']);
|
||||
if (!secret || !channel) {
|
||||
throw new Error("Unable to display the share button: share.secret and share.channel required");
|
||||
}
|
||||
Cryptpad.getRecentPads(function (err, recent) {
|
||||
var $shareIcon = $('<span>', {'class': 'fa fa-share-alt'});
|
||||
var $span = $('<span>', {'class': 'large'}).append(' ' +Messages.shareButton);
|
||||
var hashes = Cryptpad.getHashes(channel, secret);
|
||||
var options = [];
|
||||
|
||||
// If we have a stronger version in drive, add it and add a redirect button
|
||||
var stronger = recent && Cryptpad.findStronger(null, recent);
|
||||
if (stronger) {
|
||||
var parsed = Cryptpad.parsePadUrl(stronger);
|
||||
hashes.editHash = parsed.hash;
|
||||
}
|
||||
|
||||
if (hashes.editHash) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {title: Messages.editShareTitle, 'class': 'editShare'},
|
||||
content: '<span class="fa fa-users"></span> ' + Messages.editShare
|
||||
});
|
||||
if (stronger) {
|
||||
// We're in view mode, display the "open editing link" button
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
title: Messages.editOpenTitle,
|
||||
'class': 'editOpen',
|
||||
href: window.location.pathname + '#' + hashes.editHash,
|
||||
target: '_blank'
|
||||
},
|
||||
content: '<span class="fa fa-users"></span> ' + Messages.editOpen
|
||||
});
|
||||
}
|
||||
options.push({tag: 'hr'});
|
||||
}
|
||||
if (hashes.viewHash) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {title: Messages.viewShareTitle, 'class': 'viewShare'},
|
||||
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
|
||||
});
|
||||
if (hashes.editHash && !stronger) {
|
||||
// We're in edit mode, display the "open readonly" button
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
title: Messages.viewOpenTitle,
|
||||
'class': 'viewOpen',
|
||||
href: window.location.pathname + '#' + hashes.viewHash,
|
||||
target: '_blank'
|
||||
},
|
||||
content: '<span class="fa fa-eye"></span> ' + Messages.viewOpen
|
||||
});
|
||||
}
|
||||
}
|
||||
if (hashes.fileHash) {
|
||||
options.push({
|
||||
tag: 'a',
|
||||
attributes: {title: Messages.viewShareTitle, 'class': 'fileShare'},
|
||||
content: '<span class="fa fa-eye"></span> ' + Messages.viewShare
|
||||
});
|
||||
}
|
||||
var dropdownConfigShare = {
|
||||
text: $('<div>').append($shareIcon).append($span).html(),
|
||||
options: options
|
||||
};
|
||||
var $shareBlock = Cryptpad.createDropdown(dropdownConfigShare);
|
||||
$shareBlock.find('button').attr('id', 'shareButton');
|
||||
$shareBlock.find('.dropdown-bar-content').addClass(SHARE_CLS).addClass(EDITSHARE_CLS).addClass(VIEWSHARE_CLS);
|
||||
$userlistElement.append($shareBlock);
|
||||
|
||||
if (hashes.editHash) {
|
||||
$shareBlock.find('a.editShare').click(function () {
|
||||
var url = window.location.origin + window.location.pathname + '#' + hashes.editHash;
|
||||
var success = Cryptpad.Clipboard.copy(url);
|
||||
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||
});
|
||||
}
|
||||
if (hashes.viewHash) {
|
||||
$shareBlock.find('a.viewShare').click(function () {
|
||||
var url = window.location.origin + window.location.pathname + '#' + hashes.viewHash ;
|
||||
var success = Cryptpad.Clipboard.copy(url);
|
||||
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||
});
|
||||
}
|
||||
if (hashes.fileHash) {
|
||||
$shareBlock.find('a.fileShare').click(function () {
|
||||
var url = window.location.origin + window.location.pathname + '#' + hashes.fileHash ;
|
||||
var success = Cryptpad.Clipboard.copy(url);
|
||||
if (success) { Cryptpad.log(Messages.shareSuccess); }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var createUserList = function ($container, config, readOnly, Cryptpad) {
|
||||
if (config.displayed.indexOf('userlist') === -1 && config.displayed.indexOf('share') === -1) { return; }
|
||||
var $userlist = $('<div>', {
|
||||
'class': USER_LIST_CLS,
|
||||
id: uid(),
|
||||
});
|
||||
createUserButtons($userlist, config, readOnly, Cryptpad);
|
||||
$container.append($userlist);
|
||||
return $userlist[0];
|
||||
};
|
||||
|
||||
var getOtherUsers = function(myUserName, userList, userData) {
|
||||
var i = 0; // duplicates counter
|
||||
var list = [];
|
||||
var myUid = userData[myUserName] ? userData[myUserName].uid : undefined;
|
||||
var uids = [];
|
||||
userList.forEach(function(user) {
|
||||
if(user !== myUserName) {
|
||||
var data = (userData) ? (userData[user] || null) : null;
|
||||
var userName = (data) ? data.name : null;
|
||||
var userId = (data) ? data.uid : null;
|
||||
if (userName && uids.indexOf(userId) === -1 && (!myUid || userId !== myUid)) {
|
||||
uids.push(userId);
|
||||
list.push(userName);
|
||||
} else { i++; }
|
||||
}
|
||||
});
|
||||
return {
|
||||
list: list,
|
||||
duplicates: i
|
||||
};
|
||||
};
|
||||
|
||||
var arrayIntersect = function(a, b) {
|
||||
return $.grep(a, function(i) {
|
||||
return $.inArray(i, b) > -1;
|
||||
});
|
||||
};
|
||||
|
||||
var getViewers = function (n) {
|
||||
if (!n || !parseInt(n) || n === 0) { return ''; }
|
||||
if (n === 1) { return '; + ' + Messages.oneViewer; }
|
||||
return '; + ' + Messages._getKey('viewers', [n]);
|
||||
};
|
||||
|
||||
var checkSynchronizing = function (userList, myUserName, $stateElement) {
|
||||
var meIdx = userList.indexOf(myUserName);
|
||||
if (meIdx === -1) {
|
||||
$stateElement.text(Messages.synchronizing);
|
||||
return;
|
||||
}
|
||||
$stateElement.text('');
|
||||
};
|
||||
|
||||
var updateUserList = function (config, myUserName, userlistElement, userList, userData, readOnly, $userAdminElement) {
|
||||
// Make sure the elements are displayed
|
||||
var $userButtons = $(userlistElement).find("#userButtons");
|
||||
$userButtons.attr('display', 'inline');
|
||||
|
||||
if (config.displayed.indexOf('userlist') !== -1) {
|
||||
var numberOfUsers = userList.length;
|
||||
|
||||
// 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 = readOnly === -1 ? userList : arrayIntersect(userList, Object.keys(userData));
|
||||
|
||||
// Names of editing users
|
||||
var others = getOtherUsers(myUserName, userList, userData);
|
||||
var editUsersNames = others.list;
|
||||
var duplicates = others.duplicates;
|
||||
|
||||
var numberOfEditUsers = userList.length - duplicates;
|
||||
var numberOfViewUsers = numberOfUsers - numberOfEditUsers - duplicates;
|
||||
|
||||
// Number of anonymous editing users
|
||||
var anonymous = numberOfEditUsers - editUsersNames.length;
|
||||
|
||||
// Update the userlist
|
||||
var $usersTitle = $('<h2>').text(Messages.users);
|
||||
var $editUsers = $userButtons.find('.' + USERLIST_CLS);
|
||||
$editUsers.html('').append($usersTitle);
|
||||
|
||||
var $editUsersList = $('<pre>');
|
||||
if (readOnly !== 1) { // Yourself - edit
|
||||
$editUsers.append('<span class="yourself">' + Messages.yourself + '</span>');
|
||||
anonymous--;
|
||||
}
|
||||
// Editors
|
||||
$editUsersList.text(editUsersNames.join('\n')); // .text() to avoid XSS
|
||||
$editUsers.append($editUsersList);
|
||||
if (anonymous > 0) { // Anonymous editors
|
||||
var text = anonymous === 1 ? Messages.anonymousUser : Messages.anonymousUsers;
|
||||
$editUsers.append('<span class="anonymous">' + anonymous + ' ' + text + '</span>');
|
||||
}
|
||||
if (numberOfViewUsers > 0) { // Viewers
|
||||
var viewText = '<span class="viewer">';
|
||||
if (numberOfEditUsers > 0) {
|
||||
$editUsers.append('<br>');
|
||||
viewText += Messages.and + ' ';
|
||||
}
|
||||
var viewerText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
|
||||
viewText += numberOfViewUsers + ' ' + viewerText + '</span>';
|
||||
$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 viewersText = numberOfViewUsers !== 1 ? Messages.viewers : Messages.viewer;
|
||||
var editorsText = numberOfEditUsers !== 1 ? Messages.editors : Messages.editor;
|
||||
var $span = $('<span>', {'class': 'large'}).html(fa_editusers + ' ' + numberOfEditUsers + ' ' + editorsText + ' ' + fa_viewusers + ' ' + numberOfViewUsers + ' ' + viewersText);
|
||||
var $spansmall = $('<span>', {'class': 'narrow'}).html(fa_editusers + ' ' + numberOfEditUsers + ' ' + fa_viewusers + ' ' + numberOfViewUsers);
|
||||
$userButtons.find('.buttonTitle').html('').append($span).append($spansmall);
|
||||
}
|
||||
|
||||
if (config.displayed.indexOf('useradmin') !== -1) {
|
||||
// Change username in useradmin dropdown
|
||||
var $userElement = $userAdminElement.find('.' + USERNAME_CLS);
|
||||
$userElement.show();
|
||||
if (readOnly === 1) {
|
||||
$userElement.addClass(READONLY_CLS).text(Messages.readonly);
|
||||
}
|
||||
else {
|
||||
var name = userData[myUserName] && userData[myUserName].name;
|
||||
if (!name) {
|
||||
name = Messages.anonymous;
|
||||
}
|
||||
$userElement.removeClass(READONLY_CLS).text(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var createLagElement = function () {
|
||||
var $lag = $('<span>', {
|
||||
'class': LAG_ELEM_CLS,
|
||||
id: uid(),
|
||||
});
|
||||
var $a = $('<span>', {'class': 'cryptpad-lag', id: 'newLag'});
|
||||
$('<span>', {'class': 'bar1'}).appendTo($a);
|
||||
$('<span>', {'class': 'bar2'}).appendTo($a);
|
||||
$('<span>', {'class': 'bar3'}).appendTo($a);
|
||||
$('<span>', {'class': 'bar4'}).appendTo($a);
|
||||
return $a[0];
|
||||
};
|
||||
|
||||
var checkLag = function (getLag, lagElement) {
|
||||
var lag;
|
||||
if(typeof getLag === "function") {
|
||||
lag = getLag();
|
||||
}
|
||||
var lagLight = $('<div>', {
|
||||
'class': 'lag'
|
||||
});
|
||||
var title;
|
||||
var $lag = $(lagElement);
|
||||
if (lag) {
|
||||
$lag.attr('class', 'cryptpad-lag');
|
||||
firstConnection = false;
|
||||
title = Messages.lag + ' : ' + lag + ' ms\n';
|
||||
if (lag > 30000) {
|
||||
$lag.addClass('lag0');
|
||||
title = Messages.redLight;
|
||||
} else if (lag > 5000) {
|
||||
$lag.addClass('lag1');
|
||||
title += Messages.orangeLight;
|
||||
} else if (lag > 1000) {
|
||||
$lag.addClass('lag2');
|
||||
title += Messages.orangeLight;
|
||||
} else if (lag > 300) {
|
||||
$lag.addClass('lag3');
|
||||
title += Messages.greenLight;
|
||||
} else {
|
||||
$lag.addClass('lag4');
|
||||
title += Messages.greenLight;
|
||||
}
|
||||
}
|
||||
else if (!firstConnection) {
|
||||
$lag.attr('class', 'cryptpad-lag');
|
||||
// Display the red light at the 2nd failed attemp to get the lag
|
||||
lagLight.addClass('lag-red');
|
||||
title = Messages.redLight;
|
||||
}
|
||||
if (title) {
|
||||
$lag.attr('title', title);
|
||||
}
|
||||
};
|
||||
|
||||
var createLinkToMain = function ($topContainer) {
|
||||
var $linkContainer = $('<span>', {
|
||||
'class': "cryptpad-link"
|
||||
}).appendTo($topContainer);
|
||||
var $imgTag = $('<img>', {
|
||||
src: "/customize/cryptofist_mini.png?" + urlArgs,
|
||||
alt: "Cryptpad"
|
||||
});
|
||||
|
||||
// We need to override the "a" tag action here because it is inside the iframe!
|
||||
var $aTagSmall = $('<a>', {
|
||||
href: "/",
|
||||
title: Messages.header_logoTitle,
|
||||
'class': "cryptpad-logo"
|
||||
}).append($imgTag);
|
||||
var $span = $('<span>').text('CryptPad');
|
||||
var $aTagBig = $aTagSmall.clone().addClass('large').append($span);
|
||||
$aTagSmall.addClass('narrow');
|
||||
var onClick = function (e) {
|
||||
e.preventDefault();
|
||||
if (e.ctrlKey) {
|
||||
window.open('/');
|
||||
return;
|
||||
}
|
||||
window.location = "/";
|
||||
};
|
||||
|
||||
var onContext = function (e) { e.stopPropagation(); };
|
||||
|
||||
$aTagBig.click(onClick).contextmenu(onContext);
|
||||
$aTagSmall.click(onClick).contextmenu(onContext);
|
||||
|
||||
$linkContainer.append($aTagSmall).append($aTagBig);
|
||||
};
|
||||
|
||||
var createUserAdmin = function ($topContainer, config, readOnly, lagElement, Cryptpad) {
|
||||
var $lag = $(lagElement);
|
||||
|
||||
var $userContainer = $('<span>', {
|
||||
'class': USER_CLS
|
||||
}).appendTo($topContainer);
|
||||
|
||||
if (config.displayed.indexOf('state') !== -1) {
|
||||
var $state = $('<span>', {
|
||||
'class': STATE_CLS
|
||||
}).text(Messages.synchronizing).appendTo($userContainer);
|
||||
}
|
||||
|
||||
if (config.displayed.indexOf('lag') !== -1) {
|
||||
$userContainer.append($lag);
|
||||
}
|
||||
|
||||
if (config.displayed.indexOf('limit') !== -1 && Config.enablePinning) {
|
||||
var usage;
|
||||
var $limitIcon = $('<span>', {'class': 'fa fa-exclamation-triangle'});
|
||||
var $limit = $('<span>', {
|
||||
'class': LIMIT_ELEM_CLS,
|
||||
'title': Messages.pinLimitReached
|
||||
}).append($limitIcon).hide().appendTo($userContainer);
|
||||
var todo = function (e, overLimit) {
|
||||
if (e) { return void console.error("Unable to get the pinned usage"); }
|
||||
if (overLimit) {
|
||||
var message = Messages.pinLimitReachedAlert;
|
||||
if (ApiConfig.noSubscriptionButton === true) {
|
||||
message = Messages.pinLimitReachedAlertNoAccounts;
|
||||
}
|
||||
$limit.show().click(function () {
|
||||
Cryptpad.alert(message, null, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
Cryptpad.isOverPinLimit(todo);
|
||||
}
|
||||
|
||||
if (config.displayed.indexOf('newpad') !== -1) {
|
||||
var pads_options = [];
|
||||
Config.availablePadTypes.forEach(function (p) {
|
||||
if (p === 'drive') { return; }
|
||||
pads_options.push({
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'target': '_blank',
|
||||
'href': '/' + p + '/',
|
||||
},
|
||||
content: Messages.type[p]
|
||||
});
|
||||
});
|
||||
var $plusIcon = $('<span>', {'class': 'fa fa-plus'});
|
||||
var $newbig = $('<span>', {'class': 'big'}).append(' ' +Messages.newButton);
|
||||
var $newButton = $('<div>').append($plusIcon).append($newbig);
|
||||
var dropdownConfig = {
|
||||
text: $newButton.html(), // Button initial text
|
||||
options: pads_options, // Entries displayed in the menu
|
||||
left: true, // Open to the left of the button
|
||||
};
|
||||
var $newPadBlock = Cryptpad.createDropdown(dropdownConfig);
|
||||
$newPadBlock.find('button').attr('title', Messages.newButtonTitle);
|
||||
$newPadBlock.find('button').attr('id', 'newdoc');
|
||||
$newPadBlock.appendTo($userContainer);
|
||||
}
|
||||
|
||||
// User dropdown
|
||||
if (config.displayed.indexOf('useradmin') !== -1) {
|
||||
var userMenuCfg = {};
|
||||
if (!config.hideDisplayName) {
|
||||
userMenuCfg = {
|
||||
displayNameCls: USERNAME_CLS,
|
||||
changeNameButtonCls: USERBUTTON_CLS,
|
||||
};
|
||||
}
|
||||
if (readOnly !== 1) {
|
||||
userMenuCfg.displayName = 1;
|
||||
userMenuCfg.displayChangeName = 1;
|
||||
}
|
||||
var $userAdmin = Cryptpad.createUserAdminMenu(userMenuCfg);
|
||||
|
||||
$userAdmin.attr('id', 'userDropdown');
|
||||
$userContainer.append($userAdmin);
|
||||
|
||||
var $userButton = $userAdmin.find('a.' + USERBUTTON_CLS);
|
||||
var renameAlertOpened;
|
||||
$userButton.click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
Cryptpad.getLastName(function (err, lastName) {
|
||||
if (err) { return void console.error("Cannot get last name", err); }
|
||||
Cryptpad.prompt(Messages.changeNamePrompt, lastName || '', function (newName) {
|
||||
if (newName === null && typeof(lastName) === "string") { return; }
|
||||
if (newName === null) { newName = ''; }
|
||||
Cryptpad.changeDisplayName(newName);
|
||||
});
|
||||
});
|
||||
});
|
||||
Cryptpad.onDisplayNameChanged(function (newName) {
|
||||
Cryptpad.findCancelButton().click();
|
||||
});
|
||||
}
|
||||
|
||||
return $userContainer;
|
||||
};
|
||||
|
||||
var createTitle = function ($container, readOnly, config, Cryptpad) {
|
||||
var $titleContainer = $('<span>', {
|
||||
id: 'toolbarTitle',
|
||||
'class': TITLE_CLS
|
||||
}).appendTo($container);
|
||||
|
||||
if (!config || typeof config !== "object") { return; }
|
||||
|
||||
var callback = config.onRename;
|
||||
var placeholder = config.defaultName;
|
||||
var suggestName = config.suggestName;
|
||||
|
||||
// Buttons
|
||||
var $text = $('<span>', {
|
||||
'class': 'title'
|
||||
}).appendTo($titleContainer);
|
||||
var $pencilIcon = $('<span>', {
|
||||
'class': 'pencilIcon',
|
||||
'title': Messages.clickToEdit
|
||||
});
|
||||
if (readOnly === 1 || typeof(Cryptpad) === "undefined") { return $titleContainer; }
|
||||
var $input = $('<input>', {
|
||||
type: 'text',
|
||||
placeholder: placeholder
|
||||
}).appendTo($titleContainer).hide();
|
||||
if (readOnly !== 1) {
|
||||
$text.attr("title", Messages.clickToEdit);
|
||||
$text.addClass("editable");
|
||||
var $icon = $('<span>', {
|
||||
'class': 'fa fa-pencil readonly',
|
||||
style: 'font-family: FontAwesome;'
|
||||
});
|
||||
$pencilIcon.append($icon).appendTo($titleContainer);
|
||||
}
|
||||
|
||||
// Events
|
||||
$input.on('mousedown', function (e) {
|
||||
if (!$input.is(":focus")) {
|
||||
$input.focus();
|
||||
}
|
||||
e.stopPropagation();
|
||||
return true;
|
||||
});
|
||||
$input.on('keyup', function (e) {
|
||||
if (e.which === 13 && connected === true) {
|
||||
var name = $input.val().trim();
|
||||
if (name === "") {
|
||||
name = $input.attr('placeholder');
|
||||
}
|
||||
Cryptpad.renamePad(name, function (err, newtitle) {
|
||||
if (err) { return; }
|
||||
$text.text(newtitle);
|
||||
callback(null, newtitle);
|
||||
$input.hide();
|
||||
$text.show();
|
||||
//$pencilIcon.css('display', '');
|
||||
});
|
||||
}
|
||||
else if (e.which === 27) {
|
||||
$input.hide();
|
||||
$text.show();
|
||||
//$pencilIcon.css('display', '');
|
||||
}
|
||||
});
|
||||
|
||||
var displayInput = function () {
|
||||
if (connected === false) { return; }
|
||||
$text.hide();
|
||||
//$pencilIcon.css('display', 'none');
|
||||
var inputVal = suggestName() || "";
|
||||
$input.val(inputVal);
|
||||
$input.show();
|
||||
$input.focus();
|
||||
};
|
||||
$text.on('click', displayInput);
|
||||
$pencilIcon.on('click', displayInput);
|
||||
return $titleContainer;
|
||||
};
|
||||
|
||||
var create = Bar.create = function ($container, myUserName, realtime, getLag, userList, config) {
|
||||
config = config || {};
|
||||
var readOnly = (typeof config.readOnly !== "undefined") ? (config.readOnly ? 1 : 0) : -1;
|
||||
var Cryptpad = config.common;
|
||||
Messages = Cryptpad.Messages;
|
||||
config.displayed = config.displayed || [];
|
||||
|
||||
var toolbar = createRealtimeToolbar($container, config.title);
|
||||
var userListElement = createUserList(toolbar.find('.' + LEFTSIDE_CLS), config, readOnly, Cryptpad);
|
||||
var $titleElement = createTitle(toolbar.find('.' + TOP_CLS), readOnly, config.title, Cryptpad);
|
||||
var $linkElement = createLinkToMain(toolbar.find('.' + TOP_CLS));
|
||||
var lagElement = createLagElement();
|
||||
var $userAdminElement = createUserAdmin(toolbar.find('.' + TOP_CLS), config, readOnly, lagElement, Cryptpad);
|
||||
var spinner = createSpinner($userAdminElement, config);
|
||||
var userData = config.userData;
|
||||
// readOnly = 1 (readOnly enabled), 0 (disabled), -1 (old pad without readOnly mode)
|
||||
var saveElement;
|
||||
var loadElement;
|
||||
var $stateElement = toolbar.find('.' + STATE_CLS);
|
||||
|
||||
if (config.ifrw) {
|
||||
var removeDropdowns = function (e) {
|
||||
$container.find('.cryptpad-dropdown').hide();
|
||||
};
|
||||
var cancelEditTitle = function (e) {
|
||||
// Now we want to apply the title even if we click somewhere else
|
||||
if ($(e.target).parents('.' + TITLE_CLS).length || !$titleElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$titleElement.find('input').is(':visible')) { return; }
|
||||
|
||||
var ev = $.Event("keyup");
|
||||
ev.which = 13;
|
||||
$titleElement.find('input').trigger(ev);
|
||||
|
||||
/*
|
||||
$titleElement.find('input').hide();
|
||||
$titleElement.find('span.title').show();
|
||||
$titleElement.find('span.pencilIcon').css('display', '');
|
||||
*/
|
||||
};
|
||||
$(config.ifrw).on('click', removeDropdowns);
|
||||
$(config.ifrw).on('click', cancelEditTitle);
|
||||
|
||||
try {
|
||||
if (config.ifrw.$('iframe').length) {
|
||||
var innerIfrw = 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
|
||||
}
|
||||
}
|
||||
|
||||
// Update user list
|
||||
if (userList) {
|
||||
if (userData) {
|
||||
userList.change.push(function (newUserData) {
|
||||
var users = userList.users;
|
||||
if (users.indexOf(myUserName) !== -1) { connected = true; }
|
||||
if (!connected) { return; }
|
||||
checkSynchronizing(users, myUserName, $stateElement);
|
||||
updateUserList(config, myUserName, userListElement, users, userData, readOnly, $userAdminElement);
|
||||
});
|
||||
} else {
|
||||
userList.change.push(function () {
|
||||
var users = userList.users;
|
||||
if (users.indexOf(myUserName) !== -1) { connected = true; }
|
||||
if (!connected) { return; }
|
||||
checkSynchronizing(users, myUserName, $stateElement);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Display notifications when users are joining/leaving the session
|
||||
var oldUserData;
|
||||
if (typeof Cryptpad !== "undefined" && userList) {
|
||||
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 : name;
|
||||
switch(type) {
|
||||
case 1:
|
||||
Cryptpad.log(Messages._getKey("notifyJoined", [name]));
|
||||
break;
|
||||
case 0:
|
||||
oldname = (oldname === "") ? Messages.anonymous : oldname;
|
||||
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;
|
||||
};
|
||||
|
||||
userList.change.push(function (newdata) {
|
||||
// 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
|
||||
if (userList.users.indexOf(u) === -1) {
|
||||
var temp = JSON.parse(JSON.stringify(oldUserData[u]));
|
||||
delete oldUserData[u];
|
||||
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 (readOnly === 0 && !oldUserData[myUserName]) {
|
||||
oldUserData = JSON.parse(JSON.stringify(newdata));
|
||||
return;
|
||||
}
|
||||
for (var k in newdata) {
|
||||
if (k !== myUserName && userList.users.indexOf(k) !== -1) {
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
var ks = function (local) {
|
||||
return function () {
|
||||
if (connected) { kickSpinner(Cryptpad, realtime, local, spinner); }
|
||||
};
|
||||
};
|
||||
|
||||
if (realtime) {
|
||||
realtime.onPatch(ks());
|
||||
realtime.onMessage(ks(true));
|
||||
|
||||
checkLag(getLag, lagElement);
|
||||
setInterval(function () {
|
||||
if (!connected) { return; }
|
||||
checkLag(getLag, lagElement);
|
||||
}, 3000);
|
||||
} else { connected = true; }
|
||||
|
||||
var failed = function () {
|
||||
connected = false;
|
||||
$stateElement.text(Messages.disconnected);
|
||||
checkLag(undefined, lagElement);
|
||||
};
|
||||
|
||||
// On log out, remove permanently the realtime elements of the toolbar
|
||||
Cryptpad.onLogout(function () {
|
||||
failed();
|
||||
$userAdminElement.find('#userDropdown').hide();
|
||||
$(userListElement).hide();
|
||||
});
|
||||
|
||||
return {
|
||||
failed: failed,
|
||||
reconnecting: function (userId) {
|
||||
myUserName = userId;
|
||||
connected = false;
|
||||
$stateElement.text(Messages.reconnecting);
|
||||
checkLag(getLag, lagElement);
|
||||
},
|
||||
connected: function () {
|
||||
connected = true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return Bar;
|
||||
});
|
@ -1,528 +0,0 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/common/cryptpad-common.js',
|
||||
'/common/hyperscript.js',
|
||||
'/bower_components/marked/marked.min.js',
|
||||
'/common/media-tag.js',
|
||||
], function ($, Cryptpad, h, Marked, MediaTag) {
|
||||
'use strict';
|
||||
|
||||
var UI = {};
|
||||
var Messages = Cryptpad.Messages;
|
||||
|
||||
var m = function (md) {
|
||||
var d = h('div.cp-app-contacts-content');
|
||||
try {
|
||||
d.innerHTML = Marked(md || '');
|
||||
var $d = $(d);
|
||||
// remove potentially malicious elements
|
||||
$d.find('script, iframe, object, applet, video, audio').remove();
|
||||
|
||||
// activate media-tags
|
||||
$d.find('media-tag').each(function (i, e) { MediaTag(e); });
|
||||
} catch (e) {
|
||||
console.error(md);
|
||||
console.error(e);
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
var dataQuery = function (curvePublic) {
|
||||
return '[data-key="' + curvePublic + '"]';
|
||||
};
|
||||
|
||||
var initChannel = function (state, curvePublic, info) {
|
||||
console.log('initializing channel for [%s]', curvePublic);
|
||||
state.channels[curvePublic] = {
|
||||
messages: [],
|
||||
HEAD: info.lastKnownHash,
|
||||
TAIL: null,
|
||||
};
|
||||
};
|
||||
|
||||
UI.create = function (messenger, $userlist, $messages, common) {
|
||||
var origin = common.getMetadataMgr().getPrivateData().origin;
|
||||
|
||||
var state = window.state = {
|
||||
active: '',
|
||||
};
|
||||
|
||||
state.channels = {};
|
||||
var displayNames = state.displayNames = {};
|
||||
|
||||
var avatars = state.avatars = {};
|
||||
var setActive = function (curvePublic) {
|
||||
state.active = curvePublic;
|
||||
};
|
||||
var isActive = function (curvePublic) {
|
||||
return curvePublic === state.active;
|
||||
};
|
||||
|
||||
var find = {};
|
||||
find.inList = function (curvePublic) {
|
||||
return $userlist.find(dataQuery(curvePublic));
|
||||
};
|
||||
|
||||
var notify = function (curvePublic) {
|
||||
find.inList(curvePublic).addClass('cp-app-contacts-notify');
|
||||
};
|
||||
var unnotify = function (curvePublic) {
|
||||
find.inList(curvePublic).removeClass('cp-app-contacts-notify');
|
||||
};
|
||||
|
||||
var markup = {};
|
||||
markup.message = function (msg) {
|
||||
var curvePublic = msg.author;
|
||||
var name = displayNames[msg.author];
|
||||
return h('div.cp-app-contacts-message', {
|
||||
title: msg.time? new Date(msg.time).toLocaleString(): '?',
|
||||
'data-key': curvePublic,
|
||||
}, [
|
||||
name? h('div.cp-app-contacts-sender', name): undefined,
|
||||
m(msg.text),
|
||||
]);
|
||||
};
|
||||
|
||||
var getChat = function (curvePublic) {
|
||||
return $messages.find(dataQuery(curvePublic));
|
||||
};
|
||||
|
||||
var normalizeLabels = function ($messagebox) {
|
||||
$messagebox.find('div.cp-app-contacts-message').toArray().reduce(function (a, b) {
|
||||
var $b = $(b);
|
||||
if ($(a).data('key') === $b.data('key')) {
|
||||
$b.find('.cp-app-contacts-sender').hide();
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}, []);
|
||||
};
|
||||
|
||||
markup.chatbox = function (curvePublic, data) {
|
||||
var moreHistory = h('span.cp-app-contacts-more-history.fa.fa-history', {
|
||||
title: Messages.contacts_fetchHistory,
|
||||
});
|
||||
var displayName = data.displayName;
|
||||
|
||||
var fetching = false;
|
||||
var $moreHistory = $(moreHistory).click(function () {
|
||||
if (fetching) { return; }
|
||||
|
||||
// get oldest known message...
|
||||
var channel = state.channels[curvePublic];
|
||||
|
||||
if (channel.exhausted) {
|
||||
return void $moreHistory.addClass('cp-app-contacts-faded');
|
||||
}
|
||||
|
||||
console.log('getting history');
|
||||
var sig = channel.TAIL || channel.HEAD;
|
||||
|
||||
fetching = true;
|
||||
var $messagebox = $(getChat(curvePublic)).find('.cp-app-contacts-messages');
|
||||
messenger.getMoreHistory(curvePublic, sig, 10, function (e, history) {
|
||||
fetching = false;
|
||||
if (e) { return void console.error(e); }
|
||||
|
||||
if (history.length === 0) {
|
||||
channel.exhausted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
history.forEach(function (msg) {
|
||||
if (channel.exhausted) { return; }
|
||||
if (msg.sig) {
|
||||
if (msg.sig === channel.TAIL) {
|
||||
console.error('No more messages to fetch');
|
||||
channel.exhausted = true;
|
||||
console.log(channel);
|
||||
return void $moreHistory.addClass('cp-app-contacts-faded');
|
||||
} else {
|
||||
channel.TAIL = msg.sig;
|
||||
}
|
||||
} else {
|
||||
return void console.error('expected signature');
|
||||
}
|
||||
if (msg.type !== 'MSG') { return; }
|
||||
|
||||
// FIXME Schlameil the painter (performance does not scale well)
|
||||
if (channel.messages.some(function (old) {
|
||||
return msg.sig === old.sig;
|
||||
})) { return; }
|
||||
|
||||
channel.messages.unshift(msg);
|
||||
var el_message = markup.message(msg);
|
||||
$messagebox.prepend(el_message);
|
||||
});
|
||||
normalizeLabels($messagebox);
|
||||
});
|
||||
});
|
||||
|
||||
var removeHistory = h('span.cp-app-contacts-remove-history.fa.fa-eraser', {
|
||||
title: Messages.contacts_removeHistoryTitle
|
||||
});
|
||||
|
||||
$(removeHistory).click(function () {
|
||||
Cryptpad.confirm(Messages.contacts_confirmRemoveHistory, function (yes) {
|
||||
if (!yes) { return; }
|
||||
Cryptpad.clearOwnedChannel(data.channel, function (e) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
Cryptpad.alert(Messages.contacts_removeHistoryServerError);
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var avatar = h('div.cp-avatar');
|
||||
var header = h('div.cp-app-contacts-header', [
|
||||
avatar,
|
||||
moreHistory,
|
||||
removeHistory,
|
||||
]);
|
||||
var messages = h('div.cp-app-contacts-messages');
|
||||
var input = h('textarea', {
|
||||
placeholder: Messages.contacts_typeHere
|
||||
});
|
||||
var sendButton = h('button.btn.btn-primary.fa.fa-paper-plane', {
|
||||
title: Messages.contacts_send,
|
||||
});
|
||||
|
||||
var rightCol = h('span.cp-app-contacts-right-col', [
|
||||
h('span.cp-app-contacts-name', displayName),
|
||||
]);
|
||||
|
||||
var $avatar = $(avatar);
|
||||
if (data.avatar && avatars[data.avatar]) {
|
||||
$avatar.append(avatars[data.avatar]).append(rightCol);
|
||||
} else {
|
||||
common.displayAvatar($avatar, data.avatar, data.displayName, function ($img) {
|
||||
if (data.avatar && $img) {
|
||||
avatars[data.avatar] = $img[0].outerHTML;
|
||||
}
|
||||
$(rightCol).insertAfter($avatar);
|
||||
});
|
||||
}
|
||||
|
||||
var sending = false;
|
||||
var send = function (content) {
|
||||
if (typeof(content) !== 'string' || !content.trim()) { return; }
|
||||
if (sending) { return false; }
|
||||
sending = true;
|
||||
messenger.sendMessage(curvePublic, content, function (e) {
|
||||
if (e) {
|
||||
// failed to send
|
||||
return void console.error('failed to send');
|
||||
}
|
||||
input.value = '';
|
||||
sending = false;
|
||||
console.log('sent successfully');
|
||||
var $messagebox = $(messages);
|
||||
|
||||
var height = $messagebox[0].scrollHeight;
|
||||
$messagebox.scrollTop(height);
|
||||
});
|
||||
};
|
||||
|
||||
var onKeyDown = function (e) {
|
||||
// ignore anything that isn't 'enter'
|
||||
if (e.keyCode !== 13) { return; }
|
||||
// send unless they're holding a ctrl-key or shift
|
||||
if (!e.ctrlKey && !e.shiftKey) {
|
||||
send(this.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
// insert a newline if they're holding either
|
||||
var val = this.value;
|
||||
var start = this.selectionState;
|
||||
var end = this.selectionEnd;
|
||||
|
||||
if (![start,end].some(function (x) {
|
||||
return typeof(x) !== 'number';
|
||||
})) {
|
||||
this.value = val.slice(0, start) + '\n' + val.slice(end);
|
||||
this.selectionStart = this.selectionEnd = start + 1;
|
||||
} else if (document.selection && document.selection.createRange) {
|
||||
this.focus();
|
||||
var range = document.selection.createRange();
|
||||
range.text = '\r\n';
|
||||
range.collapse(false);
|
||||
range.select();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
$(input).on('keydown', onKeyDown);
|
||||
$(sendButton).click(function () { send(input.value); });
|
||||
|
||||
return h('div.cp-app-contacts-chat', {
|
||||
'data-key': curvePublic,
|
||||
}, [
|
||||
header,
|
||||
messages,
|
||||
h('div.cp-app-contacts-input', [
|
||||
input,
|
||||
sendButton,
|
||||
]),
|
||||
]);
|
||||
};
|
||||
|
||||
var hideInfo = function () {
|
||||
$messages.find('.cp-app-contacts-info').hide();
|
||||
};
|
||||
|
||||
var updateStatus = function (curvePublic) {
|
||||
var $status = find.inList(curvePublic).find('.cp-app-contacts-status');
|
||||
// FIXME this stopped working :(
|
||||
messenger.getStatus(curvePublic, function (e, online) {
|
||||
// if error maybe you shouldn't display this friend...
|
||||
if (e) {
|
||||
find.inList(curvePublic).hide();
|
||||
getChat(curvePublic).hide();
|
||||
|
||||
return void console.error(curvePublic, e);
|
||||
}
|
||||
if (online) {
|
||||
return void $status
|
||||
.removeClass('cp-app-contacts-offline').addClass('cp-app-contacts-online');
|
||||
}
|
||||
$status.removeClass('cp-app-contacts-online').addClass('cp-app-contacts-offline');
|
||||
});
|
||||
};
|
||||
|
||||
var display = function (curvePublic) {
|
||||
var channel = state.channels[curvePublic];
|
||||
var lastMsg = channel.messages.slice(-1)[0];
|
||||
|
||||
if (lastMsg) {
|
||||
channel.HEAD = lastMsg.sig;
|
||||
messenger.setChannelHead(curvePublic, channel.HEAD, function (e) {
|
||||
if (e) { console.error(e); }
|
||||
});
|
||||
}
|
||||
|
||||
setActive(curvePublic);
|
||||
unnotify(curvePublic);
|
||||
var $chat = getChat(curvePublic);
|
||||
hideInfo();
|
||||
$messages.find('div.cp-app-contacts-chat[data-key]').hide();
|
||||
if ($chat.length) {
|
||||
var $chat_messages = $chat.find('div.cp-app-contacts-message');
|
||||
if (!$chat_messages.length) {
|
||||
var $more = $chat.find('.cp-app-contacts-more-history');
|
||||
$more.click();
|
||||
}
|
||||
return void $chat.show();
|
||||
}
|
||||
messenger.getFriendInfo(curvePublic, function (e, info) {
|
||||
if (e) { return void console.error(e); } // FIXME
|
||||
var chatbox = markup.chatbox(curvePublic, info);
|
||||
$messages.append(chatbox);
|
||||
});
|
||||
};
|
||||
|
||||
var removeFriend = function (curvePublic) {
|
||||
messenger.removeFriend(curvePublic, function (e /*, removed */) {
|
||||
if (e) { return void console.error(e); }
|
||||
find.inList(curvePublic).remove();
|
||||
//console.log(removed);
|
||||
});
|
||||
};
|
||||
|
||||
markup.friend = function (data) {
|
||||
var curvePublic = data.curvePublic;
|
||||
var friend = h('div.cp-app-contacts-friend.cp-avatar', {
|
||||
'data-key': curvePublic,
|
||||
});
|
||||
|
||||
var remove = h('span.cp-app-contacts-remove.fa.fa-user-times', {
|
||||
title: Messages.contacts_remove
|
||||
});
|
||||
var status = h('span.cp-app-contacts-status');
|
||||
var rightCol = h('span.cp-app-contacts-right-col', [
|
||||
h('span.cp-app-contacts-name', [data.displayName]),
|
||||
remove,
|
||||
]);
|
||||
|
||||
var $friend = $(friend)
|
||||
.click(function () {
|
||||
display(curvePublic);
|
||||
})
|
||||
.dblclick(function () {
|
||||
if (data.profile) { window.open(origin + '/profile/#' + data.profile); }
|
||||
});
|
||||
|
||||
$(remove).click(function (e) {
|
||||
e.stopPropagation();
|
||||
Cryptpad.confirm(Messages._getKey('contacts_confirmRemove', [
|
||||
Cryptpad.fixHTML(data.displayName)
|
||||
]), function (yes) {
|
||||
if (!yes) { return; }
|
||||
removeFriend(curvePublic, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
});
|
||||
// TODO remove friend from userlist ui
|
||||
// FIXME seems to trigger EJOINED from netflux-websocket (from server);
|
||||
// (tried to join a channel in which you were already present)
|
||||
}, undefined, true);
|
||||
});
|
||||
|
||||
if (data.avatar && avatars[data.avatar]) {
|
||||
$friend.append(avatars[data.avatar]);
|
||||
$friend.append(rightCol);
|
||||
} else {
|
||||
common.displayAvatar($friend, data.avatar, data.displayName, function ($img) {
|
||||
if (data.avatar && $img) {
|
||||
avatars[data.avatar] = $img[0].outerHTML;
|
||||
}
|
||||
$friend.append(rightCol);
|
||||
});
|
||||
}
|
||||
$friend.append(status);
|
||||
return $friend;
|
||||
};
|
||||
|
||||
var isBottomedOut = function ($elem) {
|
||||
return ($elem[0].scrollHeight - $elem.scrollTop() === $elem.outerHeight());
|
||||
};
|
||||
|
||||
var initializing = true;
|
||||
messenger.on('message', function (message) {
|
||||
if (!initializing) { Cryptpad.notify(); }
|
||||
var curvePublic = message.curve;
|
||||
|
||||
var name = displayNames[curvePublic];
|
||||
var chat = getChat(curvePublic, name);
|
||||
|
||||
console.log(message);
|
||||
|
||||
var el_message = markup.message(message);
|
||||
|
||||
state.channels[curvePublic].messages.push(message);
|
||||
|
||||
var $chat = $(chat);
|
||||
|
||||
if (!$chat.length) {
|
||||
console.error("Got a message but the chat isn't open");
|
||||
}
|
||||
|
||||
var $messagebox = $chat.find('.cp-app-contacts-messages');
|
||||
var shouldScroll = isBottomedOut($messagebox);
|
||||
|
||||
$messagebox.append(el_message);
|
||||
|
||||
if (shouldScroll) {
|
||||
$messagebox.scrollTop($messagebox.outerHeight());
|
||||
}
|
||||
normalizeLabels($messagebox);
|
||||
|
||||
var channel = state.channels[curvePublic];
|
||||
if (!channel) {
|
||||
console.error('expected channel [%s] to be open', curvePublic);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isActive(curvePublic)) {
|
||||
channel.HEAD = message.sig;
|
||||
messenger.setChannelHead(curvePublic, message.sig, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
var lastMsg = channel.messages.slice(-1)[0];
|
||||
if (lastMsg.sig !== channel.HEAD) {
|
||||
return void notify(curvePublic);
|
||||
}
|
||||
unnotify(curvePublic);
|
||||
});
|
||||
|
||||
messenger.on('join', function (curvePublic, channel) {
|
||||
channel = channel;
|
||||
updateStatus(curvePublic);
|
||||
});
|
||||
messenger.on('leave', function (curvePublic, channel) {
|
||||
channel = channel;
|
||||
updateStatus(curvePublic);
|
||||
});
|
||||
|
||||
// change in your friend list
|
||||
messenger.on('update', function (info, curvePublic) {
|
||||
var name = displayNames[curvePublic] = info.displayName;
|
||||
|
||||
// update label in friend list
|
||||
find.inList(curvePublic).find('.cp-app-contacts-name').text(name);
|
||||
|
||||
// update title bar and messages
|
||||
$messages.find(dataQuery(curvePublic) + ' .cp-app-contacts-header ' +
|
||||
'.cp-app-contacts-name, div.cp-app-contacts-message'+
|
||||
dataQuery(curvePublic) + ' div.cp-app-contacts-sender').text(name).text(name);
|
||||
});
|
||||
|
||||
var connectToFriend = function (curvePublic, cb) {
|
||||
messenger.getFriendInfo(curvePublic, function (e, info) {
|
||||
if (e) { return void console.error(e); }
|
||||
var name = displayNames[curvePublic] = info.displayName;
|
||||
initChannel(state, curvePublic, info);
|
||||
|
||||
var chatbox = markup.chatbox(curvePublic, info);
|
||||
$(chatbox).hide();
|
||||
$messages.append(chatbox);
|
||||
|
||||
var friend = markup.friend(info, name);
|
||||
$userlist.append(friend);
|
||||
messenger.openFriendChannel(curvePublic, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
cb();
|
||||
updateStatus(curvePublic);
|
||||
// don't add friends that are already in your userlist
|
||||
//if (friendExistsInUserList(k)) { return; }
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
messenger.on('friend', function (curvePublic) {
|
||||
console.log('new friend: ', curvePublic);
|
||||
//console.error("TODO redraw user list");
|
||||
//console.error("TODO connect to new friend");
|
||||
// FIXME this doesn't work right now because the friend hasn't been fully added?
|
||||
connectToFriend(curvePublic, function () {
|
||||
//console.error('connected');
|
||||
});
|
||||
});
|
||||
|
||||
messenger.on('unfriend', function (curvePublic) {
|
||||
console.log('unfriend', curvePublic);
|
||||
find.inList(curvePublic).remove();
|
||||
console.error('TODO remove chatbox');
|
||||
console.error('TODO show something if that chatbox was active');
|
||||
});
|
||||
|
||||
Cryptpad.onDisplayNameChanged(function () {
|
||||
//messenger.checkNewFriends();
|
||||
messenger.updateMyData();
|
||||
});
|
||||
|
||||
// FIXME dirty hack
|
||||
messenger.getMyInfo(function (e, info) {
|
||||
displayNames[info.curvePublic] = info.displayName;
|
||||
});
|
||||
|
||||
messenger.getFriendList(function (e, keys) {
|
||||
var count = keys.length + 1;
|
||||
var ready = function () {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
initializing = false;
|
||||
Cryptpad.removeLoadingScreen();
|
||||
}
|
||||
};
|
||||
ready();
|
||||
keys.forEach(function (curvePublic) {
|
||||
connectToFriend(curvePublic, ready);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return UI;
|
||||
});
|
Loading…
Reference in New Issue