lint: pad prefix files (#4577)

* lint: pad_connectionstatus

* lint: pad_utils

* lint: pad_userlist.js -- still WIP

* shift underscore not to be in require but to be used from window

* lint: pad_modals

* pad_impexp.js

* lint: more errors done

* lint: auto reconn

* lint: pad_editor

* lint: finish auto reconn

* lint: imp exp rework

* lint: import

* lint: pad.js nearly done but pizza here...

* lint: clientVars global query

* put clientVars in window

* Revert incorrect lint fixes

* Properly fix guard-for-in lint errors

* Properly fix no-unused-vars error regarding `gritter`

* Refine lint fixes

Co-authored-by: Richard Hansen <rhansen@rhansen.org>
pull/4596/head
John McLear 2020-12-20 07:15:58 +00:00 committed by GitHub
parent 34ee77993f
commit 0362d3b05d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 512 additions and 459 deletions

View File

@ -1,3 +1,5 @@
'use strict';
/**
* 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.
@ -20,8 +22,6 @@
* limitations under the License.
*/
/* global $, window */
let socket;
// These jQuery things should create local references, but for now `require()`
@ -43,24 +43,13 @@ const padsavedrevs = require('./pad_savedrevs');
const paduserlist = require('./pad_userlist').paduserlist;
const padutils = require('./pad_utils').padutils;
const colorutils = require('./colorutils').colorutils;
var randomString = require('./pad_utils').randomString;
const gritter = require('./gritter').gritter;
const randomString = require('./pad_utils').randomString;
require('./gritter'); // Mutates the jQuery object to make $.gritter available.
const hooks = require('./pluginfw/hooks');
let receivedClientVars = false;
function randomString() {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const string_length = 20;
let randomstring = '';
for (let i = 0; i < string_length; i++) {
const rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return `t.${randomstring}`;
}
// This array represents all GET-parameters which can be used to change a setting.
// name: the parameter-name, eg `?noColors=true` => `noColors`
// checkVal: the callback is only executed when
@ -68,26 +57,101 @@ function randomString() {
// * the parameter was supplied and checkVal is null
// callback: the function to call when all above succeeds, `val` is the value supplied by the user
const getParameters = [
{name: 'noColors', checkVal: 'true', callback(val) { settings.noColors = true; $('#clearAuthorship').hide(); }},
{name: 'showControls', checkVal: 'true', callback(val) { $('#editbar').css('display', 'flex'); }},
{name: 'showChat', checkVal: null, callback(val) { if (val === 'false') { settings.hideChat = true; chat.hide(); $('#chaticon').hide(); } }},
{name: 'showLineNumbers', checkVal: 'false', callback(val) { settings.LineNumbersDisabled = true; }},
{name: 'useMonospaceFont', checkVal: 'true', callback(val) { settings.useMonospaceFontGlobal = true; }},
// If the username is set as a parameter we should set a global value that we can call once we have initiated the pad.
{name: 'userName', checkVal: null, callback(val) { settings.globalUserName = decodeURIComponent(val); clientVars.userName = decodeURIComponent(val); }},
// If the userColor is set as a parameter, set a global value to use once we have initiated the pad.
{name: 'userColor', checkVal: null, callback(val) { settings.globalUserColor = decodeURIComponent(val); clientVars.userColor = decodeURIComponent(val); }},
{name: 'rtl', checkVal: 'true', callback(val) { settings.rtlIsTrue = true; }},
{name: 'alwaysShowChat', checkVal: 'true', callback(val) { if (!settings.hideChat) chat.stickToScreen(); }},
{name: 'chatAndUsers', checkVal: 'true', callback(val) { chat.chatAndUsers(); }},
{name: 'lang', checkVal: null, callback(val) { window.html10n.localize([val, 'en']); Cookies.set('language', val); }},
{
name: 'noColors',
checkVal: 'true',
callback: (val) => {
settings.noColors = true;
$('#clearAuthorship').hide();
},
},
{
name: 'showControls',
checkVal: 'true',
callback: (val) => {
$('#editbar').css('display', 'flex');
},
},
{
name: 'showChat',
checkVal: null,
callback: (val) => {
if (val === 'false') {
settings.hideChat = true;
chat.hide();
$('#chaticon').hide();
}
},
},
{
name: 'showLineNumbers',
checkVal: 'false',
callback: (val) => {
settings.LineNumbersDisabled = true;
},
},
{
name: 'useMonospaceFont',
checkVal: 'true',
callback: (val) => {
settings.useMonospaceFontGlobal = true;
},
},
// If the username is set as a parameter we should set a global value that we can call once we
// have initiated the pad.
{
name: 'userName',
checkVal: null,
callback: (val) => {
settings.globalUserName = decodeURIComponent(val);
clientVars.userName = decodeURIComponent(val);
},
},
// If the userColor is set as a parameter, set a global value to use once we have initiated the
// pad.
{
name: 'userColor',
checkVal: null,
callback: (val) => {
settings.globalUserColor = decodeURIComponent(val);
clientVars.userColor = decodeURIComponent(val);
},
},
{
name: 'rtl',
checkVal: 'true',
callback: (val) => {
settings.rtlIsTrue = true;
},
},
{
name: 'alwaysShowChat',
checkVal: 'true',
callback: (val) => {
if (!settings.hideChat) chat.stickToScreen();
},
},
{
name: 'chatAndUsers',
checkVal: 'true',
callback: (val) => {
chat.chatAndUsers();
},
},
{
name: 'lang',
checkVal: null,
callback: (val) => {
window.html10n.localize([val, 'en']);
Cookies.set('language', val);
},
},
];
function getParams() {
const getParams = () => {
// Tries server enforced options first..
for (var i = 0; i < getParameters.length; i++) {
var setting = getParameters[i];
var value = clientVars.padOptions[setting.name];
for (const setting of getParameters) {
const value = clientVars.padOptions[setting.name];
if (value.toString() === setting.checkVal) {
setting.callback(value);
}
@ -96,19 +160,18 @@ function getParams() {
// Then URL applied stuff
const params = getUrlVars();
for (var i = 0; i < getParameters.length; i++) {
var setting = getParameters[i];
var value = params[setting.name];
for (const setting of getParameters) {
const value = params[setting.name];
if (value && (value == setting.checkVal || setting.checkVal == null)) {
if (value && (value === setting.checkVal || setting.checkVal == null)) {
setting.callback(value);
}
}
}
};
function getUrlVars() {
const vars = []; let
hash;
const getUrlVars = () => {
const vars = [];
let hash;
const hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (let i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
@ -116,12 +179,13 @@ function getUrlVars() {
vars[hash[0]] = hash[1];
}
return vars;
}
};
function sendClientReady(isReconnect, messageType) {
const sendClientReady = (isReconnect, messageType) => {
messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY';
let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1);
padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces
// unescape neccesary due to Safari and Opera interpretation of spaces
padId = decodeURIComponent(padId);
if (!isReconnect) {
const titleArray = document.title.split('|');
@ -151,12 +215,12 @@ function sendClientReady(isReconnect, messageType) {
}
socket.json.send(msg);
}
};
function handshake() {
const handshake = () => {
const loc = document.location;
// get the correct port
const port = loc.port == '' ? (loc.protocol == 'https:' ? 443 : 80) : loc.port;
const port = loc.port === '' ? (loc.protocol === 'https:' ? 443 : 80) : loc.port;
// create the url
const url = `${loc.protocol}//${loc.hostname}:${port}/`;
// find out in which subfolder we are
@ -211,12 +275,10 @@ function handshake() {
throw new Error(`socket.io connection error: ${JSON.stringify(error)}`);
});
let initalized = false;
socket.on('message', (obj) => {
// the access was not granted, give the user a message
if (obj.accessStatus) {
if (obj.accessStatus == 'deny') {
if (obj.accessStatus === 'deny') {
$('#loading').hide();
$('#permissionDenied').show();
@ -226,18 +288,15 @@ function handshake() {
$('#editorloadingbox').show();
}
}
}
// if we haven't recieved the clientVars yet, then this message should it be
else if (!receivedClientVars && obj.type == 'CLIENT_VARS') {
} else if (!receivedClientVars && obj.type === 'CLIENT_VARS') {
// if we haven't recieved the clientVars yet, then this message should it be
receivedClientVars = true;
// set some client vars
clientVars = obj.data;
window.clientVars = obj.data;
// initalize the pad
pad._afterHandshake();
initalized = true;
if (clientVars.readonly) {
chat.hide();
@ -255,63 +314,61 @@ function handshake() {
});
// If the LineNumbersDisabled value is set to true then we need to hide the Line Numbers
if (settings.LineNumbersDisabled == true) {
if (settings.LineNumbersDisabled === true) {
pad.changeViewOption('showLineNumbers', false);
}
// If the noColors value is set to true then we need to hide the background colors on the ace spans
if (settings.noColors == true) {
// If the noColors value is set to true then we need to
// hide the background colors on the ace spans
if (settings.noColors === true) {
pad.changeViewOption('noColors', true);
}
if (settings.rtlIsTrue == true) {
if (settings.rtlIsTrue === true) {
pad.changeViewOption('rtlIsTrue', true);
}
// If the Monospacefont value is set to true then change it to monospace.
if (settings.useMonospaceFontGlobal == true) {
if (settings.useMonospaceFontGlobal === true) {
pad.changeViewOption('padFontFamily', 'monospace');
}
// if the globalUserName value is set we need to tell the server and the client about the new authorname
// if the globalUserName value is set we need to tell the server and
// the client about the new authorname
if (settings.globalUserName !== false) {
pad.notifyChangeName(settings.globalUserName); // Notifies the server
pad.myUserInfo.name = settings.globalUserName;
$('#myusernameedit').val(settings.globalUserName); // Updates the current users UI
}
if (settings.globalUserColor !== false && colorutils.isCssHex(settings.globalUserColor)) {
// Add a 'globalUserColor' property to myUserInfo, so collabClient knows we have a query parameter.
// Add a 'globalUserColor' property to myUserInfo,
// so collabClient knows we have a query parameter.
pad.myUserInfo.globalUserColor = settings.globalUserColor;
pad.notifyChangeColor(settings.globalUserColor); // Updates pad.myUserInfo.colorId
paduserlist.setMyUserInfo(pad.myUserInfo);
}
}
// This handles every Message after the clientVars
else {
// this message advices the client to disconnect
if (obj.disconnect) {
padconnectionstatus.disconnected(obj.disconnect);
socket.disconnect();
} else if (obj.disconnect) {
padconnectionstatus.disconnected(obj.disconnect);
socket.disconnect();
// block user from making any change to the pad
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
// block user from making any change to the pad
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
return;
} else {
pad.collabClient.handleMessageFromServer(obj);
}
return;
} else {
pad.collabClient.handleMessageFromServer(obj);
}
});
// Bind the colorpicker
const fb = $('#colorpicker').farbtastic({callback: '#mycolorpickerpreview', width: 220});
$('#colorpicker').farbtastic({callback: '#mycolorpickerpreview', width: 220});
// Bind the read only button
$('#readonlyinput').on('click', () => {
padeditbar.setEmbedLinks();
});
}
};
var pad = {
const pad = {
// don't access these directly from outside this file, except
// for debugging
collabClient: null,
@ -322,44 +379,28 @@ var pad = {
padOptions: {},
// these don't require init; clientVars should all go through here
getPadId() {
return clientVars.padId;
},
getClientIp() {
return clientVars.clientIp;
},
getColorPalette() {
return clientVars.colorPalette;
},
getIsDebugEnabled() {
return clientVars.debugEnabled;
},
getPrivilege(name) {
return clientVars.accountPrivs[name];
},
getUserId() {
return pad.myUserInfo.userId;
},
getUserName() {
return pad.myUserInfo.name;
},
userList() {
return paduserlist.users();
},
switchToPad(padId) {
let newHref = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname) || clientVars.padId;
getPadId: () => clientVars.padId,
getClientIp: () => clientVars.clientIp,
getColorPalette: () => clientVars.colorPalette,
getIsDebugEnabled: () => clientVars.debugEnabled,
getPrivilege: (name) => clientVars.accountPrivs[name],
getUserId: () => pad.myUserInfo.userId,
getUserName: () => pad.myUserInfo.name,
userList: () => paduserlist.users(),
switchToPad: (padId) => {
let newHref = new RegExp(/.*\/p\/[^/]+/).exec(document.location.pathname) || clientVars.padId;
newHref = newHref[0];
const options = clientVars.padOptions;
if (typeof options !== 'undefined' && options != null) {
var option_str = [];
const optionArr = [];
$.each(options, (k, v) => {
const str = `${k}=${v}`;
option_str.push(str);
optionArr.push(str);
});
var option_str = option_str.join('&');
const optionStr = optionArr.join('&');
newHref = `${newHref}?${option_str}`;
newHref = `${newHref}?${optionStr}`;
}
// destroy old pad from DOM
@ -373,21 +414,21 @@ var pad = {
window.history.pushState('', '', newHref);
receivedClientVars = false;
sendClientReady(false, 'SWITCH_TO_PAD');
} else // fallback
{
} else {
// fallback
window.location.href = newHref;
}
},
sendClientMessage(msg) {
sendClientMessage: (msg) => {
pad.collabClient.sendClientMessage(msg);
},
init() {
init: () => {
padutils.setupGlobalExceptionHandler();
$(document).ready(() => {
// start the custom js
if (typeof customStart === 'function') customStart();
if (typeof customStart === 'function') customStart(); // eslint-disable-line no-undef
handshake();
// To use etherpad you have to allow cookies.
@ -406,7 +447,6 @@ var pad = {
pad.initTime = +(new Date());
pad.padOptions = clientVars.initialOptions;
// order of inits is important here:
pad.myUserInfo = {
userId: clientVars.userId,
name: clientVars.userName,
@ -414,18 +454,60 @@ var pad = {
colorId: clientVars.userColor,
};
const postAceInit = () => {
padeditbar.init();
setTimeout(() => {
padeditor.ace.focus();
}, 0);
// if we have a cookie for always showing chat then show it
if (padcookie.getPref('chatAlwaysVisible')) {
chat.stickToScreen(true); // stick it to the screen
$('#options-stickychat').prop('checked', true); // set the checkbox to on
}
// if we have a cookie for always showing chat then show it
if (padcookie.getPref('chatAndUsers')) {
chat.chatAndUsers(true); // stick it to the screen
$('#options-chatandusers').prop('checked', true); // set the checkbox to on
}
if (padcookie.getPref('showAuthorshipColors') === false) {
pad.changeViewOption('showAuthorColors', false);
}
if (padcookie.getPref('showLineNumbers') === false) {
pad.changeViewOption('showLineNumbers', false);
}
if (padcookie.getPref('rtlIsTrue') === true) {
pad.changeViewOption('rtlIsTrue', true);
}
pad.changeViewOption('padFontFamily', padcookie.getPref('padFontFamily'));
$('#viewfontmenu').val(padcookie.getPref('padFontFamily')).niceSelect('update');
// Prevent sticky chat or chat and users to be checked for mobiles
const checkChatAndUsersVisibility = (x) => {
if (x.matches) { // If media query matches
$('#options-chatandusers:checked').click();
$('#options-stickychat:checked').click();
}
};
const mobileMatch = window.matchMedia('(max-width: 800px)');
mobileMatch.addListener(checkChatAndUsersVisibility); // check if window resized
setTimeout(() => { checkChatAndUsersVisibility(mobileMatch); }, 0); // check now after load
$('#editorcontainer').addClass('initialized');
hooks.aCallAll('postAceInit', {ace: padeditor.ace, pad});
};
// order of inits is important here:
padimpexp.init(this);
padsavedrevs.init(this);
padeditor.init(postAceInit, pad.padOptions.view || {}, this);
paduserlist.init(pad.myUserInfo, this);
padconnectionstatus.init();
padmodals.init(this);
pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, {
colorPalette: pad.getColorPalette(),
}, pad);
pad.collabClient = getCollabClient(
padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo,
{colorPalette: pad.getColorPalette()}, pad);
pad.collabClient.setOnUserJoin(pad.handleUserJoin);
pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate);
pad.collabClient.setOnUserLeave(pad.handleUserLeave);
@ -434,68 +516,27 @@ var pad = {
pad.collabClient.setOnInternalAction(pad.handleCollabAction);
// load initial chat-messages
if (clientVars.chatHead != -1) {
if (clientVars.chatHead !== -1) {
const chatHead = clientVars.chatHead;
const start = Math.max(chatHead - 100, 0);
pad.collabClient.sendMessage({type: 'GET_CHAT_MESSAGES', start, end: chatHead});
} else // there are no messages
{
} else {
// there are no messages
$('#chatloadmessagesbutton').css('display', 'none');
}
function postAceInit() {
padeditbar.init();
setTimeout(() => {
padeditor.ace.focus();
}, 0);
if (padcookie.getPref('chatAlwaysVisible')) { // if we have a cookie for always showing chat then show it
chat.stickToScreen(true); // stick it to the screen
$('#options-stickychat').prop('checked', true); // set the checkbox to on
}
if (padcookie.getPref('chatAndUsers')) { // if we have a cookie for always showing chat then show it
chat.chatAndUsers(true); // stick it to the screen
$('#options-chatandusers').prop('checked', true); // set the checkbox to on
}
if (padcookie.getPref('showAuthorshipColors') == false) {
pad.changeViewOption('showAuthorColors', false);
}
if (padcookie.getPref('showLineNumbers') == false) {
pad.changeViewOption('showLineNumbers', false);
}
if (padcookie.getPref('rtlIsTrue') == true) {
pad.changeViewOption('rtlIsTrue', true);
}
pad.changeViewOption('padFontFamily', padcookie.getPref('padFontFamily'));
$('#viewfontmenu').val(padcookie.getPref('padFontFamily')).niceSelect('update');
// Prevent sticky chat or chat and users to be checked for mobiles
function checkChatAndUsersVisibility(x) {
if (x.matches) { // If media query matches
$('#options-chatandusers:checked').click();
$('#options-stickychat:checked').click();
}
}
const mobileMatch = window.matchMedia('(max-width: 800px)');
mobileMatch.addListener(checkChatAndUsersVisibility); // check if window resized
setTimeout(() => { checkChatAndUsersVisibility(mobileMatch); }, 0); // check now after load
$('#editorcontainer').addClass('initialized');
hooks.aCallAll('postAceInit', {ace: padeditor.ace, pad});
}
},
dispose() {
dispose: () => {
padeditor.dispose();
},
notifyChangeName(newName) {
notifyChangeName: (newName) => {
pad.myUserInfo.name = newName;
pad.collabClient.updateUserInfo(pad.myUserInfo);
},
notifyChangeColor(newColorId) {
notifyChangeColor: (newColorId) => {
pad.myUserInfo.colorId = newColorId;
pad.collabClient.updateUserInfo(pad.myUserInfo);
},
changePadOption(key, value) {
changePadOption: (key, value) => {
const options = {};
options[key] = value;
pad.handleOptionsChange(options);
@ -506,32 +547,30 @@ var pad = {
changedBy: pad.myUserInfo.name || 'unnamed',
});
},
changeViewOption(key, value) {
changeViewOption: (key, value) => {
const options = {
view: {},
};
options.view[key] = value;
pad.handleOptionsChange(options);
},
handleOptionsChange(opts) {
handleOptionsChange: (opts) => {
// opts object is a full set of options or just
// some options to change
if (opts.view) {
if (!pad.padOptions.view) {
pad.padOptions.view = {};
}
for (const k in opts.view) {
pad.padOptions.view[k] = opts.view[k];
padcookie.setPref(k, opts.view[k]);
for (const [k, v] of Object.entries(opts.view)) {
pad.padOptions.view[k] = v;
padcookie.setPref(k, v);
}
padeditor.setViewOptions(pad.padOptions.view);
}
},
getPadOptions() {
// caller shouldn't mutate the object
return pad.padOptions;
},
suggestUserName(userId, name) {
// caller shouldn't mutate the object
getPadOptions: () => pad.padOptions,
suggestUserName: (userId, name) => {
pad.collabClient.sendClientMessage(
{
type: 'suggestUserName',
@ -539,31 +578,31 @@ var pad = {
newName: name,
});
},
handleUserJoin(userInfo) {
handleUserJoin: (userInfo) => {
paduserlist.userJoinOrUpdate(userInfo);
},
handleUserUpdate(userInfo) {
handleUserUpdate: (userInfo) => {
paduserlist.userJoinOrUpdate(userInfo);
},
handleUserLeave(userInfo) {
handleUserLeave: (userInfo) => {
paduserlist.userLeave(userInfo);
},
handleClientMessage(msg) {
if (msg.type == 'suggestUserName') {
if (msg.unnamedId == pad.myUserInfo.userId && msg.newName && !pad.myUserInfo.name) {
handleClientMessage: (msg) => {
if (msg.type === 'suggestUserName') {
if (msg.unnamedId === pad.myUserInfo.userId && msg.newName && !pad.myUserInfo.name) {
pad.notifyChangeName(msg.newName);
paduserlist.setMyUserInfo(pad.myUserInfo);
}
} else if (msg.type == 'newRevisionList') {
} else if (msg.type === 'newRevisionList') {
padsavedrevs.newRevisionList(msg.revisionList);
} else if (msg.type == 'revisionLabel') {
} else if (msg.type === 'revisionLabel') {
padsavedrevs.newRevisionList(msg.revisionList);
} else if (msg.type == 'padoptions') {
} else if (msg.type === 'padoptions') {
const opts = msg.options;
pad.handleOptionsChange(opts);
}
},
dmesg(m) {
dmesg: (m) => {
if (pad.getIsDebugEnabled()) {
const djs = $('#djs').get(0);
const wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height()) >= -20);
@ -573,31 +612,30 @@ var pad = {
}
}
},
handleChannelStateChange(newState, message) {
handleChannelStateChange: (newState, message) => {
const oldFullyConnected = !!padconnectionstatus.isFullyConnected();
const wasConnecting = (padconnectionstatus.getStatus().what == 'connecting');
if (newState == 'CONNECTED') {
const wasConnecting = (padconnectionstatus.getStatus().what === 'connecting');
if (newState === 'CONNECTED') {
padeditor.enable();
padeditbar.enable();
padimpexp.enable();
padconnectionstatus.connected();
} else if (newState == 'RECONNECTING') {
} else if (newState === 'RECONNECTING') {
padeditor.disable();
padeditbar.disable();
padimpexp.disable();
padconnectionstatus.reconnecting();
} else if (newState == 'DISCONNECTED') {
} else if (newState === 'DISCONNECTED') {
pad.diagnosticInfo.disconnectedMessage = message;
pad.diagnosticInfo.padId = pad.getPadId();
pad.diagnosticInfo.socket = {};
// we filter non objects from the socket object and put them in the diagnosticInfo
// this ensures we have no cyclic data - this allows us to stringify the data
for (const i in socket.socket) {
const value = socket.socket[i];
for (const [i, value] of Object.entries(socket.socket || {})) {
const type = typeof value;
if (type == 'string' || type == 'number') {
if (type === 'string' || type === 'number') {
pad.diagnosticInfo.socket[i] = value;
}
}
@ -613,11 +651,11 @@ var pad = {
padconnectionstatus.disconnected(message);
}
const newFullyConnected = !!padconnectionstatus.isFullyConnected();
if (newFullyConnected != oldFullyConnected) {
if (newFullyConnected !== oldFullyConnected) {
pad.handleIsFullyConnected(newFullyConnected, wasConnecting);
}
},
handleIsFullyConnected(isConnected, isInitialConnect) {
handleIsFullyConnected: (isConnected, isInitialConnect) => {
pad.determineChatVisibility(isConnected && !isInitialConnect);
pad.determineChatAndUsersVisibility(isConnected && !isInitialConnect);
pad.determineAuthorshipColorsVisibility();
@ -625,7 +663,7 @@ var pad = {
padeditbar.toggleDropDown('none');
}, 1000);
},
determineChatVisibility(asNowConnectedFeedback) {
determineChatVisibility: (asNowConnectedFeedback) => {
const chatVisCookie = padcookie.getPref('chatAlwaysVisible');
if (chatVisCookie) { // if the cookie is set for chat always visible
chat.stickToScreen(true); // stick it to the screen
@ -634,7 +672,7 @@ var pad = {
$('#options-stickychat').prop('checked', false); // set the checkbox for off
}
},
determineChatAndUsersVisibility(asNowConnectedFeedback) {
determineChatAndUsersVisibility: (asNowConnectedFeedback) => {
const chatAUVisCookie = padcookie.getPref('chatAndUsersVisible');
if (chatAUVisCookie) { // if the cookie is set for chat always visible
chat.chatAndUsers(true); // stick it to the screen
@ -643,7 +681,7 @@ var pad = {
$('#options-chatandusers').prop('checked', false); // set the checkbox for off
}
},
determineAuthorshipColorsVisibility() {
determineAuthorshipColorsVisibility: () => {
const authColCookie = padcookie.getPref('showAuthorshipColors');
if (authColCookie) {
pad.changeViewOption('showAuthorColors', true);
@ -652,14 +690,14 @@ var pad = {
$('#options-colorscheck').prop('checked', false);
}
},
handleCollabAction(action) {
if (action == 'commitPerformed') {
handleCollabAction: (action) => {
if (action === 'commitPerformed') {
padeditbar.setSyncStatus('syncing');
} else if (action == 'newlyIdle') {
} else if (action === 'newlyIdle') {
padeditbar.setSyncStatus('done');
}
},
asyncSendDiagnosticInfo() {
asyncSendDiagnosticInfo: () => {
window.setTimeout(() => {
$.ajax(
{
@ -668,32 +706,29 @@ var pad = {
data: {
diagnosticInfo: JSON.stringify(pad.diagnosticInfo),
},
success() {},
error() {},
success: () => {},
error: () => {},
});
}, 0);
},
forceReconnect() {
forceReconnect: () => {
$('form#reconnectform input.padId').val(pad.getPadId());
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
$('form#reconnectform input.diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
$('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
$('form#reconnectform input.missedChanges')
.val(JSON.stringify(pad.collabClient.getMissedChanges()));
$('form#reconnectform').submit();
},
// this is called from code put into a frame from the server:
handleImportExportFrameCall(callName, varargs) {
handleImportExportFrameCall: (callName, varargs) => {
padimpexp.handleFrameCall.call(padimpexp, callName, Array.prototype.slice.call(arguments, 1));
},
callWhenNotCommitting(f) {
callWhenNotCommitting: (f) => {
pad.collabClient.callWhenNotCommitting(f);
},
getCollabRevisionNumber() {
return pad.collabClient.getCurrentRevisionNumber();
},
isFullyConnected() {
return padconnectionstatus.isFullyConnected();
},
addHistoricalAuthors(data) {
getCollabRevisionNumber: () => pad.collabClient.getCurrentRevisionNumber(),
isFullyConnected: () => padconnectionstatus.isFullyConnected(),
addHistoricalAuthors: (data) => {
if (!pad.collabClient) {
window.setTimeout(() => {
pad.addHistoricalAuthors(data);
@ -704,11 +739,9 @@ var pad = {
},
};
function init() {
return pad.init();
}
const init = () => pad.init();
var settings = {
const settings = {
LineNumbersDisabled: false,
noColors: false,
useMonospaceFontGlobal: false,
@ -718,6 +751,7 @@ var settings = {
};
pad.settings = settings;
exports.baseURL = '';
exports.settings = settings;
exports.randomString = randomString;

View File

@ -1,4 +1,6 @@
exports.showCountDownTimerToReconnectOnModal = function ($modal, pad) {
'use strict';
exports.showCountDownTimerToReconnectOnModal = ($modal, pad) => {
if (clientVars.automaticReconnectionTimeout && $modal.is('.with_reconnect_timer')) {
createCountDownElementsIfNecessary($modal);
@ -13,7 +15,7 @@ exports.showCountDownTimerToReconnectOnModal = function ($modal, pad) {
}
};
var createCountDownElementsIfNecessary = function ($modal) {
const createCountDownElementsIfNecessary = ($modal) => {
const elementsDoNotExist = $modal.find('#cancelreconnect').length === 0;
if (elementsDoNotExist) {
const $defaultMessage = $modal.find('#defaulttext');
@ -45,12 +47,13 @@ var createCountDownElementsIfNecessary = function ($modal) {
}
};
var localize = function ($element) {
const localize = ($element) => {
html10n.translateElement(html10n.translations, $element.get(0));
};
var createTimerForModal = function ($modal, pad) {
const timeUntilReconnection = clientVars.automaticReconnectionTimeout * reconnectionTries.nextTry();
const createTimerForModal = ($modal, pad) => {
const timeUntilReconnection =
clientVars.automaticReconnectionTimeout * reconnectionTries.nextTry();
const timer = new CountDownTimer(timeUntilReconnection);
timer.onTick((minutes, seconds) => {
@ -68,23 +71,23 @@ var createTimerForModal = function ($modal, pad) {
return timer;
};
var disableAutomaticReconnection = function ($modal) {
const disableAutomaticReconnection = ($modal) => {
toggleAutomaticReconnectionOption($modal, true);
};
var enableAutomaticReconnection = function ($modal) {
const enableAutomaticReconnection = ($modal) => {
toggleAutomaticReconnectionOption($modal, false);
};
var toggleAutomaticReconnectionOption = function ($modal, disableAutomaticReconnect) {
const toggleAutomaticReconnectionOption = ($modal, disableAutomaticReconnect) => {
$modal.find('#cancelreconnect, .reconnecttimer').toggleClass('hidden', disableAutomaticReconnect);
$modal.find('#defaulttext').toggleClass('hidden', !disableAutomaticReconnect);
};
var waitUntilClientCanConnectToServerAndThen = function (callback, pad) {
const waitUntilClientCanConnectToServerAndThen = (callback, pad) => {
whenConnectionIsRestablishedWithServer(callback, pad);
pad.socket.connect();
};
var whenConnectionIsRestablishedWithServer = function (callback, pad) {
const whenConnectionIsRestablishedWithServer = (callback, pad) => {
// only add listener for the first try, don't need to add another listener
// on every unsuccessful try
if (reconnectionTries.counter === 1) {
@ -92,11 +95,11 @@ var whenConnectionIsRestablishedWithServer = function (callback, pad) {
}
};
var forceReconnection = function ($modal) {
const forceReconnection = ($modal) => {
$modal.find('#forcereconnect').click();
};
var updateCountDownTimerMessage = function ($modal, minutes, seconds) {
const updateCountDownTimerMessage = ($modal, minutes, seconds) => {
minutes = minutes < 10 ? `0${minutes}` : minutes;
seconds = seconds < 10 ? `0${seconds}` : seconds;
@ -105,7 +108,7 @@ var updateCountDownTimerMessage = function ($modal, minutes, seconds) {
// store number of tries to reconnect to server, in order to increase time to wait
// until next try
var reconnectionTries = {
const reconnectionTries = {
counter: 0,
nextTry() {
@ -119,8 +122,9 @@ var reconnectionTries = {
// Timer based on http://stackoverflow.com/a/20618517.
// duration: how many **seconds** until the timer ends
// granularity (optional): how many **milliseconds** between each 'tick' of timer. Default: 1000ms (1s)
var CountDownTimer = function (duration, granularity) {
// granularity (optional): how many **milliseconds**
// between each 'tick' of timer. Default: 1000ms (1s)
const CountDownTimer = function (duration, granularity) {
this.duration = duration;
this.granularity = granularity || 1000;
this.running = false;
@ -137,8 +141,7 @@ CountDownTimer.prototype.start = function () {
const start = Date.now();
const that = this;
let diff;
(function timer() {
const timer = () => {
diff = that.duration - Math.floor((Date.now() - start) / 1000);
if (diff > 0) {
@ -149,7 +152,8 @@ CountDownTimer.prototype.start = function () {
that.tick(0);
that.expire();
}
}());
};
timer();
};
CountDownTimer.prototype.tick = function (diff) {
@ -184,9 +188,7 @@ CountDownTimer.prototype.cancel = function () {
return this;
};
CountDownTimer.parse = function (seconds) {
return {
minutes: (seconds / 60) | 0,
seconds: (seconds % 60) | 0,
};
};
CountDownTimer.parse = (seconds) => ({
minutes: (seconds / 60) | 0,
seconds: (seconds % 60) | 0,
});

View File

@ -1,3 +1,5 @@
'use strict';
/**
* 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.
@ -22,25 +24,25 @@
const padmodals = require('./pad_modals').padmodals;
const padconnectionstatus = (function () {
const padconnectionstatus = (() => {
let status = {
what: 'connecting',
};
const self = {
init() {
init: () => {
$('button#forcereconnect').click(() => {
window.location.reload();
});
},
connected() {
connected: () => {
status = {
what: 'connected',
};
padmodals.showModal('connected');
padmodals.hideOverlay();
},
reconnecting() {
reconnecting: () => {
status = {
what: 'reconnecting',
};
@ -48,8 +50,8 @@ const padconnectionstatus = (function () {
padmodals.showModal('reconnecting');
padmodals.showOverlay();
},
disconnected(msg) {
if (status.what == 'disconnected') return;
disconnected: (msg) => {
if (status.what === 'disconnected') return;
status = {
what: 'disconnected',
@ -81,14 +83,10 @@ const padconnectionstatus = (function () {
padmodals.showModal(k);
padmodals.showOverlay();
},
isFullyConnected() {
return status.what == 'connected';
},
getStatus() {
return status;
},
isFullyConnected: () => status.what === 'connected',
getStatus: () => status,
};
return self;
}());
})();
exports.padconnectionstatus = padconnectionstatus;

View File

@ -1,3 +1,5 @@
'use strict';
/**
* Copyright 2009 Google Inc.
*

View File

@ -1,4 +1,5 @@
'use strict';
/**
* 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.

View File

@ -1,3 +1,5 @@
'use strict';
/**
* 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.
@ -24,26 +26,26 @@ const Cookies = require('./pad_utils').Cookies;
const padcookie = require('./pad_cookie').padcookie;
const padutils = require('./pad_utils').padutils;
const padeditor = (function () {
const padeditor = (() => {
let Ace2Editor = undefined;
let pad = undefined;
let settings = undefined;
var self = {
const self = {
ace: null,
// this is accessed directly from other files
viewZoom: 100,
init(readyFunc, initialViewOptions, _pad) {
init: (readyFunc, initialViewOptions, _pad) => {
Ace2Editor = require('./ace').Ace2Editor;
pad = _pad;
settings = pad.settings;
function aceReady() {
const aceReady = () => {
$('#editorloadingbox').hide();
if (readyFunc) {
readyFunc();
}
}
};
self.ace = new Ace2Editor();
self.ace.init('editorcontainer', '', aceReady);
@ -57,7 +59,7 @@ const padeditor = (function () {
// view bar
$('#viewbarcontents').show();
},
initViewOptions() {
initViewOptions: () => {
// Line numbers
padutils.bindCheckboxChange($('#options-linenoscheck'), () => {
pad.changeViewOption('showLineNumbers', padutils.getCheckbox($('#options-linenoscheck')));
@ -74,8 +76,8 @@ const padeditor = (function () {
pad.changeViewOption('rtlIsTrue', padutils.getCheckbox($('#options-rtlcheck')));
});
html10n.bind('localized', () => {
pad.changeViewOption('rtlIsTrue', ('rtl' == html10n.getDirection()));
padutils.setCheckbox($('#options-rtlcheck'), ('rtl' == html10n.getDirection()));
pad.changeViewOption('rtlIsTrue', ('rtl' === html10n.getDirection()));
padutils.setCheckbox($('#options-rtlcheck'), ('rtl' === html10n.getDirection()));
});
// font family change
@ -87,9 +89,10 @@ const padeditor = (function () {
html10n.bind('localized', () => {
$('#languagemenu').val(html10n.getLanguage());
// translate the value of 'unnamed' and 'Enter your name' textboxes in the userlist
// this does not interfere with html10n's normal value-setting because html10n just ingores <input>s
// also, a value which has been set by the user will be not overwritten since a user-edited <input>
// does *not* have the editempty-class
// this does not interfere with html10n's normal value-setting because
// html10n just ingores <input>s
// also, a value which has been set by the user will be not overwritten
// since a user-edited <input> does *not* have the editempty-class
$('input[data-l10n-id]').each((key, input) => {
input = $(input);
if (input.hasClass('editempty')) {
@ -106,17 +109,17 @@ const padeditor = (function () {
}
});
},
setViewOptions(newOptions) {
function getOption(key, defaultValue) {
setViewOptions: (newOptions) => {
const getOption = (key, defaultValue) => {
const value = String(newOptions[key]);
if (value == 'true') return true;
if (value == 'false') return false;
if (value === 'true') return true;
if (value === 'false') return false;
return defaultValue;
}
};
let v;
v = getOption('rtlIsTrue', ('rtl' == html10n.getDirection()));
v = getOption('rtlIsTrue', ('rtl' === html10n.getDirection()));
self.ace.setProperty('rtlIsTrue', v);
padutils.setCheckbox($('#options-rtlcheck'), v);
@ -137,29 +140,29 @@ const padeditor = (function () {
self.ace.setProperty('textface', newOptions.padFontFamily || '');
},
dispose() {
dispose: () => {
if (self.ace) {
self.ace.destroy();
self.ace = null;
}
},
enable() {
enable: () => {
if (self.ace) {
self.ace.setEditable(true);
}
},
disable() {
disable: () => {
if (self.ace) {
self.ace.setProperty('grayedOut', true);
self.ace.setEditable(false);
}
},
restoreRevisionText(dataFromServer) {
restoreRevisionText: (dataFromServer) => {
pad.addHistoricalAuthors(dataFromServer.historicalAuthorData);
self.ace.importAText(dataFromServer.atext, dataFromServer.apool, true);
},
};
return self;
}());
})();
exports.padeditor = padeditor;

View File

@ -1,3 +1,5 @@
'use strict';
/**
* 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.
@ -20,24 +22,27 @@
* limitations under the License.
*/
const padimpexp = (function () {
const padimpexp = (() => {
// /// import
let currentImportTimer = null;
function addImportFrames() {
const addImportFrames = () => {
$('#import .importframe').remove();
const iframe = $('<iframe style="display: none;" name="importiframe" class="importframe"></iframe>');
const iframe = $('<iframe>')
.css('display', 'none')
.attr('name', 'importiframe')
.addClass('importframe');
$('#import').append(iframe);
}
};
function fileInputUpdated() {
const fileInputUpdated = () => {
$('#importsubmitinput').addClass('throbbold');
$('#importformfilediv').addClass('importformenabled');
$('#importsubmitinput').removeAttr('disabled');
$('#importmessagefail').fadeOut('fast');
}
};
function fileInputSubmit() {
const fileInputSubmit = () => {
$('#importmessagefail').fadeOut('fast');
const ret = window.confirm(html10n.get('pad.impexp.confirmimport'));
if (ret) {
@ -64,13 +69,13 @@ const padimpexp = (function () {
$('#importstatusball').show();
}
return ret;
}
};
function importFailed(msg) {
const importFailed = (msg) => {
importErrorMessage(msg);
}
};
function importDone() {
const importDone = () => {
$('#importsubmitinput').removeAttr('disabled').val(html10n.get('pad.impexp.importbutton'));
window.setTimeout(() => {
$('#importfileinput').removeAttr('disabled');
@ -78,16 +83,16 @@ const padimpexp = (function () {
$('#importstatusball').hide();
importClearTimeout();
addImportFrames();
}
};
function importClearTimeout() {
const importClearTimeout = () => {
if (currentImportTimer) {
window.clearTimeout(currentImportTimer);
currentImportTimer = null;
}
}
};
function importErrorMessage(status) {
const importErrorMessage = (status) => {
let msg = '';
if (status === 'convertFailed') {
@ -102,9 +107,11 @@ const padimpexp = (function () {
msg = html10n.get('pad.impexp.permission');
}
function showError(fade) {
$('#importmessagefail').html(`<strong style="color: red">${html10n.get('pad.impexp.importfailed')}:</strong> ${msg || html10n.get('pad.impexp.copypaste', '')}`)[(fade ? 'fadeIn' : 'show')]();
}
const showError = (fade) => {
$('#importmessagefail').html(
`<strong style="color: red">${html10n.get('pad.impexp.importfailed')}:</strong> ` +
`${msg || html10n.get('pad.impexp.copypaste', '')}`)[(fade ? 'fadeIn' : 'show')]();
};
if ($('#importexport .importmessage').is(':visible')) {
$('#importmessagesuccess').fadeOut('fast');
@ -114,7 +121,7 @@ const padimpexp = (function () {
} else {
showError();
}
}
};
// /// export
@ -134,16 +141,17 @@ const padimpexp = (function () {
}
// ///
var pad = undefined;
let pad = undefined;
const self = {
init(_pad) {
init: (_pad) => {
pad = _pad;
// get /p/padname
// if /p/ isn't available due to a rewrite we use the clientVars padId
const pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname) || clientVars.padId;
const padRootPath = /.*\/p\/[^/]+/.exec(document.location.pathname) || clientVars.padId;
// get http://example.com/p/padname without Params
const pad_root_url = `${document.location.protocol}//${document.location.host}${document.location.pathname}`;
const dl = document.location;
const padRootUrl = `${dl.protocol}//${dl.host}${dl.pathname}`;
// i10l buttom import
$('#importsubmitinput').val(html10n.get('pad.impexp.importbutton'));
@ -152,32 +160,32 @@ const padimpexp = (function () {
});
// build the export links
$('#exporthtmla').attr('href', `${pad_root_path}/export/html`);
$('#exportetherpada').attr('href', `${pad_root_path}/export/etherpad`);
$('#exportplaina').attr('href', `${pad_root_path}/export/txt`);
$('#exporthtmla').attr('href', `${padRootPath}/export/html`);
$('#exportetherpada').attr('href', `${padRootPath}/export/etherpad`);
$('#exportplaina').attr('href', `${padRootPath}/export/txt`);
// activate action to import in the form
$('#importform').attr('action', `${pad_root_url}/import`);
$('#importform').attr('action', `${padRootUrl}/import`);
// hide stuff thats not avaible if abiword/soffice is disabled
if (clientVars.exportAvailable == 'no') {
if (clientVars.exportAvailable === 'no') {
$('#exportworda').remove();
$('#exportpdfa').remove();
$('#exportopena').remove();
$('#importmessageabiword').show();
} else if (clientVars.exportAvailable == 'withoutPDF') {
} else if (clientVars.exportAvailable === 'withoutPDF') {
$('#exportpdfa').remove();
$('#exportworda').attr('href', `${pad_root_path}/export/doc`);
$('#exportopena').attr('href', `${pad_root_path}/export/odt`);
$('#exportworda').attr('href', `${padRootPath}/export/doc`);
$('#exportopena').attr('href', `${padRootPath}/export/odt`);
$('#importexport').css({height: '142px'});
$('#importexportline').css({height: '142px'});
} else {
$('#exportworda').attr('href', `${pad_root_path}/export/doc`);
$('#exportpdfa').attr('href', `${pad_root_path}/export/pdf`);
$('#exportopena').attr('href', `${pad_root_path}/export/odt`);
$('#exportworda').attr('href', `${padRootPath}/export/doc`);
$('#exportpdfa').attr('href', `${padRootPath}/export/pdf`);
$('#exportopena').attr('href', `${padRootPath}/export/odt`);
}
addImportFrames();
@ -185,7 +193,7 @@ const padimpexp = (function () {
$('#importform').unbind('submit').submit(fileInputSubmit);
$('.disabledexport').click(cantExport);
},
handleFrameCall(directDatabaseAccess, status) {
handleFrameCall: (directDatabaseAccess, status) => {
if (directDatabaseAccess === 'undefined') directDatabaseAccess = false;
if (status !== 'ok') {
importFailed(status);
@ -201,18 +209,18 @@ const padimpexp = (function () {
importDone();
},
disable() {
disable: () => {
$('#impexp-disabled-clickcatcher').show();
$('#import').css('opacity', 0.5);
$('#impexp-export').css('opacity', 0.5);
},
enable() {
enable: () => {
$('#impexp-disabled-clickcatcher').hide();
$('#import').css('opacity', 1);
$('#impexp-export').css('opacity', 1);
},
};
return self;
}());
})();
exports.padimpexp = padimpexp;

View File

@ -1,3 +1,5 @@
'use strict';
/**
* 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.
@ -23,13 +25,13 @@
const padeditbar = require('./pad_editbar').padeditbar;
const automaticReconnect = require('./pad_automatic_reconnect');
const padmodals = (function () {
const padmodals = (() => {
let pad = undefined;
const self = {
init(_pad) {
init: (_pad) => {
pad = _pad;
},
showModal(messageId) {
showModal: (messageId) => {
padeditbar.toggleDropDown('none', () => {
$('#connectivity .visible').removeClass('visible');
$(`#connectivity .${messageId}`).addClass('visible');
@ -40,15 +42,15 @@ const padmodals = (function () {
padeditbar.toggleDropDown('connectivity');
});
},
showOverlay() {
showOverlay: () => {
// Prevent the user to interact with the toolbar. Useful when user is disconnected for example
$('#toolbar-overlay').show();
},
hideOverlay() {
hideOverlay: () => {
$('#toolbar-overlay').hide();
},
};
return self;
}());
})();
exports.padmodals = padmodals;

View File

@ -1,3 +1,5 @@
'use strict';
/**
* Copyright 2012 Peter 'Pita' Martischka
*
@ -16,13 +18,14 @@
let pad;
exports.saveNow = function () {
exports.saveNow = () => {
pad.collabClient.sendMessage({type: 'SAVE_REVISION'});
$.gritter.add({
// (string | mandatory) the heading of the notification
title: _('pad.savedrevs.marked'),
title: html10n.get('pad.savedrevs.marked'),
// (string | mandatory) the text inside the notification
text: _('pad.savedrevs.timeslider') || 'You can view saved revisions in the timeslider',
text: html10n.get('pad.savedrevs.timeslider') ||
'You can view saved revisions in the timeslider',
// (bool | optional) if you want it to fade out on its own or just sit there
sticky: false,
time: 3000,
@ -30,6 +33,6 @@ exports.saveNow = function () {
});
};
exports.init = function (_pad) {
exports.init = (_pad) => {
pad = _pad;
};

View File

@ -1,3 +1,5 @@
'use strict';
/**
* 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.
@ -22,13 +24,12 @@
const padutils = require('./pad_utils').padutils;
const hooks = require('./pluginfw/hooks');
const browser = require('./browser');
let myUserInfo = {};
let colorPickerOpen = false;
let colorPickerSetup = false;
let previousColorId = 0;
const paduserlist = (function () {
const rowManager = (function () {
@ -36,9 +37,7 @@ const paduserlist = (function () {
// their insertion, removal, and reordering. It manipulates TD height
// and TD opacity.
function nextRowId() {
return `usertr${nextRowId.counter++}`;
}
const nextRowId = () => `usertr${nextRowId.counter++}`;
nextRowId.counter = 1;
// objects are shared; fields are "domId","data","animationStep"
const rowsFadingOut = []; // unordered set
@ -47,20 +46,67 @@ const paduserlist = (function () {
const ANIMATION_START = -12; // just starting to fade in
const ANIMATION_END = 12; // just finishing fading out
const animateStep = () => {
// animation must be symmetrical
for (let i = rowsFadingIn.length - 1; i >= 0; i--) { // backwards to allow removal
const row = rowsFadingIn[i];
const step = ++row.animationStep;
const animHeight = getAnimationHeight(step, row.animationPower);
const node = rowNode(row);
const baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
if (step <= -OPACITY_STEPS) {
node.find('td').height(animHeight);
} else if (step === -OPACITY_STEPS + 1) {
node.empty().append(createUserRowTds(animHeight, row.data))
.find('td').css('opacity', baseOpacity * 1 / OPACITY_STEPS);
} else if (step < 0) {
node.find('td').css('opacity', baseOpacity * (OPACITY_STEPS - (-step)) / OPACITY_STEPS)
.height(animHeight);
} else if (step === 0) {
// set HTML in case modified during animation
node.empty().append(createUserRowTds(animHeight, row.data))
.find('td').css('opacity', baseOpacity * 1).height(animHeight);
rowsFadingIn.splice(i, 1); // remove from set
}
}
for (let i = rowsFadingOut.length - 1; i >= 0; i--) { // backwards to allow removal
const row = rowsFadingOut[i];
const step = ++row.animationStep;
const node = rowNode(row);
const animHeight = getAnimationHeight(step, row.animationPower);
const baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
if (step < OPACITY_STEPS) {
node.find('td').css('opacity', baseOpacity * (OPACITY_STEPS - step) / OPACITY_STEPS)
.height(animHeight);
} else if (step === OPACITY_STEPS) {
node.empty().append(createEmptyRowTds(animHeight));
} else if (step <= ANIMATION_END) {
node.find('td').height(animHeight);
} else {
rowsFadingOut.splice(i, 1); // remove from set
node.remove();
}
}
function getAnimationHeight(step, power) {
handleOtherUserInputs();
return (rowsFadingIn.length > 0) || (rowsFadingOut.length > 0); // is more to do
};
const getAnimationHeight = (step, power) => {
let a = Math.abs(step / 12);
if (power == 2) a *= a;
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;
if (power === 2) a **= 2;
else if (power === 3) a **= 3;
else if (power === 4) a **= 4;
else if (power >= 5) a **= 5;
return Math.round(26 * (1 - a));
}
};
const OPACITY_STEPS = 6;
const ANIMATION_STEP_TIME = 20;
const LOWER_FRAMERATE_FACTOR = 2;
const scheduleAnimation = padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME, LOWER_FRAMERATE_FACTOR).scheduleAnimation;
const {scheduleAnimation} =
padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME, LOWER_FRAMERATE_FACTOR);
const NUMCOLS = 4;
@ -72,11 +118,9 @@ const paduserlist = (function () {
.css('border', 0)
.css('height', `${height}px`);
function isNameEditable(data) {
return (!data.name) && (data.status != 'Disconnected');
}
const isNameEditable = (data) => (!data.name) && (data.status !== 'Disconnected');
function replaceUserRowContents(tr, height, data) {
const replaceUserRowContents = (tr, height, data) => {
const tds = createUserRowTds(height, data);
if (isNameEditable(data) && tr.find('td.usertdname input:enabled').length > 0) {
// preserve input field node
@ -93,7 +137,7 @@ const paduserlist = (function () {
tr.empty().append(tds);
}
return tr;
}
};
const createUserRowTds = (height, data) => {
let name;
@ -105,7 +149,7 @@ const paduserlist = (function () {
.attr('type', 'text')
.addClass('editempty')
.addClass('newinput')
.attr('value', _('pad.userlist.unnamed'));
.attr('value', html10n.get('pad.userlist.unnamed'));
if (isNameEditable(data)) name.attr('disabled', 'disabled');
}
return $()
@ -131,19 +175,17 @@ const paduserlist = (function () {
.attr('id', id)
.append(contents);
function rowNode(row) {
return $(`#${row.domId}`);
}
const rowNode = (row) => $(`#${row.domId}`);
function handleRowData(row) {
if (row.data && row.data.status == 'Disconnected') {
const handleRowData = (row) => {
if (row.data && row.data.status === 'Disconnected') {
row.opacity = 0.5;
} else {
delete row.opacity;
}
}
};
function handleOtherUserInputs() {
const handleOtherUserInputs = () => {
// handle 'INPUT' elements for naming other unnamed users
$('#otheruserstable input.newinput').each(function () {
const input = $(this);
@ -156,12 +198,12 @@ const paduserlist = (function () {
}
}
}).removeClass('newinput');
}
};
// animationPower is 0 to skip animation, 1 for linear, 2 for quadratic, etc.
function insertRow(position, data, animationPower) {
const insertRow = (position, data, animationPower) => {
position = Math.max(0, Math.min(rowsPresent.length, position));
animationPower = (animationPower === undefined ? 4 : animationPower);
@ -177,7 +219,7 @@ const paduserlist = (function () {
handleRowData(row);
rowsPresent.splice(position, 0, row);
let tr;
if (animationPower == 0) {
if (animationPower === 0) {
tr = createRow(domId, createUserRowTds(getAnimationHeight(0), data), authorId);
row.animationStep = 0;
} else {
@ -185,41 +227,43 @@ const paduserlist = (function () {
tr = createRow(domId, createEmptyRowTds(getAnimationHeight(ANIMATION_START)), authorId);
}
$('table#otheruserstable').show();
if (position == 0) {
if (position === 0) {
$('table#otheruserstable').prepend(tr);
} else {
rowNode(rowsPresent[position - 1]).after(tr);
}
if (animationPower != 0) {
if (animationPower !== 0) {
scheduleAnimation();
}
handleOtherUserInputs();
return row;
}
};
function updateRow(position, data) {
const updateRow = (position, data) => {
const row = rowsPresent[position];
if (row) {
row.data = data;
handleRowData(row);
if (row.animationStep == 0) {
if (row.animationStep === 0) {
// not currently animating
const tr = rowNode(row);
replaceUserRowContents(tr, getAnimationHeight(0), row.data).find('td').css('opacity', (row.opacity === undefined ? 1 : row.opacity));
replaceUserRowContents(tr, getAnimationHeight(0), row.data)
.find('td')
.css('opacity', (row.opacity === undefined ? 1 : row.opacity));
handleOtherUserInputs();
}
}
}
};
function removeRow(position, animationPower) {
const removeRow = (position, animationPower) => {
animationPower = (animationPower === undefined ? 4 : animationPower);
const row = rowsPresent[position];
if (row) {
rowsPresent.splice(position, 1); // remove
if (animationPower == 0) {
if (animationPower === 0) {
rowNode(row).remove();
} else {
row.animationStep = -row.animationStep; // use symmetry
@ -231,65 +275,20 @@ const paduserlist = (function () {
if (rowsPresent.length === 0) {
$('table#otheruserstable').hide();
}
}
};
// newPosition is position after the row has been removed
function moveRow(oldPosition, newPosition, animationPower) {
const moveRow = (oldPosition, newPosition, animationPower) => {
animationPower = (animationPower === undefined ? 1 : animationPower); // linear is best
const row = rowsPresent[oldPosition];
if (row && oldPosition != newPosition) {
if (row && oldPosition !== newPosition) {
const rowData = row.data;
removeRow(oldPosition, animationPower);
insertRow(newPosition, rowData, animationPower);
}
}
function animateStep() {
// animation must be symmetrical
for (var i = rowsFadingIn.length - 1; i >= 0; i--) { // backwards to allow removal
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);
if (step <= -OPACITY_STEPS) {
node.find('td').height(animHeight);
} else if (step == -OPACITY_STEPS + 1) {
node.empty().append(createUserRowTds(animHeight, row.data))
.find('td').css('opacity', baseOpacity * 1 / OPACITY_STEPS);
} else if (step < 0) {
node.find('td').css('opacity', baseOpacity * (OPACITY_STEPS - (-step)) / OPACITY_STEPS).height(animHeight);
} else if (step == 0) {
// set HTML in case modified during animation
node.empty().append(createUserRowTds(animHeight, row.data))
.find('td').css('opacity', baseOpacity * 1).height(animHeight);
rowsFadingIn.splice(i, 1); // remove from set
}
}
for (var i = rowsFadingOut.length - 1; i >= 0; i--) { // backwards to allow removal
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);
if (step < OPACITY_STEPS) {
node.find('td').css('opacity', baseOpacity * (OPACITY_STEPS - step) / OPACITY_STEPS).height(animHeight);
} else if (step == OPACITY_STEPS) {
node.empty().append(createEmptyRowTds(animHeight));
} else if (step <= ANIMATION_END) {
node.find('td').height(animHeight);
} else {
rowsFadingOut.splice(i, 1); // remove from set
node.remove();
}
}
handleOtherUserInputs();
return (rowsFadingIn.length > 0) || (rowsFadingOut.length > 0); // is more to do
}
};
const self = {
insertRow,
@ -302,7 +301,7 @@ const paduserlist = (function () {
const otherUsersInfo = [];
const otherUsersData = [];
function rowManagerMakeNameEditor(jnode, userId) {
const rowManagerMakeNameEditor = (jnode, userId) => {
setUpEditable(jnode, () => {
const existingIndex = findExistingIndex(userId);
if (existingIndex >= 0) {
@ -313,26 +312,26 @@ const paduserlist = (function () {
}, (newName) => {
if (!newName) {
jnode.addClass('editempty');
jnode.val(_('pad.userlist.unnamed'));
jnode.val(html10n.get('pad.userlist.unnamed'));
} else {
jnode.attr('disabled', 'disabled');
pad.suggestUserName(userId, newName);
}
});
}
};
function findExistingIndex(userId) {
const findExistingIndex = (userId) => {
let existingIndex = -1;
for (let i = 0; i < otherUsersInfo.length; i++) {
if (otherUsersInfo[i].userId == userId) {
if (otherUsersInfo[i].userId === userId) {
existingIndex = i;
break;
}
}
return existingIndex;
}
};
function setUpEditable(jqueryNode, valueGetter, valueSetter) {
const setUpEditable = (jqueryNode, valueGetter, valueSetter) => {
jqueryNode.bind('focus', (evt) => {
const oldValue = valueGetter();
if (jqueryNode.val() !== oldValue) {
@ -350,16 +349,18 @@ const paduserlist = (function () {
jqueryNode.val(valueGetter()).blur();
});
jqueryNode.removeAttr('disabled').addClass('editable');
}
};
var pad = undefined;
var self = {
let pad = undefined;
const self = {
init(myInitialUserInfo, _pad) {
pad = _pad;
self.setMyUserInfo(myInitialUserInfo);
if ($('#online_count').length === 0) $('#editbar [data-key=showusers] > a').append('<span id="online_count">1</span>');
if ($('#online_count').length === 0) {
$('#editbar [data-key=showusers] > a').append('<span id="online_count">1</span>');
}
$('#otheruserstable tr').remove();
@ -388,23 +389,24 @@ const paduserlist = (function () {
});
//
},
usersOnline() {
usersOnline: () => {
// Returns an object of users who are currently online on this pad
const userList = [].concat(otherUsersInfo); // Make a copy of the otherUsersInfo, otherwise every call to users modifies the referenced array
// Make a copy of the otherUsersInfo, otherwise every call to users
// modifies the referenced array
const userList = [].concat(otherUsersInfo);
// Now we need to add ourselves..
userList.push(myUserInfo);
return userList;
},
users() {
users: () => {
// Returns an object of users who have been on this pad
const userList = self.usersOnline();
// Now we add historical authors
const historical = clientVars.collab_client_vars.historicalAuthorData;
for (const key in historical) {
var userId = historical[key].userId;
for (const [key, {userId}] of Object.entries(historical)) {
// Check we don't already have this author in our array
var exists = false;
let exists = false;
userList.forEach((user) => {
if (user.userId === userId) exists = true;
@ -416,7 +418,7 @@ const paduserlist = (function () {
}
return userList;
},
setMyUserInfo(info) {
setMyUserInfo: (info) => {
// translate the colorId
if (typeof info.colorId === 'number') {
info.colorId = clientVars.colorPalette[info.colorId];
@ -427,8 +429,8 @@ const paduserlist = (function () {
self.renderMyUserInfo();
},
userJoinOrUpdate(info) {
if ((!info.userId) || (info.userId == myUserInfo.userId)) {
userJoinOrUpdate: (info) => {
if ((!info.userId) || (info.userId === myUserInfo.userId)) {
// not sure how this would happen
return;
}
@ -438,7 +440,8 @@ const paduserlist = (function () {
});
const userData = {};
userData.color = typeof info.colorId === 'number' ? clientVars.colorPalette[info.colorId] : info.colorId;
userData.color = typeof info.colorId === 'number'
? clientVars.colorPalette[info.colorId] : info.colorId;
userData.name = info.name;
userData.status = '';
userData.activity = '';
@ -460,12 +463,12 @@ const paduserlist = (function () {
const nameThis = (info.name || '').toLowerCase();
const idN = infoN.userId;
const idThis = info.userId;
return (nameN > nameThis) || (nameN == nameThis && idN > idThis);
return (nameN > nameThis) || (nameN === nameThis && idN > idThis);
});
if (existingIndex >= 0) {
// update
if (existingIndex == newIndex) {
if (existingIndex === newIndex) {
otherUsersInfo[existingIndex] = info;
otherUsersData[existingIndex] = userData;
rowManager.updateRow(existingIndex, userData);
@ -485,10 +488,10 @@ const paduserlist = (function () {
self.updateNumberOfOnlineUsers();
},
updateNumberOfOnlineUsers() {
updateNumberOfOnlineUsers: () => {
let online = 1; // you are always online!
for (let i = 0; i < otherUsersData.length; i++) {
if (otherUsersData[i].status == '') {
if (otherUsersData[i].status === '') {
online++;
}
}
@ -497,7 +500,7 @@ const paduserlist = (function () {
return online;
},
userLeave(info) {
userLeave: (info) => {
const existingIndex = findExistingIndex(info.userId);
if (existingIndex >= 0) {
const userData = otherUsersData[existingIndex];
@ -510,11 +513,12 @@ const paduserlist = (function () {
// joins, or updates happen for this user in the
// next N seconds, to remove the user from the list.
const thisUserId = info.userId;
var thisLeaveTimer = window.setTimeout(() => {
const thisLeaveTimer = window.setTimeout(() => {
const newExistingIndex = findExistingIndex(thisUserId);
if (newExistingIndex >= 0) {
const newUserData = otherUsersData[newExistingIndex];
if (newUserData.status == 'Disconnected' && newUserData.leaveTimer == thisLeaveTimer) {
if (newUserData.status === 'Disconnected' &&
newUserData.leaveTimer === thisLeaveTimer) {
otherUsersInfo.splice(newExistingIndex, 1);
otherUsersData.splice(newExistingIndex, 1);
rowManager.removeRow(newExistingIndex);
@ -529,7 +533,7 @@ const paduserlist = (function () {
self.updateNumberOfOnlineUsers();
},
renderMyUserInfo() {
renderMyUserInfo: () => {
if (myUserInfo.name) {
$('#myusernameedit').removeClass('editempty').val(myUserInfo.name);
} else {
@ -549,23 +553,20 @@ const paduserlist = (function () {
return self;
}());
function getColorPickerSwatchIndex(jnode) {
// return Number(jnode.get(0).className.match(/\bn([0-9]+)\b/)[1])-1;
return $('#colorpickerswatches li').index(jnode);
}
const getColorPickerSwatchIndex = (jnode) => $('#colorpickerswatches li').index(jnode);
function closeColorPicker(accept) {
const closeColorPicker = (accept) => {
if (accept) {
var newColor = $('#mycolorpickerpreview').css('background-color');
let newColor = $('#mycolorpickerpreview').css('background-color');
const parts = newColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
// parts now should be ["rgb(0, 70, 255", "0", "70", "255"]
if (parts) {
delete (parts[0]);
for (let i = 1; i <= 3; ++i) {
parts[i] = parseInt(parts[i]).toString(16);
if (parts[i].length == 1) parts[i] = `0${parts[i]}`;
if (parts[i].length === 1) parts[i] = `0${parts[i]}`;
}
var newColor = `#${parts.join('')}`; // "0070ff"
newColor = `#${parts.join('')}`; // "0070ff"
}
myUserInfo.colorId = newColor;
pad.notifyChangeColor(newColor);
@ -577,10 +578,9 @@ function closeColorPicker(accept) {
colorPickerOpen = false;
$('#mycolorpicker').removeClass('popup-show');
}
};
function showColorPicker() {
previousColorId = myUserInfo.colorId;
const showColorPicker = () => {
$.farbtastic('#colorpicker').setColor(myUserInfo.colorId);
if (!colorPickerOpen) {
@ -613,6 +613,6 @@ function showColorPicker() {
$('#colorpickerswatches li').removeClass('picked');
$($('#colorpickerswatches li')[myUserInfo.colorId]).addClass('picked'); // seems weird
}
}
};
exports.paduserlist = paduserlist;

View File

@ -1,3 +1,5 @@
'use strict';
/**
* 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.
@ -20,8 +22,6 @@
* limitations under the License.
*/
'use strict';
const Security = require('./security');
/**