diff --git a/src/static/js/pad_utils.js b/src/static/js/pad_utils.js
index 4aaedaf0e..1fdf7cd6c 100644
--- a/src/static/js/pad_utils.js
+++ b/src/static/js/pad_utils.js
@@ -20,13 +20,15 @@
* limitations under the License.
*/
+'use strict';
+
const Security = require('./security');
/**
- * Generates a random String with the given length. Is needed to generate the Author, Group, readonly, session Ids
+ * Generates a random String with the given length. Is needed to generate the Author, Group,
+ * readonly, session Ids
*/
-
-function randomString(len) {
+const randomString = (len) => {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let randomstring = '';
len = len || 20;
@@ -35,41 +37,69 @@ function randomString(len) {
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
-}
+};
-var padutils = {
- escapeHtml(x) {
- return Security.escapeHTML(String(x));
- },
- uniqueId() {
+const padutils = {
+ escapeHtml: (x) => Security.escapeHTML(String(x)),
+ uniqueId: () => {
const pad = require('./pad').pad; // Sidestep circular dependency
- function encodeNum(n, width) {
- // returns string that is exactly 'width' chars, padding with zeros
- // and taking rightmost digits
- return (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
- }
- return [pad.getClientIp(), encodeNum(+new Date(), 7), encodeNum(Math.floor(Math.random() * 1e9), 4)].join('.');
+ // returns string that is exactly 'width' chars, padding with zeros and taking rightmost digits
+ const encodeNum =
+ (n, width) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
+ return [
+ pad.getClientIp(),
+ encodeNum(+new Date(), 7),
+ encodeNum(Math.floor(Math.random() * 1e9), 4),
+ ].join('.');
},
// e.g. "Thu Jun 18 2009 13:09"
- simpleDateTime(date) {
+ simpleDateTime: (date) => {
const d = new Date(+date); // accept either number or date
const dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()];
- const month = (['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])[d.getMonth()];
+ const month = ([
+ 'Jan',
+ 'Feb',
+ 'Mar',
+ 'Apr',
+ 'May',
+ 'Jun',
+ 'Jul',
+ 'Aug',
+ 'Sep',
+ 'Oct',
+ 'Nov',
+ 'Dec',
+ ])[d.getMonth()];
const dayOfMonth = d.getDate();
const year = d.getFullYear();
const hourmin = `${d.getHours()}:${(`0${d.getMinutes()}`).slice(-2)}`;
return `${dayOfWeek} ${month} ${dayOfMonth} ${year} ${hourmin}`;
},
- findURLs(text) {
+ findURLs: (text) => {
// copied from ACE
- const _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
- const _REGEX_URLCHAR = new RegExp(`(${/[-:@a-zA-Z0-9_.,~%+\/?=()$]/.source}|${_REGEX_WORDCHAR.source})`);
- const _REGEX_URL = new RegExp(`${/(?:(?:https?|s?ftp|ftps|file|nfs):\/\/|(about|geo|mailto|tel):)/.source + _REGEX_URLCHAR.source}*(?![:.,;])${_REGEX_URLCHAR.source}`, 'g');
+ const _REGEX_WORDCHAR = new RegExp(`[${[
+ '\u0030-\u0039',
+ '\u0041-\u005A',
+ '\u0061-\u007A',
+ '\u00C0-\u00D6',
+ '\u00D8-\u00F6',
+ '\u00F8-\u00FF',
+ '\u0100-\u1FFF',
+ '\u3040-\u9FFF',
+ '\uF900-\uFDFF',
+ '\uFE70-\uFEFE',
+ '\uFF10-\uFF19',
+ '\uFF21-\uFF3A',
+ '\uFF41-\uFF5A',
+ '\uFF66-\uFFDC',
+ ].join('')}]`);
+ const _REGEX_URLCHAR = new RegExp(`([-:@a-zA-Z0-9_.,~%+/?=()$]|${_REGEX_WORDCHAR.source})`);
+ const _REGEX_URL = new RegExp(
+ '(?:(?:https?|s?ftp|ftps|file|nfs)://|(about|geo|mailto|tel):)' +
+ `${_REGEX_URLCHAR.source}*(?![:.,;])${_REGEX_URLCHAR.source}`, 'g');
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
-
-
- function _findURLs(text) {
+ const _findURLs = (text) => {
_REGEX_URL.lastIndex = 0;
let urls = null;
let execResult;
@@ -81,34 +111,39 @@ var padutils = {
}
return urls;
- }
+ };
return _findURLs(text);
},
- escapeHtmlWithClickableLinks(text, target) {
+ escapeHtmlWithClickableLinks: (text, target) => {
let idx = 0;
const pieces = [];
const urls = padutils.findURLs(text);
- function advanceTo(i) {
+ const advanceTo = (i) => {
if (i > idx) {
pieces.push(Security.escapeHTML(text.substring(idx, i)));
idx = i;
}
- }
+ };
if (urls) {
for (let j = 0; j < urls.length; j++) {
const startIndex = urls[j][0];
const href = urls[j][1];
advanceTo(startIndex);
- // Using rel="noreferrer" stops leaking the URL/location of the pad when clicking links in the document.
- // Not all browsers understand this attribute, but it's part of the HTML5 standard.
- // https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer
+ // Using rel="noreferrer" stops leaking the URL/location of the pad when clicking links in
+ // the document. Not all browsers understand this attribute, but it's part of the HTML5
+ // standard. https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer
// Additionally, we do rel="noopener" to ensure a higher level of referrer security.
// https://html.spec.whatwg.org/multipage/links.html#link-type-noopener
// https://mathiasbynens.github.io/rel-noopener/
// https://github.com/ether/etherpad-lite/pull/3636
- pieces.push('');
+ pieces.push(
+ '');
advanceTo(startIndex + href.length);
pieces.push('');
}
@@ -116,14 +151,14 @@ var padutils = {
advanceTo(text.length);
return pieces.join('');
},
- bindEnterAndEscape(node, onEnter, onEscape) {
- // Use keypress instead of keyup in bindEnterAndEscape
- // Keyup event is fired on enter in IME (Input Method Editor), But
- // keypress is not. So, I changed to use keypress instead of keyup.
- // It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox 3.6.10, Chrome 6.0.472, Safari 5.0).
+ bindEnterAndEscape: (node, onEnter, onEscape) => {
+ // Use keypress instead of keyup in bindEnterAndEscape. Keyup event is fired on enter in IME
+ // (Input Method Editor), But keypress is not. So, I changed to use keypress instead of keyup.
+ // It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox
+ // 3.6.10, Chrome 6.0.472, Safari 5.0).
if (onEnter) {
node.keypress((evt) => {
- if (evt.which == 13) {
+ if (evt.which === 13) {
onEnter(evt);
}
});
@@ -131,18 +166,18 @@ var padutils = {
if (onEscape) {
node.keydown((evt) => {
- if (evt.which == 27) {
+ if (evt.which === 27) {
onEscape(evt);
}
});
}
},
- timediff(d) {
+ timediff: (d) => {
const pad = require('./pad').pad; // Sidestep circular dependency
- function format(n, word) {
+ const format = (n, word) => {
n = Math.round(n);
- return (`${n} ${word}${n != 1 ? 's' : ''} ago`);
- }
+ return (`${n} ${word}${n !== 1 ? 's' : ''} ago`);
+ };
d = Math.max(0, (+(new Date()) - (+d) - pad.clientTimeOffset) / 1000);
if (d < 60) {
return format(d, 'second');
@@ -158,14 +193,14 @@ var padutils = {
d /= 24;
return format(d, 'day');
},
- makeAnimationScheduler(funcToAnimateOneStep, stepTime, stepsAtOnce) {
+ makeAnimationScheduler: (funcToAnimateOneStep, stepTime, stepsAtOnce) => {
if (stepsAtOnce === undefined) {
stepsAtOnce = 1;
}
let animationTimer = null;
- function scheduleAnimation() {
+ const scheduleAnimation = () => {
if (!animationTimer) {
animationTimer = window.setTimeout(() => {
animationTimer = null;
@@ -181,43 +216,16 @@ var padutils = {
}
}, stepTime * stepsAtOnce);
}
- }
- return {
- scheduleAnimation,
};
+ return {scheduleAnimation};
},
- makeShowHideAnimator(funcToArriveAtState, initiallyShown, fps, totalMs) {
+ makeShowHideAnimator: (funcToArriveAtState, initiallyShown, fps, totalMs) => {
let animationState = (initiallyShown ? 0 : -2); // -2 hidden, -1 to 0 fade in, 0 to 1 fade out
const animationFrameDelay = 1000 / fps;
const animationStep = animationFrameDelay / totalMs;
- const scheduleAnimation = padutils.makeAnimationScheduler(animateOneStep, animationFrameDelay).scheduleAnimation;
-
- function doShow() {
- animationState = -1;
- funcToArriveAtState(animationState);
- scheduleAnimation();
- }
-
- function doQuickShow() { // start showing without losing any fade-in progress
- if (animationState < -1) {
- animationState = -1;
- } else if (animationState > 0) {
- animationState = Math.max(-1, Math.min(0, -animationState));
- }
- funcToArriveAtState(animationState);
- scheduleAnimation();
- }
-
- function doHide() {
- if (animationState >= -1 && animationState <= 0) {
- animationState = 1e-6;
- scheduleAnimation();
- }
- }
-
- function animateOneStep() {
- if (animationState < -1 || animationState == 0) {
+ const animateOneStep = () => {
+ if (animationState < -1 || animationState === 0) {
return false;
} else if (animationState < 0) {
// animate show
@@ -243,17 +251,37 @@ var padutils = {
return true;
}
}
- }
+ };
+
+ const scheduleAnimation =
+ padutils.makeAnimationScheduler(animateOneStep, animationFrameDelay).scheduleAnimation;
return {
- show: doShow,
- hide: doHide,
- quickShow: doQuickShow,
+ show: () => {
+ animationState = -1;
+ funcToArriveAtState(animationState);
+ scheduleAnimation();
+ },
+ quickShow: () => { // start showing without losing any fade-in progress
+ if (animationState < -1) {
+ animationState = -1;
+ } else if (animationState > 0) {
+ animationState = Math.max(-1, Math.min(0, -animationState));
+ }
+ funcToArriveAtState(animationState);
+ scheduleAnimation();
+ },
+ hide: () => {
+ if (animationState >= -1 && animationState <= 0) {
+ animationState = 1e-6;
+ scheduleAnimation();
+ }
+ },
};
},
_nextActionId: 1,
uncanceledActions: {},
- getCancellableAction(actionType, actionFunc) {
+ getCancellableAction: (actionType, actionFunc) => {
let o = padutils.uncanceledActions[actionType];
if (!o) {
o = {};
@@ -261,27 +289,27 @@ var padutils = {
}
const actionId = (padutils._nextActionId++);
o[actionId] = true;
- return function () {
+ return () => {
const p = padutils.uncanceledActions[actionType];
if (p && p[actionId]) {
actionFunc();
}
};
},
- cancelActions(actionType) {
+ cancelActions: (actionType) => {
const o = padutils.uncanceledActions[actionType];
if (o) {
// clear it
delete padutils.uncanceledActions[actionType];
}
},
- makeFieldLabeledWhenEmpty(field, labelText) {
+ makeFieldLabeledWhenEmpty: (field, labelText) => {
field = $(field);
- function clear() {
+ const clear = () => {
field.addClass('editempty');
field.val(labelText);
- }
+ };
field.focus(() => {
if (field.hasClass('editempty')) {
field.val('');
@@ -297,34 +325,28 @@ var padutils = {
clear,
};
},
- getCheckbox(node) {
- return $(node).is(':checked');
- },
- setCheckbox(node, value) {
+ getCheckbox: (node) => $(node).is(':checked'),
+ setCheckbox: (node, value) => {
if (value) {
$(node).attr('checked', 'checked');
} else {
$(node).removeAttr('checked');
}
},
- bindCheckboxChange(node, func) {
+ bindCheckboxChange: (node, func) => {
$(node).change(func);
},
- encodeUserId(userId) {
- return userId.replace(/[^a-y0-9]/g, (c) => {
- if (c == '.') return '-';
- return `z${c.charCodeAt(0)}z`;
- });
- },
- decodeUserId(encodedUserId) {
- return encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => {
- if (cc == '-') { return '.'; } else if (cc.charAt(0) == 'z') {
- return String.fromCharCode(Number(cc.slice(1, -1)));
- } else {
- return cc;
- }
- });
- },
+ encodeUserId: (userId) => userId.replace(/[^a-y0-9]/g, (c) => {
+ if (c === '.') return '-';
+ return `z${c.charCodeAt(0)}z`;
+ }),
+ decodeUserId: (encodedUserId) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => {
+ if (cc === '-') { return '.'; } else if (cc.charAt(0) === 'z') {
+ return String.fromCharCode(Number(cc.slice(1, -1)));
+ } else {
+ return cc;
+ }
+ }),
};
let globalExceptionHandler = null;
@@ -402,13 +424,13 @@ padutils.setupGlobalExceptionHandler = () => {
padutils.binarySearch = require('./ace2_common').binarySearch;
// https://stackoverflow.com/a/42660748
-function inThirdPartyIframe() {
+const inThirdPartyIframe = () => {
try {
return (!window.top.location.hostname);
} catch (e) {
return true;
}
-}
+};
// This file is included from Node so that it can reuse randomString, but Node doesn't have a global
// window object.
diff --git a/tests/frontend/helper/methods.js b/tests/frontend/helper/methods.js
index 191202c65..39245ea63 100644
--- a/tests/frontend/helper/methods.js
+++ b/tests/frontend/helper/methods.js
@@ -1,3 +1,5 @@
+'use strict';
+
/**
* Spys on socket.io messages and saves them into several arrays
* that are visible in tests
diff --git a/tests/frontend/specs/timeslider_follow.js b/tests/frontend/specs/timeslider_follow.js
index c570cbd07..be9bc8125 100644
--- a/tests/frontend/specs/timeslider_follow.js
+++ b/tests/frontend/specs/timeslider_follow.js
@@ -1,3 +1,5 @@
+'use strict';
+
describe('timeslider follow', function () {
// create a new pad before each test run
beforeEach(function (cb) {
@@ -23,7 +25,7 @@ describe('timeslider follow', function () {
helper.contentWindow().$('#playpause_button_icon').click();
let newTop;
- return helper.waitForPromise(() => {
+ await helper.waitForPromise(() => {
newTop = helper.contentWindow().$('#innerdocbody').offset();
return newTop.top < originalTop.top;
});
@@ -96,9 +98,9 @@ describe('timeslider follow', function () {
* @param {number} lineNum
* @returns {boolean} scrolled to the lineOffset?
*/
-function hasFollowedToLine(lineNum) {
+const hasFollowedToLine = (lineNum) => {
const scrollPosition = helper.contentWindow().$('#editorcontainerbox')[0].scrollTop;
- const lineOffset = helper.contentWindow().$('#innerdocbody').find(`div:nth-child(${lineNum})`)[0].offsetTop;
-
+ const lineOffset =
+ helper.contentWindow().$('#innerdocbody').find(`div:nth-child(${lineNum})`)[0].offsetTop;
return Math.abs(scrollPosition - lineOffset) < 1;
-}
+};