Merge branch 'develop' of github.com:ether/etherpad-lite into image-example

This commit is contained in:
John McLear 2021-01-14 13:07:20 +00:00
commit 5bedfa0ba1
6 changed files with 198 additions and 211 deletions

View file

@ -1,3 +1,4 @@
'use strict';
/** /**
* This code represents the Attribute Pool Object of the original Etherpad. * This code represents the Attribute Pool Object of the original Etherpad.
* 90% of the code is still like in the original Etherpad * 90% of the code is still like in the original Etherpad
@ -69,7 +70,7 @@ AttributePool.prototype.getAttribValue = function (num) {
}; };
AttributePool.prototype.eachAttrib = function (func) { AttributePool.prototype.eachAttrib = function (func) {
for (const n in this.numToAttrib) { for (const n of Object.keys(this.numToAttrib)) {
const pair = this.numToAttrib[n]; const pair = this.numToAttrib[n];
func(pair[0], pair[1]); func(pair[0], pair[1]);
} }
@ -86,7 +87,7 @@ AttributePool.prototype.fromJsonable = function (obj) {
this.numToAttrib = obj.numToAttrib; this.numToAttrib = obj.numToAttrib;
this.nextNum = obj.nextNum; this.nextNum = obj.nextNum;
this.attribToNum = {}; this.attribToNum = {};
for (const n in this.numToAttrib) { for (const n of Object.keys(this.numToAttrib)) {
this.attribToNum[String(this.numToAttrib[n])] = Number(n); this.attribToNum[String(this.numToAttrib[n])] = Number(n);
} }
return this; return this;

View file

@ -1,18 +1,20 @@
'use strict';
// One rep.line(div) can be broken in more than one line in the browser. // One rep.line(div) can be broken in more than one line in the browser.
// This function is useful to get the caret position of the line as // This function is useful to get the caret position of the line as
// is represented by the browser // is represented by the browser
exports.getPosition = function () { exports.getPosition = () => {
let rect, line; let rect, line;
const editor = $('#innerdocbody')[0];
const range = getSelectionRange(); const range = getSelectionRange();
const isSelectionInsideTheEditor = range && $(range.endContainer).closest('body')[0].id === 'innerdocbody'; const isSelectionInsideTheEditor = range &&
$(range.endContainer).closest('body')[0].id === 'innerdocbody';
if (isSelectionInsideTheEditor) { if (isSelectionInsideTheEditor) {
// when we have the caret in an empty line, e.g. a line with only a <br>, // when we have the caret in an empty line, e.g. a line with only a <br>,
// getBoundingClientRect() returns all dimensions value as 0 // getBoundingClientRect() returns all dimensions value as 0
const selectionIsInTheBeginningOfLine = range.endOffset > 0; const selectionIsInTheBeginningOfLine = range.endOffset > 0;
if (selectionIsInTheBeginningOfLine) { if (selectionIsInTheBeginningOfLine) {
var clonedRange = createSelectionRange(range); const clonedRange = createSelectionRange(range);
line = getPositionOfElementOrSelection(clonedRange); line = getPositionOfElementOrSelection(clonedRange);
clonedRange.detach(); clonedRange.detach();
} }
@ -20,7 +22,7 @@ exports.getPosition = function () {
// when there's a <br> or any element that has no height, we can't get // when there's a <br> or any element that has no height, we can't get
// the dimension of the element where the caret is // the dimension of the element where the caret is
if (!rect || rect.height === 0) { if (!rect || rect.height === 0) {
var clonedRange = createSelectionRange(range); const clonedRange = createSelectionRange(range);
// as we can't get the element height, we create a text node to get the dimensions // as we can't get the element height, we create a text node to get the dimensions
// on the position // on the position
@ -36,8 +38,8 @@ exports.getPosition = function () {
return line; return line;
}; };
var createSelectionRange = function (range) { const createSelectionRange = (range) => {
clonedRange = range.cloneRange(); const clonedRange = range.cloneRange();
// we set the selection start and end to avoid error when user selects a text bigger than // we set the selection start and end to avoid error when user selects a text bigger than
// the viewport height and uses the arrow keys to expand the selection. In this particular // the viewport height and uses the arrow keys to expand the selection. In this particular
@ -48,7 +50,7 @@ var createSelectionRange = function (range) {
return clonedRange; return clonedRange;
}; };
const getPositionOfRepLineAtOffset = function (node, offset) { const getPositionOfRepLineAtOffset = (node, offset) => {
// it is not a text node, so we cannot make a selection // it is not a text node, so we cannot make a selection
if (node.tagName === 'BR' || node.tagName === 'EMPTY') { if (node.tagName === 'BR' || node.tagName === 'EMPTY') {
return getPositionOfElementOrSelection(node); return getPositionOfElementOrSelection(node);
@ -66,7 +68,7 @@ const getPositionOfRepLineAtOffset = function (node, offset) {
return linePosition; return linePosition;
}; };
function getPositionOfElementOrSelection(element) { const getPositionOfElementOrSelection = (element) => {
const rect = element.getBoundingClientRect(); const rect = element.getBoundingClientRect();
const linePosition = { const linePosition = {
bottom: rect.bottom, bottom: rect.bottom,
@ -74,15 +76,15 @@ function getPositionOfElementOrSelection(element) {
top: rect.top, top: rect.top,
}; };
return linePosition; return linePosition;
} };
// here we have two possibilities: // here we have two possibilities:
// [1] the line before the caret line has the same type, so both of them has the same margin, padding // [1] the line before the caret line has the same type, so both of them has the same margin,
// height, etc. So, we can use the caret line to make calculation necessary to know where is the top // padding height, etc. So, we can use the caret line to make calculation necessary to know
// of the previous line // where is the top of the previous line
// [2] the line before is part of another rep line. It's possible this line has different margins // [2] the line before is part of another rep line. It's possible this line has different margins
// height. So we have to get the exactly position of the line // height. So we have to get the exactly position of the line
exports.getPositionTopOfPreviousBrowserLine = function (caretLinePosition, rep) { exports.getPositionTopOfPreviousBrowserLine = (caretLinePosition, rep) => {
let previousLineTop = caretLinePosition.top - caretLinePosition.height; // [1] let previousLineTop = caretLinePosition.top - caretLinePosition.height; // [1]
const isCaretLineFirstBrowserLine = caretLineIsFirstBrowserLine(caretLinePosition.top, rep); const isCaretLineFirstBrowserLine = caretLineIsFirstBrowserLine(caretLinePosition.top, rep);
@ -91,13 +93,14 @@ exports.getPositionTopOfPreviousBrowserLine = function (caretLinePosition, rep)
if (isCaretLineFirstBrowserLine) { // [2] if (isCaretLineFirstBrowserLine) { // [2]
const lineBeforeCaretLine = rep.selStart[0] - 1; const lineBeforeCaretLine = rep.selStart[0] - 1;
const firstLineVisibleBeforeCaretLine = getPreviousVisibleLine(lineBeforeCaretLine, rep); const firstLineVisibleBeforeCaretLine = getPreviousVisibleLine(lineBeforeCaretLine, rep);
const linePosition = getDimensionOfLastBrowserLineOfRepLine(firstLineVisibleBeforeCaretLine, rep); const linePosition =
getDimensionOfLastBrowserLineOfRepLine(firstLineVisibleBeforeCaretLine, rep);
previousLineTop = linePosition.top; previousLineTop = linePosition.top;
} }
return previousLineTop; return previousLineTop;
}; };
function caretLineIsFirstBrowserLine(caretLineTop, rep) { const caretLineIsFirstBrowserLine = (caretLineTop, rep) => {
const caretRepLine = rep.selStart[0]; const caretRepLine = rep.selStart[0];
const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const lineNode = rep.lines.atIndex(caretRepLine).lineNode;
const firstRootNode = getFirstRootChildNode(lineNode); const firstRootNode = getFirstRootChildNode(lineNode);
@ -105,37 +108,28 @@ function caretLineIsFirstBrowserLine(caretLineTop, rep) {
// to get the position of the node we get the position of the first char // to get the position of the node we get the position of the first char
const positionOfFirstRootNode = getPositionOfRepLineAtOffset(firstRootNode, 1); const positionOfFirstRootNode = getPositionOfRepLineAtOffset(firstRootNode, 1);
return positionOfFirstRootNode.top === caretLineTop; return positionOfFirstRootNode.top === caretLineTop;
} };
// find the first root node, usually it is a text node // find the first root node, usually it is a text node
function getFirstRootChildNode(node) { const getFirstRootChildNode = (node) => {
if (!node.firstChild) { if (!node.firstChild) {
return node; return node;
} else { } else {
return getFirstRootChildNode(node.firstChild); return getFirstRootChildNode(node.firstChild);
} }
} };
function getPreviousVisibleLine(line, rep) { const getDimensionOfLastBrowserLineOfRepLine = (line, rep) => {
if (line < 0) {
return 0;
} else if (isLineVisible(line, rep)) {
return line;
} else {
return getPreviousVisibleLine(line - 1, rep);
}
}
function getDimensionOfLastBrowserLineOfRepLine(line, rep) {
const lineNode = rep.lines.atIndex(line).lineNode; const lineNode = rep.lines.atIndex(line).lineNode;
const lastRootChildNode = getLastRootChildNode(lineNode); const lastRootChildNode = getLastRootChildNode(lineNode);
// we get the position of the line in the last char of it // we get the position of the line in the last char of it
const lastRootChildNodePosition = getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); const lastRootChildNodePosition =
getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length);
return lastRootChildNodePosition; return lastRootChildNodePosition;
} };
function getLastRootChildNode(node) { const getLastRootChildNode = (node) => {
if (!node.lastChild) { if (!node.lastChild) {
return { return {
node, node,
@ -144,39 +138,42 @@ function getLastRootChildNode(node) {
} else { } else {
return getLastRootChildNode(node.lastChild); return getLastRootChildNode(node.lastChild);
} }
} };
// here we have two possibilities: // here we have two possibilities:
// [1] The next line is part of the same rep line of the caret line, so we have the same dimensions. // [1] The next line is part of the same rep line of the caret line, so we have the same dimensions.
// So, we can use the caret line to calculate the bottom of the line. // So, we can use the caret line to calculate the bottom of the line.
// [2] the next line is part of another rep line. It's possible this line has different dimensions, so we // [2] the next line is part of another rep line.
// have to get the exactly dimension of it // It's possible this line has different dimensions, so we have to get the exactly dimension of it
exports.getBottomOfNextBrowserLine = function (caretLinePosition, rep) { exports.getBottomOfNextBrowserLine = (caretLinePosition, rep) => {
let nextLineBottom = caretLinePosition.bottom + caretLinePosition.height; // [1] let nextLineBottom = caretLinePosition.bottom + caretLinePosition.height; // [1]
const isCaretLineLastBrowserLine = caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep); const isCaretLineLastBrowserLine =
caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep);
// the caret is at the end of a rep line, so we can get the next browser line dimension // the caret is at the end of a rep line, so we can get the next browser line dimension
// using the position of the first char of the next rep line // using the position of the first char of the next rep line
if (isCaretLineLastBrowserLine) { // [2] if (isCaretLineLastBrowserLine) { // [2]
const nextLineAfterCaretLine = rep.selStart[0] + 1; const nextLineAfterCaretLine = rep.selStart[0] + 1;
const firstNextLineVisibleAfterCaretLine = getNextVisibleLine(nextLineAfterCaretLine, rep); const firstNextLineVisibleAfterCaretLine = getNextVisibleLine(nextLineAfterCaretLine, rep);
const linePosition = getDimensionOfFirstBrowserLineOfRepLine(firstNextLineVisibleAfterCaretLine, rep); const linePosition =
getDimensionOfFirstBrowserLineOfRepLine(firstNextLineVisibleAfterCaretLine, rep);
nextLineBottom = linePosition.bottom; nextLineBottom = linePosition.bottom;
} }
return nextLineBottom; return nextLineBottom;
}; };
function caretLineIsLastBrowserLineOfRepLine(caretLineTop, rep) { const caretLineIsLastBrowserLineOfRepLine = (caretLineTop, rep) => {
const caretRepLine = rep.selStart[0]; const caretRepLine = rep.selStart[0];
const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const lineNode = rep.lines.atIndex(caretRepLine).lineNode;
const lastRootChildNode = getLastRootChildNode(lineNode); const lastRootChildNode = getLastRootChildNode(lineNode);
// we take a rep line and get the position of the last char of it // we take a rep line and get the position of the last char of it
const lastRootChildNodePosition = getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); const lastRootChildNodePosition =
getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length);
return lastRootChildNodePosition.top === caretLineTop; return lastRootChildNodePosition.top === caretLineTop;
} };
function getPreviousVisibleLine(line, rep) { const getPreviousVisibleLine = (line, rep) => {
const firstLineOfPad = 0; const firstLineOfPad = 0;
if (line <= firstLineOfPad) { if (line <= firstLineOfPad) {
return firstLineOfPad; return firstLineOfPad;
@ -185,10 +182,12 @@ function getPreviousVisibleLine(line, rep) {
} else { } else {
return getPreviousVisibleLine(line - 1, rep); return getPreviousVisibleLine(line - 1, rep);
} }
} };
exports.getPreviousVisibleLine = getPreviousVisibleLine; exports.getPreviousVisibleLine = getPreviousVisibleLine;
function getNextVisibleLine(line, rep) { const getNextVisibleLine = (line, rep) => {
const lastLineOfThePad = rep.lines.length() - 1; const lastLineOfThePad = rep.lines.length() - 1;
if (line >= lastLineOfThePad) { if (line >= lastLineOfThePad) {
return lastLineOfThePad; return lastLineOfThePad;
@ -197,31 +196,28 @@ function getNextVisibleLine(line, rep) {
} else { } else {
return getNextVisibleLine(line + 1, rep); return getNextVisibleLine(line + 1, rep);
} }
} };
exports.getNextVisibleLine = getNextVisibleLine; exports.getNextVisibleLine = getNextVisibleLine;
function isLineVisible(line, rep) { const isLineVisible = (line, rep) => rep.lines.atIndex(line).lineNode.offsetHeight > 0;
return rep.lines.atIndex(line).lineNode.offsetHeight > 0;
}
function getDimensionOfFirstBrowserLineOfRepLine(line, rep) { const getDimensionOfFirstBrowserLineOfRepLine = (line, rep) => {
const lineNode = rep.lines.atIndex(line).lineNode; const lineNode = rep.lines.atIndex(line).lineNode;
const firstRootChildNode = getFirstRootChildNode(lineNode); const firstRootChildNode = getFirstRootChildNode(lineNode);
// we can get the position of the line, getting the position of the first char of the rep line // we can get the position of the line, getting the position of the first char of the rep line
const firstRootChildNodePosition = getPositionOfRepLineAtOffset(firstRootChildNode, 1); const firstRootChildNodePosition = getPositionOfRepLineAtOffset(firstRootChildNode, 1);
return firstRootChildNodePosition; return firstRootChildNodePosition;
} };
function getSelectionRange() { const getSelectionRange = () => {
let selection;
if (!window.getSelection) { if (!window.getSelection) {
return; return;
} }
selection = window.getSelection(); const selection = window.getSelection();
if (selection.rangeCount > 0) { if (selection.rangeCount > 0) {
return selection.getRangeAt(0); return selection.getRangeAt(0);
} else { } else {
return null; return null;
} }
} };

View file

@ -1,3 +1,5 @@
'use strict';
/** /**
* This code is mostly from the old Etherpad. Please help us to comment this code. * 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. * This helps other people to understand this code better and helps them to improve it.
@ -26,24 +28,24 @@ const colorutils = {};
// Check that a given value is a css hex color value, e.g. // Check that a given value is a css hex color value, e.g.
// "#ffffff" or "#fff" // "#ffffff" or "#fff"
colorutils.isCssHex = function (cssColor) { colorutils.isCssHex = (cssColor) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(cssColor);
return /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(cssColor);
};
// "#ffffff" or "#fff" or "ffffff" or "fff" to [1.0, 1.0, 1.0] // "#ffffff" or "#fff" or "ffffff" or "fff" to [1.0, 1.0, 1.0]
colorutils.css2triple = function (cssColor) { colorutils.css2triple = (cssColor) => {
const sixHex = colorutils.css2sixhex(cssColor); const sixHex = colorutils.css2sixhex(cssColor);
function hexToFloat(hh) { const hexToFloat = (hh) => Number(`0x${hh}`) / 255;
return Number(`0x${hh}`) / 255; return [
} hexToFloat(sixHex.substr(0, 2)),
return [hexToFloat(sixHex.substr(0, 2)), hexToFloat(sixHex.substr(2, 2)), hexToFloat(sixHex.substr(4, 2))]; hexToFloat(sixHex.substr(2, 2)),
hexToFloat(sixHex.substr(4, 2)),
];
}; };
// "#ffffff" or "#fff" or "ffffff" or "fff" to "ffffff" // "#ffffff" or "#fff" or "ffffff" or "fff" to "ffffff"
colorutils.css2sixhex = function (cssColor) { colorutils.css2sixhex = (cssColor) => {
let h = /[0-9a-fA-F]+/.exec(cssColor)[0]; let h = /[0-9a-fA-F]+/.exec(cssColor)[0];
if (h.length != 6) { if (h.length !== 6) {
const a = h.charAt(0); const a = h.charAt(0);
const b = h.charAt(1); const b = h.charAt(1);
const c = h.charAt(2); const c = h.charAt(2);
@ -53,66 +55,54 @@ colorutils.css2sixhex = function (cssColor) {
}; };
// [1.0, 1.0, 1.0] -> "#ffffff" // [1.0, 1.0, 1.0] -> "#ffffff"
colorutils.triple2css = function (triple) { colorutils.triple2css = (triple) => {
function floatToHex(n) { const floatToHex = (n) => {
const n2 = colorutils.clamp(Math.round(n * 255), 0, 255); const n2 = colorutils.clamp(Math.round(n * 255), 0, 255);
return (`0${n2.toString(16)}`).slice(-2); return (`0${n2.toString(16)}`).slice(-2);
} };
return `#${floatToHex(triple[0])}${floatToHex(triple[1])}${floatToHex(triple[2])}`; return `#${floatToHex(triple[0])}${floatToHex(triple[1])}${floatToHex(triple[2])}`;
}; };
colorutils.clamp = function (v, bot, top) { colorutils.clamp = (v, bot, top) => v < bot ? bot : (v > top ? top : v);
return v < bot ? bot : (v > top ? top : v); colorutils.min3 = (a, b, c) => (a < b) ? (a < c ? a : c) : (b < c ? b : c);
}; colorutils.max3 = (a, b, c) => (a > b) ? (a > c ? a : c) : (b > c ? b : c);
colorutils.min3 = function (a, b, c) { colorutils.colorMin = (c) => colorutils.min3(c[0], c[1], c[2]);
return (a < b) ? (a < c ? a : c) : (b < c ? b : c); colorutils.colorMax = (c) => colorutils.max3(c[0], c[1], c[2]);
}; colorutils.scale = (v, bot, top) => colorutils.clamp(bot + v * (top - bot), 0, 1);
colorutils.max3 = function (a, b, c) { colorutils.unscale = (v, bot, top) => colorutils.clamp((v - bot) / (top - bot), 0, 1);
return (a > b) ? (a > c ? a : c) : (b > c ? b : c);
};
colorutils.colorMin = function (c) {
return colorutils.min3(c[0], c[1], c[2]);
};
colorutils.colorMax = function (c) {
return colorutils.max3(c[0], c[1], c[2]);
};
colorutils.scale = function (v, bot, top) {
return colorutils.clamp(bot + v * (top - bot), 0, 1);
};
colorutils.unscale = function (v, bot, top) {
return colorutils.clamp((v - bot) / (top - bot), 0, 1);
};
colorutils.scaleColor = function (c, bot, top) { colorutils.scaleColor = (c, bot, top) => [
return [colorutils.scale(c[0], bot, top), colorutils.scale(c[1], bot, top), colorutils.scale(c[2], bot, top)]; colorutils.scale(c[0], bot, top),
}; colorutils.scale(c[1], bot, top),
colorutils.scale(c[2], bot, top),
];
colorutils.unscaleColor = function (c, bot, top) { colorutils.unscaleColor = (c, bot, top) => [
return [colorutils.unscale(c[0], bot, top), colorutils.unscale(c[1], bot, top), colorutils.unscale(c[2], bot, top)]; colorutils.unscale(c[0], bot, top),
}; colorutils.unscale(c[1], bot, top),
colorutils.unscale(c[2], bot, top),
];
colorutils.luminosity = function (c) { // rule of thumb for RGB brightness; 1.0 is white
// rule of thumb for RGB brightness; 1.0 is white colorutils.luminosity = (c) => c[0] * 0.30 + c[1] * 0.59 + c[2] * 0.11;
return c[0] * 0.30 + c[1] * 0.59 + c[2] * 0.11;
};
colorutils.saturate = function (c) { colorutils.saturate = (c) => {
const min = colorutils.colorMin(c); const min = colorutils.colorMin(c);
const max = colorutils.colorMax(c); const max = colorutils.colorMax(c);
if (max - min <= 0) return [1.0, 1.0, 1.0]; if (max - min <= 0) return [1.0, 1.0, 1.0];
return colorutils.unscaleColor(c, min, max); return colorutils.unscaleColor(c, min, max);
}; };
colorutils.blend = function (c1, c2, t) { colorutils.blend = (c1, c2, t) => [
return [colorutils.scale(t, c1[0], c2[0]), colorutils.scale(t, c1[1], c2[1]), colorutils.scale(t, c1[2], c2[2])]; colorutils.scale(t, c1[0], c2[0]),
}; colorutils.scale(t, c1[1], c2[1]),
colorutils.scale(t, c1[2], c2[2]),
];
colorutils.invert = function (c) { colorutils.invert = (c) => [1 - c[0], 1 - c[1], 1 - c[2]];
return [1 - c[0], 1 - c[1], 1 - c[2]];
};
colorutils.complementary = function (c) { colorutils.complementary = (c) => {
const inv = colorutils.invert(c); const inv = colorutils.invert(c);
return [ return [
(inv[0] >= c[0]) ? Math.min(inv[0] * 1.30, 1) : (c[0] * 0.30), (inv[0] >= c[0]) ? Math.min(inv[0] * 1.30, 1) : (c[0] * 0.30),
@ -121,9 +111,9 @@ colorutils.complementary = function (c) {
]; ];
}; };
colorutils.textColorFromBackgroundColor = function (bgcolor, skinName) { colorutils.textColorFromBackgroundColor = (bgcolor, skinName) => {
const white = skinName == 'colibris' ? 'var(--super-light-color)' : '#fff'; const white = skinName === 'colibris' ? 'var(--super-light-color)' : '#fff';
const black = skinName == 'colibris' ? 'var(--super-dark-color)' : '#222'; const black = skinName === 'colibris' ? 'var(--super-dark-color)' : '#222';
return colorutils.luminosity(colorutils.css2triple(bgcolor)) < 0.5 ? white : black; return colorutils.luminosity(colorutils.css2triple(bgcolor)) < 0.5 ? white : black;
}; };

View file

@ -1,3 +1,5 @@
'use strict';
/** /**
* This code is mostly from the old Etherpad. Please help us to comment this code. * 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. * This helps other people to understand this code better and helps them to improve it.
@ -20,14 +22,15 @@
* limitations under the License. * limitations under the License.
*/ */
function makeCSSManager(emptyStylesheetTitle, doc) { const makeCSSManager = (emptyStylesheetTitle, doc) => {
if (doc === true) { if (doc === true) {
doc = 'parent'; doc = 'parent';
} else if (!doc) { } else if (!doc) {
doc = 'inner'; doc = 'inner';
} }
function getSheetByTitle(title) { const getSheetByTitle = (title) => {
let win;
if (doc === 'parent') { if (doc === 'parent') {
win = window.parent.parent; win = window.parent.parent;
} else if (doc === 'inner') { } else if (doc === 'inner') {
@ -35,46 +38,44 @@ function makeCSSManager(emptyStylesheetTitle, doc) {
} else if (doc === 'outer') { } else if (doc === 'outer') {
win = window.parent; win = window.parent;
} else { } else {
throw 'Unknown dynamic style container'; throw new Error('Unknown dynamic style container');
} }
const allSheets = win.document.styleSheets; const allSheets = win.document.styleSheets;
for (let i = 0; i < allSheets.length; i++) { for (let i = 0; i < allSheets.length; i++) {
const s = allSheets[i]; const s = allSheets[i];
if (s.title == title) { if (s.title === title) {
return s; return s;
} }
} }
return null; return null;
} };
const browserSheet = getSheetByTitle(emptyStylesheetTitle); const browserSheet = getSheetByTitle(emptyStylesheetTitle);
function browserRules() { const browserRules = () => (browserSheet.cssRules || browserSheet.rules);
return (browserSheet.cssRules || browserSheet.rules);
}
function browserDeleteRule(i) { const browserDeleteRule = (i) => {
if (browserSheet.deleteRule) browserSheet.deleteRule(i); if (browserSheet.deleteRule) browserSheet.deleteRule(i);
else browserSheet.removeRule(i); else browserSheet.removeRule(i);
} };
function browserInsertRule(i, selector) { const browserInsertRule = (i, selector) => {
if (browserSheet.insertRule) browserSheet.insertRule(`${selector} {}`, i); if (browserSheet.insertRule) browserSheet.insertRule(`${selector} {}`, i);
else browserSheet.addRule(selector, null, i); else browserSheet.addRule(selector, null, i);
} };
const selectorList = []; const selectorList = [];
function indexOfSelector(selector) { const indexOfSelector = (selector) => {
for (let i = 0; i < selectorList.length; i++) { for (let i = 0; i < selectorList.length; i++) {
if (selectorList[i] == selector) { if (selectorList[i] === selector) {
return i; return i;
} }
} }
return -1; return -1;
} };
function selectorStyle(selector) { const selectorStyle = (selector) => {
let i = indexOfSelector(selector); let i = indexOfSelector(selector);
if (i < 0) { if (i < 0) {
// add selector // add selector
@ -83,23 +84,21 @@ function makeCSSManager(emptyStylesheetTitle, doc) {
i = 0; i = 0;
} }
return browserRules().item(i).style; return browserRules().item(i).style;
} };
function removeSelectorStyle(selector) { const removeSelectorStyle = (selector) => {
const i = indexOfSelector(selector); const i = indexOfSelector(selector);
if (i >= 0) { if (i >= 0) {
browserDeleteRule(i); browserDeleteRule(i);
selectorList.splice(i, 1); selectorList.splice(i, 1);
} }
} };
return { return {
selectorStyle, selectorStyle,
removeSelectorStyle, removeSelectorStyle,
info() { info: () => `${selectorList.length}:${browserRules().length}`,
return `${selectorList.length}:${browserRules().length}`;
},
}; };
} };
exports.makeCSSManager = makeCSSManager; exports.makeCSSManager = makeCSSManager;

View file

@ -1,3 +1,5 @@
'use strict';
/** /**
* This code is mostly from the old Etherpad. Please help us to comment this code. * 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. * This helps other people to understand this code better and helps them to improve it.
@ -31,7 +33,6 @@
const Changeset = require('./Changeset'); const Changeset = require('./Changeset');
const hooks = require('./pluginfw/hooks'); const hooks = require('./pluginfw/hooks');
const linestylefilter = {}; const linestylefilter = {};
const _ = require('./underscore');
const AttributeManager = require('./AttributeManager'); const AttributeManager = require('./AttributeManager');
const padutils = require('./pad_utils').padutils; const padutils = require('./pad_utils').padutils;
@ -45,32 +46,30 @@ linestylefilter.ATTRIB_CLASSES = {
const lineAttributeMarker = 'lineAttribMarker'; const lineAttributeMarker = 'lineAttribMarker';
exports.lineAttributeMarker = lineAttributeMarker; exports.lineAttributeMarker = lineAttributeMarker;
linestylefilter.getAuthorClassName = function (author) { linestylefilter.getAuthorClassName = (author) => `author-${author.replace(/[^a-y0-9]/g, (c) => {
return `author-${author.replace(/[^a-y0-9]/g, (c) => { if (c === '.') return '-';
if (c == '.') return '-'; return `z${c.charCodeAt(0)}z`;
return `z${c.charCodeAt(0)}z`; })}`;
})}`;
};
// lineLength is without newline; aline includes newline, // lineLength is without newline; aline includes newline,
// but may be falsy if lineLength == 0 // but may be falsy if lineLength == 0
linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFunc, apool) { linestylefilter.getLineStyleFilter = (lineLength, aline, textAndClassFunc, apool) => {
// Plugin Hook to add more Attrib Classes // Plugin Hook to add more Attrib Classes
for (const attribClasses of hooks.callAll('aceAttribClasses', linestylefilter.ATTRIB_CLASSES)) { for (const attribClasses of hooks.callAll('aceAttribClasses', linestylefilter.ATTRIB_CLASSES)) {
Object.assign(linestylefilter.ATTRIB_CLASSES, attribClasses); Object.assign(linestylefilter.ATTRIB_CLASSES, attribClasses);
} }
if (lineLength == 0) return textAndClassFunc; if (lineLength === 0) return textAndClassFunc;
const nextAfterAuthorColors = textAndClassFunc; const nextAfterAuthorColors = textAndClassFunc;
const authorColorFunc = (function () { const authorColorFunc = (() => {
const lineEnd = lineLength; const lineEnd = lineLength;
let curIndex = 0; let curIndex = 0;
let extraClasses; let extraClasses;
let leftInAuthor; let leftInAuthor;
function attribsToClasses(attribs) { const attribsToClasses = (attribs) => {
let classes = ''; let classes = '';
let isLineAttribMarker = false; let isLineAttribMarker = false;
top.console.log("attribs", attribs) top.console.log("attribs", attribs)
@ -81,14 +80,14 @@ top.console.log("attribs", attribs)
if (key) { if (key) {
const value = apool.getAttribValue(n); const value = apool.getAttribValue(n);
if (value) { if (value) {
if (!isLineAttribMarker && _.indexOf(AttributeManager.lineAttributes, key) >= 0) { if (!isLineAttribMarker && AttributeManager.lineAttributes.indexOf(key) >= 0) {
isLineAttribMarker = true; isLineAttribMarker = true;
} }
if (key == 'author') { if (key === 'author') {
classes += ` ${linestylefilter.getAuthorClassName(value)}`; classes += ` ${linestylefilter.getAuthorClassName(value)}`;
} else if (key == 'list') { } else if (key === 'list') {
classes += ` list:${value}`; classes += ` list:${value}`;
} else if (key == 'start') { } else if (key === 'start') {
// Needed to introduce the correct Ordered list item start number on import // Needed to introduce the correct Ordered list item start number on import
classes += ` start:${value}`; classes += ` start:${value}`;
} else if (linestylefilter.ATTRIB_CLASSES[key]) { } else if (linestylefilter.ATTRIB_CLASSES[key]) {
@ -106,37 +105,38 @@ top.console.log("attribs", attribs)
if (isLineAttribMarker) classes += ` ${lineAttributeMarker}`; if (isLineAttribMarker) classes += ` ${lineAttributeMarker}`;
return classes.substring(1); return classes.substring(1);
} };
const attributionIter = Changeset.opIterator(aline); const attributionIter = Changeset.opIterator(aline);
let nextOp, nextOpClasses; let nextOp, nextOpClasses;
function goNextOp() { const goNextOp = () => {
nextOp = attributionIter.next(); nextOp = attributionIter.next();
nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs)); nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
} };
goNextOp(); goNextOp();
function nextClasses() { const nextClasses = () => {
if (curIndex < lineEnd) { if (curIndex < lineEnd) {
extraClasses = nextOpClasses; extraClasses = nextOpClasses;
leftInAuthor = nextOp.chars; leftInAuthor = nextOp.chars;
goNextOp(); goNextOp();
while (nextOp.opcode && nextOpClasses == extraClasses) { while (nextOp.opcode && nextOpClasses === extraClasses) {
leftInAuthor += nextOp.chars; leftInAuthor += nextOp.chars;
goNextOp(); goNextOp();
} }
} }
} };
nextClasses(); nextClasses();
return function (txt, cls) { return (txt, cls) => {
const disableAuthColorForThisLine = hooks.callAll('disableAuthorColorsForThisLine', { const disableAuthColorForThisLine = hooks.callAll('disableAuthorColorsForThisLine', {
linestylefilter, linestylefilter,
text: txt, text: txt,
class: cls, class: cls,
}, ' ', ' ', ''); }, ' ', ' ', '');
const disableAuthors = (disableAuthColorForThisLine == null || disableAuthColorForThisLine.length == 0) ? false : disableAuthColorForThisLine[0]; const disableAuthors = (disableAuthColorForThisLine == null ||
disableAuthColorForThisLine.length === 0) ? false : disableAuthColorForThisLine[0];
while (txt.length > 0) { while (txt.length > 0) {
if (leftInAuthor <= 0 || disableAuthors) { if (leftInAuthor <= 0 || disableAuthors) {
// prevent infinite loop if something funny's going on // prevent infinite loop if something funny's going on
@ -151,7 +151,7 @@ top.console.log("attribs", attribs)
nextAfterAuthorColors(curTxt, (cls && `${cls} `) + extraClasses); nextAfterAuthorColors(curTxt, (cls && `${cls} `) + extraClasses);
curIndex += spanSize; curIndex += spanSize;
leftInAuthor -= spanSize; leftInAuthor -= spanSize;
if (leftInAuthor == 0) { if (leftInAuthor === 0) {
nextClasses(); nextClasses();
} }
} }
@ -160,7 +160,7 @@ top.console.log("attribs", attribs)
return authorColorFunc; return authorColorFunc;
}; };
linestylefilter.getAtSignSplitterFilter = function (lineText, textAndClassFunc) { linestylefilter.getAtSignSplitterFilter = (lineText, textAndClassFunc) => {
const at = /@/g; const at = /@/g;
at.lastIndex = 0; at.lastIndex = 0;
let splitPoints = null; let splitPoints = null;
@ -177,66 +177,66 @@ linestylefilter.getAtSignSplitterFilter = function (lineText, textAndClassFunc)
return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints); return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints);
}; };
linestylefilter.getRegexpFilter = function (regExp, tag) { linestylefilter.getRegexpFilter = (regExp, tag) => (lineText, textAndClassFunc) => {
return function (lineText, textAndClassFunc) { regExp.lastIndex = 0;
regExp.lastIndex = 0; let regExpMatchs = null;
let regExpMatchs = null; let splitPoints = null;
let splitPoints = null; let execResult;
let execResult; while ((execResult = regExp.exec(lineText))) {
while ((execResult = regExp.exec(lineText))) { if (!regExpMatchs) {
if (!regExpMatchs) { regExpMatchs = [];
regExpMatchs = []; splitPoints = [];
splitPoints = [];
}
const startIndex = execResult.index;
const regExpMatch = execResult[0];
regExpMatchs.push([startIndex, regExpMatch]);
splitPoints.push(startIndex, startIndex + regExpMatch.length);
} }
const startIndex = execResult.index;
const regExpMatch = execResult[0];
regExpMatchs.push([startIndex, regExpMatch]);
splitPoints.push(startIndex, startIndex + regExpMatch.length);
}
if (!regExpMatchs) return textAndClassFunc; if (!regExpMatchs) return textAndClassFunc;
function regExpMatchForIndex(idx) { const regExpMatchForIndex = (idx) => {
for (let k = 0; k < regExpMatchs.length; k++) { for (let k = 0; k < regExpMatchs.length; k++) {
const u = regExpMatchs[k]; const u = regExpMatchs[k];
if (idx >= u[0] && idx < u[0] + u[1].length) { if (idx >= u[0] && idx < u[0] + u[1].length) {
return u[1]; return u[1];
}
} }
return false;
} }
return false;
const handleRegExpMatchsAfterSplit = (function () {
let curIndex = 0;
return function (txt, cls) {
const txtlen = txt.length;
let newCls = cls;
const regExpMatch = regExpMatchForIndex(curIndex);
if (regExpMatch) {
newCls += ` ${tag}:${regExpMatch}`;
}
textAndClassFunc(txt, newCls);
curIndex += txtlen;
};
})();
return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints);
}; };
const handleRegExpMatchsAfterSplit = (() => {
let curIndex = 0;
return (txt, cls) => {
const txtlen = txt.length;
let newCls = cls;
const regExpMatch = regExpMatchForIndex(curIndex);
if (regExpMatch) {
newCls += ` ${tag}:${regExpMatch}`;
}
textAndClassFunc(txt, newCls);
curIndex += txtlen;
};
})();
return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints);
}; };
linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(padutils.urlRegex, 'url'); linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(padutils.urlRegex, 'url');
linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt) { linestylefilter.textAndClassFuncSplitter = (func, splitPointsOpt) => {
let nextPointIndex = 0; let nextPointIndex = 0;
let idx = 0; let idx = 0;
// don't split at 0 // don't split at 0
while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0) { while (splitPointsOpt &&
nextPointIndex < splitPointsOpt.length &&
splitPointsOpt[nextPointIndex] === 0) {
nextPointIndex++; nextPointIndex++;
} }
function spanHandler(txt, cls) { const spanHandler = (txt, cls) => {
if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length) { if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length) {
func(txt, cls); func(txt, cls);
idx += txt.length; idx += txt.length;
@ -247,7 +247,7 @@ linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt) {
if (pointLocInSpan >= txtlen) { if (pointLocInSpan >= txtlen) {
func(txt, cls); func(txt, cls);
idx += txt.length; idx += txt.length;
if (pointLocInSpan == txtlen) { if (pointLocInSpan === txtlen) {
nextPointIndex++; nextPointIndex++;
} }
} else { } else {
@ -260,18 +260,18 @@ linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt) {
spanHandler(txt.substring(pointLocInSpan), cls); spanHandler(txt.substring(pointLocInSpan), cls);
} }
} }
} };
return spanHandler; return spanHandler;
}; };
linestylefilter.getFilterStack = function (lineText, textAndClassFunc, abrowser) { linestylefilter.getFilterStack = (lineText, textAndClassFunc, abrowser) => {
let func = linestylefilter.getURLFilter(lineText, textAndClassFunc); let func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
const hookFilters = hooks.callAll('aceGetFilterStack', { const hookFilters = hooks.callAll('aceGetFilterStack', {
linestylefilter, linestylefilter,
browser: abrowser, browser: abrowser,
}); });
_.map(hookFilters, (hookFilter) => { hookFilters.map((hookFilter) => {
func = hookFilter(lineText, func); func = hookFilter(lineText, func);
}); });
@ -279,16 +279,16 @@ linestylefilter.getFilterStack = function (lineText, textAndClassFunc, abrowser)
}; };
// domLineObj is like that returned by domline.createDomLine // domLineObj is like that returned by domline.createDomLine
linestylefilter.populateDomLine = function (textLine, aline, apool, domLineObj) { linestylefilter.populateDomLine = (textLine, aline, apool, domLineObj) => {
// remove final newline from text if any // remove final newline from text if any
let text = textLine; let text = textLine;
if (text.slice(-1) == '\n') { if (text.slice(-1) === '\n') {
text = text.substring(0, text.length - 1); text = text.substring(0, text.length - 1);
} }
function textAndClassFunc(tokenText, tokenClass) { const textAndClassFunc = (tokenText, tokenClass) => {
domLineObj.appendSpan(tokenText, tokenClass); domLineObj.appendSpan(tokenText, tokenClass);
} };
let func = linestylefilter.getFilterStack(text, textAndClassFunc); let func = linestylefilter.getFilterStack(text, textAndClassFunc);
func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool); func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool);

View file

@ -1,4 +1,5 @@
// Proviedes a require'able version of jQuery without leaking $ and jQuery; 'use strict';
// Provides a require'able version of jQuery without leaking $ and jQuery;
window.$ = require('./jquery'); window.$ = require('./jquery');
const jq = window.$.noConflict(true); const jq = window.$.noConflict(true);
exports.jQuery = exports.$ = jq; exports.jQuery = exports.$ = jq;