etherpad-lite/src/static/js/pad_userlist.js

685 lines
23 KiB
JavaScript
Raw Normal View History

2012-04-09 18:42:09 +03:00
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
2011-03-26 13:10:41 +00:00
/**
* Copyright 2009 Google Inc.
2011-07-07 18:59:34 +01:00
*
2011-03-26 13:10:41 +00:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
2011-07-07 18:59:34 +01:00
*
2011-03-26 13:10:41 +00:00
* http://www.apache.org/licenses/LICENSE-2.0
2011-07-07 18:59:34 +01:00
*
2011-03-26 13:10:41 +00:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2020-11-23 13:24:19 -05:00
const padutils = require('./pad_utils').padutils;
const hooks = require('./pluginfw/hooks');
2020-11-23 13:24:19 -05:00
let myUserInfo = {};
2020-11-23 13:24:19 -05:00
let colorPickerOpen = false;
let colorPickerSetup = false;
let previousColorId = 0;
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
const paduserlist = (function () {
const rowManager = (function () {
2011-03-26 13:10:41 +00:00
// The row manager handles rendering rows of the user list and animating
// their insertion, removal, and reordering. It manipulates TD height
// and TD opacity.
function nextRowId() {
2020-11-23 13:24:19 -05:00
return `usertr${nextRowId.counter++}`;
2011-03-26 13:10:41 +00:00
}
nextRowId.counter = 1;
// objects are shared; fields are "domId","data","animationStep"
2020-11-23 13:24:19 -05:00
const rowsFadingOut = []; // unordered set
const rowsFadingIn = []; // unordered set
const rowsPresent = []; // in order
const ANIMATION_START = -12; // just starting to fade in
const ANIMATION_END = 12; // just finishing fading out
2011-07-07 18:59:34 +01:00
function getAnimationHeight(step, power) {
2020-11-23 13:24:19 -05:00
let a = Math.abs(step / 12);
if (power == 2) a *= a;
2011-07-07 18:59:34 +01:00
else if (power == 3) a = a * a * a;
else if (power == 4) a = a * a * a * a;
else if (power >= 5) a = a * a * a * a * a;
return Math.round(26 * (1 - a));
2011-03-26 13:10:41 +00:00
}
2020-11-23 13:24:19 -05:00
const OPACITY_STEPS = 6;
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
const ANIMATION_STEP_TIME = 20;
const LOWER_FRAMERATE_FACTOR = 2;
const scheduleAnimation = padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME, LOWER_FRAMERATE_FACTOR).scheduleAnimation;
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
const NUMCOLS = 4;
2011-03-26 13:10:41 +00:00
// we do lots of manipulation of table rows and stuff that JQuery makes ok, despite
// IE's poor handling when manipulating the DOM directly.
function getEmptyRowHtml(height) {
2020-11-23 13:24:19 -05:00
return `<td colspan="${NUMCOLS}" style="border:0;height:${height}px"><!-- --></td>`;
2011-03-26 13:10:41 +00:00
}
2011-07-07 18:59:34 +01:00
function isNameEditable(data) {
2011-07-07 18:59:34 +01:00
return (!data.name) && (data.status != 'Disconnected');
2011-03-26 13:10:41 +00:00
}
2011-07-07 18:59:34 +01:00
function replaceUserRowContents(tr, height, data) {
2020-11-23 13:24:19 -05:00
const tds = getUserRowHtml(height, data).match(/<td.*?<\/td>/gi);
if (isNameEditable(data) && tr.find('td.usertdname input:enabled').length > 0) {
2011-03-26 13:10:41 +00:00
// preserve input field node
2020-11-23 13:24:19 -05:00
for (let i = 0; i < tds.length; i++) {
const oldTd = $(tr.find('td').get(i));
if (!oldTd.hasClass('usertdname')) {
2011-03-26 13:10:41 +00:00
oldTd.replaceWith(tds[i]);
}
}
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
tr.html(tds.join(''));
}
return tr;
}
2011-07-07 18:59:34 +01:00
function getUserRowHtml(height, data) {
2020-11-23 13:24:19 -05:00
let nameHtml;
if (data.name) {
2011-03-26 13:10:41 +00:00
nameHtml = padutils.escapeHtml(data.name);
2020-11-23 13:24:19 -05:00
} else {
nameHtml = `<input data-l10n-id="pad.userlist.unnamed" type="text" class="editempty newinput" value="${_('pad.userlist.unnamed')}" ${isNameEditable(data) ? '' : 'disabled="disabled" '}/>`;
2011-03-26 13:10:41 +00:00
}
2020-11-23 13:24:19 -05:00
return ['<td style="height:', height, `px" class="usertdswatch"><div class="swatch" style="background:${padutils.escapeHtml(data.color)}">&nbsp;</div></td>`, '<td style="height:', height, 'px" class="usertdname">', nameHtml, '</td>', '<td style="height:', height, 'px" class="activity">', padutils.escapeHtml(data.activity), '</td>'].join('');
2011-03-26 13:10:41 +00:00
}
2011-07-07 18:59:34 +01:00
function getRowHtml(id, innerHtml, authorId) {
2020-11-23 13:24:19 -05:00
return `<tr data-authorId="${authorId}" id="${id}">${innerHtml}</tr>`;
2011-03-26 13:10:41 +00:00
}
2011-07-07 18:59:34 +01:00
function rowNode(row) {
2020-11-23 13:24:19 -05:00
return $(`#${row.domId}`);
2011-03-26 13:10:41 +00:00
}
2011-07-07 18:59:34 +01:00
function handleRowData(row) {
2020-11-23 13:24:19 -05:00
if (row.data && row.data.status == 'Disconnected') {
2011-03-26 13:10:41 +00:00
row.opacity = 0.5;
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
delete row.opacity;
}
}
2011-07-07 18:59:34 +01:00
function handleRowNode(tr, data) {
2020-11-23 13:24:19 -05:00
if (data.titleText) {
const titleText = data.titleText;
window.setTimeout(() => {
/* tr.attr('title', titleText)*/
2011-07-07 18:59:34 +01:00
}, 0);
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
tr.removeAttr('title');
}
}
2011-07-07 18:59:34 +01:00
function handleOtherUserInputs() {
2011-03-26 13:10:41 +00:00
// handle 'INPUT' elements for naming other unnamed users
2020-11-23 13:24:19 -05:00
$('#otheruserstable input.newinput').each(function () {
const input = $(this);
const tr = input.closest('tr');
if (tr.length > 0) {
const index = tr.parent().children().index(tr);
if (index >= 0) {
const userId = rowsPresent[index].data.id;
2011-03-26 13:10:41 +00:00
rowManagerMakeNameEditor($(this), userId);
}
}
}).removeClass('newinput');
}
// animationPower is 0 to skip animation, 1 for linear, 2 for quadratic, etc.
2011-07-07 18:59:34 +01:00
function insertRow(position, data, animationPower) {
2011-03-26 13:10:41 +00:00
position = Math.max(0, Math.min(rowsPresent.length, position));
animationPower = (animationPower === undefined ? 4 : animationPower);
2020-11-23 13:24:19 -05:00
const domId = nextRowId();
const row = {
data,
2011-07-07 18:59:34 +01:00
animationStep: ANIMATION_START,
2020-11-23 13:24:19 -05:00
domId,
animationPower,
2011-07-07 18:59:34 +01:00
};
2020-11-23 13:24:19 -05:00
const authorId = data.id;
2011-03-26 13:10:41 +00:00
handleRowData(row);
rowsPresent.splice(position, 0, row);
2020-11-23 13:24:19 -05:00
let tr;
if (animationPower == 0) {
tr = $(getRowHtml(domId, getUserRowHtml(getAnimationHeight(0), data), authorId));
2011-03-26 13:10:41 +00:00
row.animationStep = 0;
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
rowsFadingIn.push(row);
tr = $(getRowHtml(domId, getEmptyRowHtml(getAnimationHeight(ANIMATION_START)), authorId));
2011-03-26 13:10:41 +00:00
}
handleRowNode(tr, data);
2020-11-23 13:24:19 -05:00
$('table#otheruserstable').show();
if (position == 0) {
$('table#otheruserstable').prepend(tr);
} else {
2011-07-07 18:59:34 +01:00
rowNode(rowsPresent[position - 1]).after(tr);
2011-03-26 13:10:41 +00:00
}
2020-11-23 13:24:19 -05:00
if (animationPower != 0) {
2011-03-26 13:10:41 +00:00
scheduleAnimation();
}
handleOtherUserInputs();
return row;
}
function updateRow(position, data) {
2020-11-23 13:24:19 -05:00
const row = rowsPresent[position];
if (row) {
2011-03-26 13:10:41 +00:00
row.data = data;
handleRowData(row);
2020-11-23 13:24:19 -05:00
if (row.animationStep == 0) {
2011-03-26 13:10:41 +00:00
// not currently animating
2020-11-23 13:24:19 -05:00
const tr = rowNode(row);
replaceUserRowContents(tr, getAnimationHeight(0), row.data).find('td').css('opacity', (row.opacity === undefined ? 1 : row.opacity));
2011-03-26 13:10:41 +00:00
handleRowNode(tr, data);
handleOtherUserInputs();
}
}
}
function removeRow(position, animationPower) {
2011-03-26 13:10:41 +00:00
animationPower = (animationPower === undefined ? 4 : animationPower);
2020-11-23 13:24:19 -05:00
const row = rowsPresent[position];
if (row) {
2011-03-26 13:10:41 +00:00
rowsPresent.splice(position, 1); // remove
2020-11-23 13:24:19 -05:00
if (animationPower == 0) {
2011-03-26 13:10:41 +00:00
rowNode(row).remove();
2020-11-23 13:24:19 -05:00
} else {
2011-07-07 18:59:34 +01:00
row.animationStep = -row.animationStep; // use symmetry
2011-03-26 13:10:41 +00:00
row.animationPower = animationPower;
rowsFadingOut.push(row);
scheduleAnimation();
}
}
2020-04-06 14:31:07 +02:00
if (rowsPresent.length === 0) {
2020-11-23 13:24:19 -05:00
$('table#otheruserstable').hide();
2020-04-06 14:31:07 +02:00
}
2011-03-26 13:10:41 +00:00
}
// newPosition is position after the row has been removed
2011-07-07 18:59:34 +01:00
function moveRow(oldPosition, newPosition, animationPower) {
2011-03-26 13:10:41 +00:00
animationPower = (animationPower === undefined ? 1 : animationPower); // linear is best
2020-11-23 13:24:19 -05:00
const row = rowsPresent[oldPosition];
if (row && oldPosition != newPosition) {
const rowData = row.data;
2011-03-26 13:10:41 +00:00
removeRow(oldPosition, animationPower);
insertRow(newPosition, rowData, animationPower);
}
}
function animateStep() {
2011-03-26 13:10:41 +00:00
// animation must be symmetrical
2020-11-23 13:24:19 -05:00
for (var i = rowsFadingIn.length - 1; i >= 0; i--) { // backwards to allow removal
2011-03-26 13:10:41 +00:00
var row = rowsFadingIn[i];
var step = ++row.animationStep;
var animHeight = getAnimationHeight(step, row.animationPower);
var node = rowNode(row);
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
2020-11-23 13:24:19 -05:00
if (step <= -OPACITY_STEPS) {
node.find('td').height(animHeight);
} else if (step == -OPACITY_STEPS + 1) {
node.html(getUserRowHtml(animHeight, row.data)).find('td').css('opacity', baseOpacity * 1 / OPACITY_STEPS);
2011-03-26 13:10:41 +00:00
handleRowNode(node, row.data);
2020-11-23 13:24:19 -05:00
} else if (step < 0) {
node.find('td').css('opacity', baseOpacity * (OPACITY_STEPS - (-step)) / OPACITY_STEPS).height(animHeight);
} else if (step == 0) {
2011-03-26 13:10:41 +00:00
// set HTML in case modified during animation
2020-11-23 13:24:19 -05:00
node.html(getUserRowHtml(animHeight, row.data)).find('td').css('opacity', baseOpacity * 1).height(animHeight);
2011-03-26 13:10:41 +00:00
handleRowNode(node, row.data);
rowsFadingIn.splice(i, 1); // remove from set
}
}
2020-11-23 13:24:19 -05:00
for (var i = rowsFadingOut.length - 1; i >= 0; i--) { // backwards to allow removal
2011-03-26 13:10:41 +00:00
var row = rowsFadingOut[i];
var step = ++row.animationStep;
var node = rowNode(row);
var animHeight = getAnimationHeight(step, row.animationPower);
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
2020-11-23 13:24:19 -05:00
if (step < OPACITY_STEPS) {
node.find('td').css('opacity', baseOpacity * (OPACITY_STEPS - step) / OPACITY_STEPS).height(animHeight);
} else if (step == OPACITY_STEPS) {
2011-03-26 13:10:41 +00:00
node.html(getEmptyRowHtml(animHeight));
2020-11-23 13:24:19 -05:00
} else if (step <= ANIMATION_END) {
node.find('td').height(animHeight);
} else {
2011-03-26 13:10:41 +00:00
rowsFadingOut.splice(i, 1); // remove from set
node.remove();
}
}
handleOtherUserInputs();
return (rowsFadingIn.length > 0) || (rowsFadingOut.length > 0); // is more to do
}
2020-11-23 13:24:19 -05:00
const self = {
insertRow,
removeRow,
moveRow,
updateRow,
2011-03-26 13:10:41 +00:00
};
return self;
2020-11-23 13:24:19 -05:00
}()); // //////// rowManager
const otherUsersInfo = [];
const otherUsersData = [];
2011-03-26 13:10:41 +00:00
function rowManagerMakeNameEditor(jnode, userId) {
2020-11-23 13:24:19 -05:00
setUpEditable(jnode, () => {
const existingIndex = findExistingIndex(userId);
if (existingIndex >= 0) {
2011-03-26 13:10:41 +00:00
return otherUsersInfo[existingIndex].name || '';
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
return '';
}
2020-11-23 13:24:19 -05:00
}, (newName) => {
if (!newName) {
jnode.addClass('editempty');
2012-12-09 08:56:58 +01:00
jnode.val(_('pad.userlist.unnamed'));
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
jnode.attr('disabled', 'disabled');
pad.suggestUserName(userId, newName);
}
});
}
function findExistingIndex(userId) {
2020-11-23 13:24:19 -05:00
let existingIndex = -1;
for (let i = 0; i < otherUsersInfo.length; i++) {
if (otherUsersInfo[i].userId == userId) {
2011-03-26 13:10:41 +00:00
existingIndex = i;
break;
}
}
return existingIndex;
}
function setUpEditable(jqueryNode, valueGetter, valueSetter) {
2020-11-23 13:24:19 -05:00
jqueryNode.bind('focus', (evt) => {
const oldValue = valueGetter();
if (jqueryNode.val() !== oldValue) {
2011-03-26 13:10:41 +00:00
jqueryNode.val(oldValue);
}
2020-11-23 13:24:19 -05:00
jqueryNode.addClass('editactive').removeClass('editempty');
2011-03-26 13:10:41 +00:00
});
2020-11-23 13:24:19 -05:00
jqueryNode.bind('blur', (evt) => {
const newValue = jqueryNode.removeClass('editactive').val();
2011-03-26 13:10:41 +00:00
valueSetter(newValue);
});
2020-11-23 13:24:19 -05:00
padutils.bindEnterAndEscape(jqueryNode, () => {
2011-03-26 13:10:41 +00:00
jqueryNode.blur();
2020-11-23 13:24:19 -05:00
}, () => {
2011-03-26 13:10:41 +00:00
jqueryNode.val(valueGetter()).blur();
});
jqueryNode.removeAttr('disabled').addClass('editable');
}
2020-11-23 13:24:19 -05:00
const knocksToIgnore = {};
let guestPromptFlashState = 0;
const guestPromptFlash = padutils.makeAnimationScheduler(
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
() => {
const prompts = $('#guestprompts .guestprompt');
if (prompts.length == 0) {
return false; // no more to do
}
2011-07-07 18:59:34 +01:00
2020-11-23 13:24:19 -05:00
guestPromptFlashState = 1 - guestPromptFlashState;
if (guestPromptFlashState) {
prompts.css('background', '#ffa');
} else {
prompts.css('background', '#ffe');
}
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
return true;
}, 1000);
2011-03-26 13:10:41 +00:00
var pad = undefined;
2011-03-26 13:10:41 +00:00
var self = {
2020-11-23 13:24:19 -05:00
init(myInitialUserInfo, _pad) {
pad = _pad;
2011-03-26 13:10:41 +00:00
self.setMyUserInfo(myInitialUserInfo);
2011-04-07 21:08:41 +02:00
2020-11-23 13:24:19 -05:00
if ($('#online_count').length === 0) $('#editbar [data-key=showusers] > a').append('<span id="online_count">1</span>');
2020-11-23 13:24:19 -05:00
$('#otheruserstable tr').remove();
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
if (pad.getUserIsGuest()) {
$('#myusernameedit').addClass('myusernameedithoverable');
setUpEditable($('#myusernameedit'), () => myUserInfo.name || '', (newValue) => {
2011-07-07 18:59:34 +01:00
myUserInfo.name = newValue;
pad.notifyChangeName(newValue);
// wrap with setTimeout to do later because we get
// a double "blur" fire in IE...
2020-11-23 13:24:19 -05:00
window.setTimeout(() => {
2011-07-07 18:59:34 +01:00
self.renderMyUserInfo();
}, 0);
});
2011-03-26 13:10:41 +00:00
}
// color picker
2020-11-23 13:24:19 -05:00
$('#myswatchbox').click(showColorPicker);
$('#mycolorpicker .pickerswatchouter').click(function () {
$('#mycolorpicker .pickerswatchouter').removeClass('picked');
2011-03-26 13:10:41 +00:00
$(this).addClass('picked');
});
2020-11-23 13:24:19 -05:00
$('#mycolorpickersave').click(() => {
2011-03-26 13:10:41 +00:00
closeColorPicker(true);
});
2020-11-23 13:24:19 -05:00
$('#mycolorpickercancel').click(() => {
2011-03-26 13:10:41 +00:00
closeColorPicker(false);
});
//
},
2020-11-23 13:24:19 -05:00
usersOnline() {
// Returns an object of users who are currently online on this pad
2020-11-23 13:24:19 -05:00
const userList = [].concat(otherUsersInfo); // Make a copy of the otherUsersInfo, otherwise every call to users modifies the referenced array
// Now we need to add ourselves..
userList.push(myUserInfo);
return userList;
},
2020-11-23 13:24:19 -05:00
users() {
// Returns an object of users who have been on this pad
2020-11-23 13:24:19 -05:00
const userList = self.usersOnline();
// Now we add historical authors
2020-11-23 13:24:19 -05:00
const historical = clientVars.collab_client_vars.historicalAuthorData;
for (const key in historical) {
var userId = historical[key].userId;
// Check we don't already have this author in our array
var exists = false;
2020-11-23 13:24:19 -05:00
userList.forEach((user) => {
if (user.userId === userId) exists = true;
});
2020-11-23 13:24:19 -05:00
if (exists === false) {
userList.push(historical[key]);
}
}
return userList;
},
2020-11-23 13:24:19 -05:00
setMyUserInfo(info) {
// translate the colorId
if (typeof info.colorId === 'number') {
info.colorId = clientVars.colorPalette[info.colorId];
}
2011-07-07 18:59:34 +01:00
myUserInfo = $.extend(
2020-11-23 13:24:19 -05:00
{}, info);
2011-03-26 13:10:41 +00:00
2011-04-07 20:45:28 +02:00
self.renderMyUserInfo();
2011-03-26 13:10:41 +00:00
},
2020-11-23 13:24:19 -05:00
userJoinOrUpdate(info) {
if ((!info.userId) || (info.userId == myUserInfo.userId)) {
2011-03-26 13:10:41 +00:00
// not sure how this would happen
return;
}
hooks.callAll('userJoinOrUpdate', {
2020-11-23 13:24:19 -05:00
userInfo: info,
});
2020-11-23 13:24:19 -05:00
const userData = {};
userData.color = typeof info.colorId === 'number' ? clientVars.colorPalette[info.colorId] : info.colorId;
2011-03-26 13:10:41 +00:00
userData.name = info.name;
userData.status = '';
userData.activity = '';
userData.id = info.userId;
// Firefox ignores \n in title text; Safari does a linebreak
2011-07-07 18:59:34 +01:00
userData.titleText = [info.userAgent || '', info.ip || ''].join(' \n');
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
const existingIndex = findExistingIndex(info.userId);
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
let numUsersBesides = otherUsersInfo.length;
if (existingIndex >= 0) {
2011-03-26 13:10:41 +00:00
numUsersBesides--;
}
2020-11-23 13:24:19 -05:00
const newIndex = padutils.binarySearch(numUsersBesides, (n) => {
if (existingIndex >= 0 && n >= existingIndex) {
2011-03-26 13:10:41 +00:00
// pretend existingIndex isn't there
n++;
}
2020-11-23 13:24:19 -05:00
const infoN = otherUsersInfo[n];
const nameN = (infoN.name || '').toLowerCase();
const nameThis = (info.name || '').toLowerCase();
const idN = infoN.userId;
const idThis = info.userId;
2011-07-07 18:59:34 +01:00
return (nameN > nameThis) || (nameN == nameThis && idN > idThis);
2011-03-26 13:10:41 +00:00
});
2020-11-23 13:24:19 -05:00
if (existingIndex >= 0) {
2011-03-26 13:10:41 +00:00
// update
2020-11-23 13:24:19 -05:00
if (existingIndex == newIndex) {
2011-03-26 13:10:41 +00:00
otherUsersInfo[existingIndex] = info;
otherUsersData[existingIndex] = userData;
rowManager.updateRow(existingIndex, userData);
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
otherUsersInfo.splice(existingIndex, 1);
otherUsersData.splice(existingIndex, 1);
otherUsersInfo.splice(newIndex, 0, info);
otherUsersData.splice(newIndex, 0, userData);
rowManager.updateRow(existingIndex, userData);
rowManager.moveRow(existingIndex, newIndex);
}
2020-11-23 13:24:19 -05:00
} else {
2011-03-26 13:10:41 +00:00
otherUsersInfo.splice(newIndex, 0, info);
otherUsersData.splice(newIndex, 0, userData);
rowManager.insertRow(newIndex, userData);
}
2011-04-07 21:34:06 +02:00
self.updateNumberOfOnlineUsers();
2011-04-07 21:18:49 +02:00
},
2020-11-23 13:24:19 -05:00
updateNumberOfOnlineUsers() {
let online = 1; // you are always online!
for (let i = 0; i < otherUsersData.length; i++) {
if (otherUsersData[i].status == '') {
2011-04-07 21:18:49 +02:00
online++;
}
}
$('#online_count').text(online);
2011-07-07 18:59:34 +01:00
2011-04-07 21:18:49 +02:00
return online;
2011-03-26 13:10:41 +00:00
},
2020-11-23 13:24:19 -05:00
userLeave(info) {
const existingIndex = findExistingIndex(info.userId);
if (existingIndex >= 0) {
const userData = otherUsersData[existingIndex];
2011-03-26 13:10:41 +00:00
userData.status = 'Disconnected';
rowManager.updateRow(existingIndex, userData);
2020-11-23 13:24:19 -05:00
if (userData.leaveTimer) {
2011-03-26 13:10:41 +00:00
window.clearTimeout(userData.leaveTimer);
}
// set up a timer that will only fire if no leaves,
// joins, or updates happen for this user in the
// next N seconds, to remove the user from the list.
2020-11-23 13:24:19 -05:00
const thisUserId = info.userId;
var thisLeaveTimer = window.setTimeout(() => {
const newExistingIndex = findExistingIndex(thisUserId);
if (newExistingIndex >= 0) {
const newUserData = otherUsersData[newExistingIndex];
if (newUserData.status == 'Disconnected' && newUserData.leaveTimer == thisLeaveTimer) {
2011-03-26 13:10:41 +00:00
otherUsersInfo.splice(newExistingIndex, 1);
otherUsersData.splice(newExistingIndex, 1);
rowManager.removeRow(newExistingIndex);
2013-06-17 14:32:11 +02:00
hooks.callAll('userLeave', {
2020-11-23 13:24:19 -05:00
userInfo: info,
2013-06-17 14:32:11 +02:00
});
2011-03-26 13:10:41 +00:00
}
}
}, 8000); // how long to wait
userData.leaveTimer = thisLeaveTimer;
}
2011-07-07 18:59:34 +01:00
self.updateNumberOfOnlineUsers();
2011-03-26 13:10:41 +00:00
},
2020-11-23 13:24:19 -05:00
showGuestPrompt(userId, displayName) {
if (knocksToIgnore[userId]) {
2011-03-26 13:10:41 +00:00
return;
}
2020-11-23 13:24:19 -05:00
const encodedUserId = padutils.encodeUserId(userId);
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
const actionName = `hide-guest-prompt-${encodedUserId}`;
2011-03-26 13:10:41 +00:00
padutils.cancelActions(actionName);
2020-11-23 13:24:19 -05:00
let box = $(`#guestprompt-${encodedUserId}`);
if (box.length == 0) {
2011-03-26 13:10:41 +00:00
// make guest prompt box
2020-11-23 13:24:19 -05:00
box = $(`<div id="${padutils.escapeHtml(`guestprompt-${encodedUserId}`)}" class="guestprompt"><div class="choices"><a href="${padutils.escapeHtml(`javascript:void(require(${JSON.stringify(module.id)}).paduserlist.answerGuestPrompt(${JSON.stringify(encodedUserId)},false))`)}">${_('pad.userlist.deny')}</a> <a href="${padutils.escapeHtml(`javascript:void(require(${JSON.stringify(module.id)}).paduserlist.answerGuestPrompt(${JSON.stringify(encodedUserId)},true))`)}">${_('pad.userlist.approve')}</a></div><div class="guestname"><strong>${_('pad.userlist.guest')}:</strong> ${padutils.escapeHtml(displayName)}</div></div>`);
$('#guestprompts').append(box);
} else {
2011-03-26 13:10:41 +00:00
// update display name
2020-11-23 13:24:19 -05:00
box.find('.guestname').html(`<strong>${_('pad.userlist.guest')}:</strong> ${padutils.escapeHtml(displayName)}`);
2011-03-26 13:10:41 +00:00
}
2020-11-23 13:24:19 -05:00
const hideLater = padutils.getCancellableAction(actionName, () => {
2011-03-26 13:10:41 +00:00
self.removeGuestPrompt(userId);
});
window.setTimeout(hideLater, 15000); // time-out with no knock
guestPromptFlash.scheduleAnimation();
},
2020-11-23 13:24:19 -05:00
removeGuestPrompt(userId) {
const box = $(`#guestprompt-${padutils.encodeUserId(userId)}`);
2011-03-26 13:10:41 +00:00
// remove ID now so a new knock by same user gets new, unfaded box
2020-11-23 13:24:19 -05:00
box.removeAttr('id').fadeOut('fast', () => {
2011-03-26 13:10:41 +00:00
box.remove();
});
knocksToIgnore[userId] = true;
2020-11-23 13:24:19 -05:00
window.setTimeout(() => {
2011-03-26 13:10:41 +00:00
delete knocksToIgnore[userId];
}, 5000);
},
2020-11-23 13:24:19 -05:00
answerGuestPrompt(encodedUserId, approve) {
const guestId = padutils.decodeUserId(encodedUserId);
2011-03-26 13:10:41 +00:00
2020-11-23 13:24:19 -05:00
const msg = {
2011-03-26 13:10:41 +00:00
type: 'guestanswer',
authId: pad.getUserId(),
2020-11-23 13:24:19 -05:00
guestId,
answer: (approve ? 'approved' : 'denied'),
2011-03-26 13:10:41 +00:00
};
pad.sendClientMessage(msg);
self.removeGuestPrompt(guestId);
2011-04-07 20:45:28 +02:00
},
2020-11-23 13:24:19 -05:00
renderMyUserInfo() {
if (myUserInfo.name) {
$('#myusernameedit').removeClass('editempty').val(myUserInfo.name);
} else {
$('#myusernameedit').attr('placeholder', html10n.get('pad.userlist.entername'));
2011-04-07 20:45:28 +02:00
}
2020-11-23 13:24:19 -05:00
if (colorPickerOpen) {
$('#myswatchbox').addClass('myswatchboxunhoverable').removeClass('myswatchboxhoverable');
} else {
$('#myswatchbox').addClass('myswatchboxhoverable').removeClass('myswatchboxunhoverable');
2011-04-07 20:45:28 +02:00
}
2020-11-23 13:24:19 -05:00
$('#myswatch').css({'background-color': myUserInfo.colorId});
if (browser.msie && parseInt(browser.version) <= 8) {
2020-11-23 13:24:19 -05:00
$('li[data-key=showusers] > a').css({'box-shadow': `inset 0 0 30px ${myUserInfo.colorId}`, 'background-color': myUserInfo.colorId});
} else {
$('li[data-key=showusers] > a').css({'box-shadow': `inset 0 0 30px ${myUserInfo.colorId}`});
}
2020-11-23 13:24:19 -05:00
},
2011-03-26 13:10:41 +00:00
};
return self;
2011-04-07 20:45:28 +02:00
}());
function getColorPickerSwatchIndex(jnode) {
2011-07-07 18:59:34 +01:00
// return Number(jnode.get(0).className.match(/\bn([0-9]+)\b/)[1])-1;
2020-11-23 13:24:19 -05:00
return $('#colorpickerswatches li').index(jnode);
2011-04-07 20:45:28 +02:00
}
2011-07-07 18:59:34 +01:00
function closeColorPicker(accept) {
2020-11-23 13:24:19 -05:00
if (accept) {
var newColor = $('#mycolorpickerpreview').css('background-color');
const parts = newColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
2011-08-20 18:22:10 +01:00
// parts now should be ["rgb(0, 70, 255", "0", "70", "255"]
if (parts) {
delete (parts[0]);
2020-11-23 13:24:19 -05:00
for (let i = 1; i <= 3; ++i) {
parts[i] = parseInt(parts[i]).toString(16);
if (parts[i].length == 1) parts[i] = `0${parts[i]}`;
}
2020-11-23 13:24:19 -05:00
var newColor = `#${parts.join('')}`; // "0070ff"
2011-04-07 20:45:28 +02:00
}
2011-08-20 18:22:10 +01:00
myUserInfo.colorId = newColor;
pad.notifyChangeColor(newColor);
2011-04-07 20:45:28 +02:00
paduserlist.renderMyUserInfo();
2020-11-23 13:24:19 -05:00
} else {
// pad.notifyChangeColor(previousColorId);
// paduserlist.renderMyUserInfo();
2011-04-07 20:45:28 +02:00
}
2011-07-07 18:59:34 +01:00
2011-04-07 20:45:28 +02:00
colorPickerOpen = false;
2020-11-23 13:24:19 -05:00
$('#mycolorpicker').removeClass('popup-show');
2011-04-07 20:45:28 +02:00
}
function showColorPicker() {
2011-07-07 18:59:34 +01:00
previousColorId = myUserInfo.colorId;
2020-11-23 13:24:19 -05:00
$.farbtastic('#colorpicker').setColor(myUserInfo.colorId);
2011-04-07 20:45:28 +02:00
2020-11-23 13:24:19 -05:00
if (!colorPickerOpen) {
const palette = pad.getColorPalette();
2011-04-07 20:45:28 +02:00
2020-11-23 13:24:19 -05:00
if (!colorPickerSetup) {
const colorsList = $('#colorpickerswatches');
for (let i = 0; i < palette.length; i++) {
const li = $('<li>', {
style: `background: ${palette[i]};`,
2011-04-07 20:45:28 +02:00
});
2011-07-07 18:59:34 +01:00
2011-04-07 20:45:28 +02:00
li.appendTo(colorsList);
2011-07-07 18:59:34 +01:00
2020-11-23 13:24:19 -05:00
li.bind('click', (event) => {
$('#colorpickerswatches li').removeClass('picked');
$(event.target).addClass('picked');
2011-07-07 18:59:34 +01:00
2020-11-23 13:24:19 -05:00
const newColorId = getColorPickerSwatchIndex($('#colorpickerswatches .picked'));
2011-04-07 20:45:28 +02:00
pad.notifyChangeColor(newColorId);
});
}
2011-07-07 18:59:34 +01:00
2011-04-07 20:45:28 +02:00
colorPickerSetup = true;
}
2011-07-07 18:59:34 +01:00
2020-11-23 13:24:19 -05:00
$('#mycolorpicker').addClass('popup-show');
2011-04-07 20:45:28 +02:00
colorPickerOpen = true;
2011-07-07 18:59:34 +01:00
2020-11-23 13:24:19 -05:00
$('#colorpickerswatches li').removeClass('picked');
$($('#colorpickerswatches li')[myUserInfo.colorId]).addClass('picked'); // seems weird
2011-07-07 18:59:34 +01:00
}
2011-07-06 14:08:20 +01:00
}
exports.paduserlist = paduserlist;