mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-05 06:37:10 -04:00
Added POC for browser
This commit is contained in:
parent
2c2d1256d5
commit
7bad78d751
12 changed files with 117 additions and 104 deletions
|
@ -10,6 +10,9 @@ const settings = require('../../utils/Settings');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const webaccess = require('./webaccess');
|
const webaccess = require('./webaccess');
|
||||||
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
||||||
|
import {hash, createHash} from 'node:crypto'
|
||||||
|
|
||||||
|
|
||||||
import {buildSync} from 'esbuild'
|
import {buildSync} from 'esbuild'
|
||||||
exports.expressPreSession = async (hookName:string, {app}:any) => {
|
exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||||
// This endpoint is intended to conform to:
|
// This endpoint is intended to conform to:
|
||||||
|
@ -94,18 +97,30 @@ exports.expressCreateServer = async (hookName: string, args: any, cb: Function)
|
||||||
})(),
|
})(),
|
||||||
settings,
|
settings,
|
||||||
}));
|
}));
|
||||||
|
const hash = createHash('sha256').update(fs.readFileSync(path.join(settings.root, 'var/js/padbootstrap.js'))).digest('hex');
|
||||||
|
|
||||||
|
const fileName = `padbootstrap-${hash.substring(0,16)}.min.js`
|
||||||
const result = buildSync({
|
const result = buildSync({
|
||||||
entryPoints: [settings.root + "/src/templates/padBootstrap.js"], // Entry file(s)
|
entryPoints: [settings.root + "/var/js/padbootstrap.js"], // Entry file(s)
|
||||||
bundle: true, // Bundle the files together
|
bundle: true, // Bundle the files together
|
||||||
minify: true, // Minify the output
|
minify: false, // Minify the output
|
||||||
sourcemap: true, // Generate source maps
|
sourcemap: true, // Generate source maps
|
||||||
sourceRoot: settings.root+"/src/static/js/",
|
sourceRoot: settings.root+"/src/static/js/",
|
||||||
target: ['es2020'], // Target ECMAScript version
|
target: ['es2020'], // Target ECMAScript version
|
||||||
write: false, // Do not write to file system,
|
metafile: true,
|
||||||
|
|
||||||
|
write: true, // Do not write to file system,
|
||||||
|
outfile: settings.root + `/var/js/${fileName}`, // Output file
|
||||||
})
|
})
|
||||||
|
|
||||||
const textResult = result.outputFiles[0].text
|
|
||||||
|
args.app.get(`/${fileName}`, (req: any, res: any) => {
|
||||||
|
res.sendFile(settings.root+`/var/js/${fileName}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
args.app.get(`/${fileName}.map`, (req: any, res: any) => {
|
||||||
|
res.sendFile(settings.root+`/var/js/${fileName}.map`)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
// serve pad.html under /p
|
// serve pad.html under /p
|
||||||
|
@ -115,7 +130,7 @@ exports.expressCreateServer = async (hookName: string, args: any, cb: Function)
|
||||||
|
|
||||||
hooks.callAll('padInitToolbar', {
|
hooks.callAll('padInitToolbar', {
|
||||||
toolbar,
|
toolbar,
|
||||||
isReadOnly,
|
isReadOnly
|
||||||
});
|
});
|
||||||
|
|
||||||
// can be removed when require-kernel is dropped
|
// can be removed when require-kernel is dropped
|
||||||
|
@ -124,6 +139,7 @@ exports.expressCreateServer = async (hookName: string, args: any, cb: Function)
|
||||||
req,
|
req,
|
||||||
toolbar,
|
toolbar,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
|
entrypoint: "/"+fileName
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -145,6 +161,4 @@ exports.expressCreateServer = async (hookName: string, args: any, cb: Function)
|
||||||
// express-session automatically calls req.session.touch() so we don't need to do it here.
|
// express-session automatically calls req.session.touch() so we don't need to do it here.
|
||||||
res.json({status: 'ok'});
|
res.json({status: 'ok'});
|
||||||
});
|
});
|
||||||
|
|
||||||
return cb();
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ const AttributeMap = require('./AttributeMap');
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const ChangesetUtils = require('./ChangesetUtils');
|
const ChangesetUtils = require('./ChangesetUtils');
|
||||||
const attributes = require('./attributes');
|
const attributes = require('./attributes');
|
||||||
const _ = require('./underscore');
|
const underscore = require("underscore")
|
||||||
|
|
||||||
const lineMarkerAttribute = 'lmkr';
|
const lineMarkerAttribute = 'lmkr';
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ const AttributeManager = function (rep, applyChangesetCallback) {
|
||||||
AttributeManager.DEFAULT_LINE_ATTRIBUTES = DEFAULT_LINE_ATTRIBUTES;
|
AttributeManager.DEFAULT_LINE_ATTRIBUTES = DEFAULT_LINE_ATTRIBUTES;
|
||||||
AttributeManager.lineAttributes = lineAttributes;
|
AttributeManager.lineAttributes = lineAttributes;
|
||||||
|
|
||||||
AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
AttributeManager.prototype = underscore.default(AttributeManager.prototype).extend({
|
||||||
|
|
||||||
applyChangeset(changeset) {
|
applyChangeset(changeset) {
|
||||||
if (!this.applyChangesetCallback) return changeset;
|
if (!this.applyChangesetCallback) return changeset;
|
||||||
|
@ -335,7 +335,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
||||||
|
|
||||||
ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]);
|
ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [lineNum, 0]);
|
||||||
|
|
||||||
const countAttribsWithMarker = _.chain(attribs).filter((a) => !!a[1])
|
const countAttribsWithMarker = underscore.chain(attribs).filter((a) => !!a[1])
|
||||||
.map((a) => a[0]).difference(DEFAULT_LINE_ATTRIBUTES).size().value();
|
.map((a) => a[0]).difference(DEFAULT_LINE_ATTRIBUTES).size().value();
|
||||||
|
|
||||||
// if we have marker and any of attributes don't need to have marker. we need delete it
|
// if we have marker and any of attributes don't need to have marker. we need delete it
|
||||||
|
|
|
@ -27,9 +27,10 @@
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
const makeCSSManager = require('./cssmanager').makeCSSManager;
|
const makeCSSManager = require('./cssmanager').makeCSSManager;
|
||||||
const pluginUtils = require('./pluginfw/shared');
|
const pluginUtils = require('./pluginfw/shared');
|
||||||
|
const ace2_inner = require('ep_etherpad-lite/static/js/ace2_inner')
|
||||||
const debugLog = (...args) => {};
|
const debugLog = (...args) => {};
|
||||||
|
const cl_plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins')
|
||||||
|
const rJQuery = require('ep_etherpad-lite/static/js/rjquery')
|
||||||
// The inner and outer iframe's locations are about:blank, so relative URLs are relative to that.
|
// The inner and outer iframe's locations are about:blank, so relative URLs are relative to that.
|
||||||
// Firefox and Chrome seem to do what the developer intends if given a relative URL, but Safari
|
// Firefox and Chrome seem to do what the developer intends if given a relative URL, but Safari
|
||||||
// errors out unless given an absolute URL for a JavaScript-created element.
|
// errors out unless given an absolute URL for a JavaScript-created element.
|
||||||
|
@ -99,6 +100,7 @@ const Ace2Editor = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
const doActionsPendingInit = () => {
|
const doActionsPendingInit = () => {
|
||||||
|
console.log('doActionsPendingInit', actionsPendingInit)
|
||||||
for (const fn of actionsPendingInit) fn();
|
for (const fn of actionsPendingInit) fn();
|
||||||
actionsPendingInit = [];
|
actionsPendingInit = [];
|
||||||
};
|
};
|
||||||
|
@ -257,19 +259,19 @@ const Ace2Editor = function () {
|
||||||
|
|
||||||
// <head> tag
|
// <head> tag
|
||||||
addStyleTagsFor(innerDocument, includedCSS);
|
addStyleTagsFor(innerDocument, includedCSS);
|
||||||
const requireKernel = innerDocument.createElement('script');
|
//const requireKernel = innerDocument.createElement('script');
|
||||||
requireKernel.type = 'text/javascript';
|
//requireKernel.type = 'text/javascript';
|
||||||
requireKernel.src =
|
//requireKernel.src =
|
||||||
absUrl(`../static/js/require-kernel.js?v=${clientVars.randomVersionString}`);
|
// absUrl(`../static/js/require-kernel.js?v=${clientVars.randomVersionString}`);
|
||||||
innerDocument.head.appendChild(requireKernel);
|
//innerDocument.head.appendChild(requireKernel);
|
||||||
// Pre-fetch modules to improve load performance.
|
// Pre-fetch modules to improve load performance.
|
||||||
for (const module of ['ace2_inner', 'ace2_common']) {
|
/*for (const module of ['ace2_inner', 'ace2_common']) {
|
||||||
const script = innerDocument.createElement('script');
|
const script = innerDocument.createElement('script');
|
||||||
script.type = 'text/javascript';
|
script.type = 'text/javascript';
|
||||||
script.src = absUrl(`../javascripts/lib/ep_etherpad-lite/static/js/${module}.js` +
|
script.src = absUrl(`../javascripts/lib/ep_etherpad-lite/static/js/${module}.js` +
|
||||||
`?callback=require.define&v=${clientVars.randomVersionString}`);
|
`?callback=require.define&v=${clientVars.randomVersionString}`);
|
||||||
innerDocument.head.appendChild(script);
|
innerDocument.head.appendChild(script);
|
||||||
}
|
}*/
|
||||||
const innerStyle = innerDocument.createElement('style');
|
const innerStyle = innerDocument.createElement('style');
|
||||||
innerStyle.type = 'text/css';
|
innerStyle.type = 'text/css';
|
||||||
innerStyle.title = 'dynamicsyntax';
|
innerStyle.title = 'dynamicsyntax';
|
||||||
|
@ -284,7 +286,7 @@ const Ace2Editor = function () {
|
||||||
innerDocument.body.classList.add('innerdocbody');
|
innerDocument.body.classList.add('innerdocbody');
|
||||||
innerDocument.body.setAttribute('spellcheck', 'false');
|
innerDocument.body.setAttribute('spellcheck', 'false');
|
||||||
innerDocument.body.appendChild(innerDocument.createTextNode('\u00A0')); //
|
innerDocument.body.appendChild(innerDocument.createTextNode('\u00A0')); //
|
||||||
|
/*
|
||||||
debugLog('Ace2Editor.init() waiting for require kernel load');
|
debugLog('Ace2Editor.init() waiting for require kernel load');
|
||||||
await eventFired(requireKernel, 'load');
|
await eventFired(requireKernel, 'load');
|
||||||
debugLog('Ace2Editor.init() require kernel loaded');
|
debugLog('Ace2Editor.init() require kernel loaded');
|
||||||
|
@ -292,17 +294,16 @@ const Ace2Editor = function () {
|
||||||
require.setRootURI(absUrl('../javascripts/src'));
|
require.setRootURI(absUrl('../javascripts/src'));
|
||||||
require.setLibraryURI(absUrl('../javascripts/lib'));
|
require.setLibraryURI(absUrl('../javascripts/lib'));
|
||||||
require.setGlobalKeyPath('require');
|
require.setGlobalKeyPath('require');
|
||||||
|
*/
|
||||||
// intentially moved before requiring client_plugins to save a 307
|
// intentially moved before requiring client_plugins to save a 307
|
||||||
innerWindow.Ace2Inner = require('ep_etherpad-lite/static/js/ace2_inner');
|
innerWindow.Ace2Inner = ace2_inner;
|
||||||
innerWindow.plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins');
|
innerWindow.plugins = cl_plugins;
|
||||||
innerWindow.plugins.adoptPluginsFromAncestorsOf(innerWindow);
|
|
||||||
|
|
||||||
innerWindow.$ = innerWindow.jQuery = require('ep_etherpad-lite/static/js/rjquery').jQuery;
|
innerWindow.$ = innerWindow.jQuery = rJQuery.jQuery;
|
||||||
|
|
||||||
debugLog('Ace2Editor.init() waiting for plugins');
|
debugLog('Ace2Editor.init() waiting for plugins');
|
||||||
await new Promise((resolve, reject) => innerWindow.plugins.ensure(
|
/*await new Promise((resolve, reject) => innerWindow.plugins.ensure(
|
||||||
(err) => err != null ? reject(err) : resolve()));
|
(err) => err != null ? reject(err) : resolve()));*/
|
||||||
debugLog('Ace2Editor.init() waiting for Ace2Inner.init()');
|
debugLog('Ace2Editor.init() waiting for Ace2Inner.init()');
|
||||||
await innerWindow.Ace2Inner.init(info, {
|
await innerWindow.Ace2Inner.init(info, {
|
||||||
inner: makeCSSManager(innerStyle.sheet),
|
inner: makeCSSManager(innerStyle.sheet),
|
||||||
|
|
|
@ -54,13 +54,16 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
let thisAuthor = '';
|
let thisAuthor = '';
|
||||||
|
|
||||||
let disposed = false;
|
let disposed = false;
|
||||||
|
const outerWin = document.getElementsByName("ace_outer")[0]
|
||||||
|
const targetDoc = outerWin.contentWindow.document.getElementsByName("ace_inner")[0].contentWindow.document
|
||||||
|
const targetBody = targetDoc.body
|
||||||
|
|
||||||
const focus = () => {
|
const focus = () => {
|
||||||
window.focus();
|
targetBody.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const outerWin = window.parent;
|
const outerDoc = outerWin.contentWindow.document;
|
||||||
const outerDoc = outerWin.document;
|
|
||||||
const sideDiv = outerDoc.getElementById('sidediv');
|
const sideDiv = outerDoc.getElementById('sidediv');
|
||||||
const lineMetricsDiv = outerDoc.getElementById('linemetricsdiv');
|
const lineMetricsDiv = outerDoc.getElementById('linemetricsdiv');
|
||||||
const sideDivInner = outerDoc.getElementById('sidedivinner');
|
const sideDivInner = outerDoc.getElementById('sidedivinner');
|
||||||
|
@ -415,7 +418,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
|
|
||||||
const setWraps = (newVal) => {
|
const setWraps = (newVal) => {
|
||||||
doesWrap = newVal;
|
doesWrap = newVal;
|
||||||
document.body.classList.toggle('doesWrap', doesWrap);
|
targetBody.classList.toggle('doesWrap', doesWrap);
|
||||||
scheduler.setTimeout(() => {
|
scheduler.setTimeout(() => {
|
||||||
inCallStackIfNecessary('setWraps', () => {
|
inCallStackIfNecessary('setWraps', () => {
|
||||||
fastIncorp(7);
|
fastIncorp(7);
|
||||||
|
@ -445,7 +448,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setTextFace = (face) => {
|
const setTextFace = (face) => {
|
||||||
document.body.style.fontFamily = face;
|
targetBody.style.fontFamily = face;
|
||||||
lineMetricsDiv.style.fontFamily = face;
|
lineMetricsDiv.style.fontFamily = face;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -456,8 +459,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
|
|
||||||
const setEditable = (newVal) => {
|
const setEditable = (newVal) => {
|
||||||
isEditable = newVal;
|
isEditable = newVal;
|
||||||
document.body.contentEditable = isEditable ? 'true' : 'false';
|
targetBody.contentEditable = isEditable ? 'true' : 'false';
|
||||||
document.body.classList.toggle('static', !isEditable);
|
targetBody.classList.toggle('static', !isEditable);
|
||||||
};
|
};
|
||||||
|
|
||||||
const enforceEditability = () => setEditable(isEditable);
|
const enforceEditability = () => setEditable(isEditable);
|
||||||
|
@ -480,6 +483,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
newText = `${lines.join('\n')}\n`;
|
newText = `${lines.join('\n')}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.console.log('importText', {text, undoable, dontProcess, newText})
|
||||||
|
|
||||||
inCallStackIfNecessary(`importText${undoable ? 'Undoable' : ''}`, () => {
|
inCallStackIfNecessary(`importText${undoable ? 'Undoable' : ''}`, () => {
|
||||||
setDocText(newText);
|
setDocText(newText);
|
||||||
});
|
});
|
||||||
|
@ -520,6 +525,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
|
|
||||||
const oldLen = rep.lines.totalWidth();
|
const oldLen = rep.lines.totalWidth();
|
||||||
const numLines = rep.lines.length();
|
const numLines = rep.lines.length();
|
||||||
|
window.console.log(rep, numLines - 1);
|
||||||
const upToLastLine = rep.lines.offsetOfIndex(numLines - 1);
|
const upToLastLine = rep.lines.offsetOfIndex(numLines - 1);
|
||||||
const lastLineLength = rep.lines.atIndex(numLines - 1).text.length;
|
const lastLineLength = rep.lines.atIndex(numLines - 1).text.length;
|
||||||
const assem = Changeset.smartOpAssembler();
|
const assem = Changeset.smartOpAssembler();
|
||||||
|
@ -640,8 +646,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// These properties are exposed
|
// These properties are exposed
|
||||||
const setters = {
|
const setters = {
|
||||||
wraps: setWraps,
|
wraps: setWraps,
|
||||||
showsauthorcolors: (val) => document.body.classList.toggle('authorColors', !!val),
|
showsauthorcolors: (val) => targetBody.classList.toggle('authorColors', !!val),
|
||||||
showsuserselections: (val) => document.body.classList.toggle('userSelections', !!val),
|
showsuserselections: (val) => targetBody.classList.toggle('userSelections', !!val),
|
||||||
showslinenumbers: (value) => {
|
showslinenumbers: (value) => {
|
||||||
hasLineNumbers = !!value;
|
hasLineNumbers = !!value;
|
||||||
sideDiv.parentNode.classList.toggle('line-numbers-hidden', !hasLineNumbers);
|
sideDiv.parentNode.classList.toggle('line-numbers-hidden', !hasLineNumbers);
|
||||||
|
@ -654,8 +660,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
styled: setStyled,
|
styled: setStyled,
|
||||||
textface: setTextFace,
|
textface: setTextFace,
|
||||||
rtlistrue: (value) => {
|
rtlistrue: (value) => {
|
||||||
document.body.classList.toggle('rtl', value);
|
targetBody.classList.toggle('rtl', value);
|
||||||
document.body.classList.toggle('ltr', !value);
|
targetBody.classList.toggle('ltr', !value);
|
||||||
document.documentElement.dir = value ? 'rtl' : 'ltr';
|
document.documentElement.dir = value ? 'rtl' : 'ltr';
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -894,11 +900,11 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
clearObservedChanges();
|
clearObservedChanges();
|
||||||
|
|
||||||
const getCleanNodeByKey = (key) => {
|
const getCleanNodeByKey = (key) => {
|
||||||
let n = document.getElementById(key);
|
let n = targetDoc.getElementById(key);
|
||||||
// copying and pasting can lead to duplicate ids
|
// copying and pasting can lead to duplicate ids
|
||||||
while (n && isNodeDirty(n)) {
|
while (n && isNodeDirty(n)) {
|
||||||
n.id = '';
|
n.id = '';
|
||||||
n = document.getElementById(key);
|
n = targetDoc.getElementById(key);
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
};
|
};
|
||||||
|
@ -980,11 +986,11 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const observeSuspiciousNodes = () => {
|
const observeSuspiciousNodes = () => {
|
||||||
// inspired by Firefox bug #473255, where pasting formatted text
|
// inspired by Firefox bug #473255, where pasting formatted text
|
||||||
// causes the cursor to jump away, making the new HTML never found.
|
// causes the cursor to jump away, making the new HTML never found.
|
||||||
if (document.body.getElementsByTagName) {
|
if (targetBody.getElementsByTagName) {
|
||||||
const elts = document.body.getElementsByTagName('style');
|
const elts = targetBody.getElementsByTagName('style');
|
||||||
for (const elt of elts) {
|
for (const elt of elts) {
|
||||||
const n = topLevel(elt);
|
const n = topLevel(elt);
|
||||||
if (n && n.parentNode === document.body) {
|
if (n && n.parentNode === targetBody) {
|
||||||
observeChangesAroundNode(n);
|
observeChangesAroundNode(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -999,8 +1005,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
if (DEBUG && window.DONT_INCORP || window.DEBUG_DONT_INCORP) return false;
|
if (DEBUG && window.DONT_INCORP || window.DEBUG_DONT_INCORP) return false;
|
||||||
|
|
||||||
// returns true if dom changes were made
|
// returns true if dom changes were made
|
||||||
if (!document.body.firstChild) {
|
if (!targetBody.firstChild) {
|
||||||
document.body.innerHTML = '<div><!-- --></div>';
|
targetBody.innerHTML = '<div><!-- --></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
observeChangesAroundSelection();
|
observeChangesAroundSelection();
|
||||||
|
@ -1022,7 +1028,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if (!dirtyRangesCheckOut) {
|
if (!dirtyRangesCheckOut) {
|
||||||
for (const bodyNode of document.body.childNodes) {
|
for (const bodyNode of targetBody.childNodes) {
|
||||||
if ((bodyNode.tagName) && ((!bodyNode.id) || (!rep.lines.containsKey(bodyNode.id)))) {
|
if ((bodyNode.tagName) && ((!bodyNode.id) || (!rep.lines.containsKey(bodyNode.id)))) {
|
||||||
observeChangesAroundNode(bodyNode);
|
observeChangesAroundNode(bodyNode);
|
||||||
}
|
}
|
||||||
|
@ -1044,11 +1050,11 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const range = dirtyRanges[i];
|
const range = dirtyRanges[i];
|
||||||
a = range[0];
|
a = range[0];
|
||||||
b = range[1];
|
b = range[1];
|
||||||
let firstDirtyNode = (((a === 0) && document.body.firstChild) ||
|
let firstDirtyNode = (((a === 0) && targetBody.firstChild) ||
|
||||||
getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling);
|
getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling);
|
||||||
firstDirtyNode = (firstDirtyNode && isNodeDirty(firstDirtyNode) && firstDirtyNode);
|
firstDirtyNode = (firstDirtyNode && isNodeDirty(firstDirtyNode) && firstDirtyNode);
|
||||||
|
|
||||||
let lastDirtyNode = (((b === rep.lines.length()) && document.body.lastChild) ||
|
let lastDirtyNode = (((b === rep.lines.length()) && targetBody.lastChild) ||
|
||||||
getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling);
|
getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling);
|
||||||
|
|
||||||
lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode);
|
lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode);
|
||||||
|
@ -1135,7 +1141,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
callstack: currentCallStack,
|
callstack: currentCallStack,
|
||||||
editorInfo,
|
editorInfo,
|
||||||
rep,
|
rep,
|
||||||
root: document.body,
|
root: targetBody,
|
||||||
point: selection.startPoint,
|
point: selection.startPoint,
|
||||||
documentAttributeManager,
|
documentAttributeManager,
|
||||||
});
|
});
|
||||||
|
@ -1147,7 +1153,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
callstack: currentCallStack,
|
callstack: currentCallStack,
|
||||||
editorInfo,
|
editorInfo,
|
||||||
rep,
|
rep,
|
||||||
root: document.body,
|
root: targetBody,
|
||||||
point: selection.endPoint,
|
point: selection.endPoint,
|
||||||
documentAttributeManager,
|
documentAttributeManager,
|
||||||
});
|
});
|
||||||
|
@ -1227,9 +1233,9 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
info.prepareForAdd();
|
info.prepareForAdd();
|
||||||
entry.lineMarker = info.lineMarker;
|
entry.lineMarker = info.lineMarker;
|
||||||
if (!nodeToAddAfter) {
|
if (!nodeToAddAfter) {
|
||||||
document.body.insertBefore(node, document.body.firstChild);
|
targetBody.insertBefore(node, targetBody.firstChild);
|
||||||
} else {
|
} else {
|
||||||
document.body.insertBefore(node, nodeToAddAfter.nextSibling);
|
targetBody.insertBefore(node, nodeToAddAfter.nextSibling);
|
||||||
}
|
}
|
||||||
nodeToAddAfter = node;
|
nodeToAddAfter = node;
|
||||||
info.notifyAdded();
|
info.notifyAdded();
|
||||||
|
@ -1326,7 +1332,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// Turn DOM node selection into [line,char] selection.
|
// Turn DOM node selection into [line,char] selection.
|
||||||
// This method has to work when the DOM is not pristine,
|
// This method has to work when the DOM is not pristine,
|
||||||
// assuming the point is not in a dirty node.
|
// assuming the point is not in a dirty node.
|
||||||
if (point.node === document.body) {
|
if (point.node === targetBody) {
|
||||||
if (point.index === 0) {
|
if (point.index === 0) {
|
||||||
return [0, 0];
|
return [0, 0];
|
||||||
} else {
|
} else {
|
||||||
|
@ -1345,7 +1351,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
col = nodeText(n).length;
|
col = nodeText(n).length;
|
||||||
}
|
}
|
||||||
let parNode, prevSib;
|
let parNode, prevSib;
|
||||||
while ((parNode = n.parentNode) !== document.body) {
|
while ((parNode = n.parentNode) !== targetBody) {
|
||||||
if ((prevSib = n.previousSibling)) {
|
if ((prevSib = n.previousSibling)) {
|
||||||
n = prevSib;
|
n = prevSib;
|
||||||
col += nodeText(n).length;
|
col += nodeText(n).length;
|
||||||
|
@ -1398,7 +1404,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
insertDomLines(nodeToAddAfter, lineEntries.map((entry) => entry.domInfo));
|
insertDomLines(nodeToAddAfter, lineEntries.map((entry) => entry.domInfo));
|
||||||
|
|
||||||
for (const k of keysToDelete) {
|
for (const k of keysToDelete) {
|
||||||
const n = document.getElementById(k);
|
const n = targetDoc.getElementById(k);
|
||||||
n.parentNode.removeChild(n);
|
n.parentNode.removeChild(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2087,7 +2093,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const a = cleanNodeForIndex(i - 1);
|
const a = cleanNodeForIndex(i - 1);
|
||||||
const b = cleanNodeForIndex(i);
|
const b = cleanNodeForIndex(i);
|
||||||
if ((!a) || (!b)) return false; // violates precondition
|
if ((!a) || (!b)) return false; // violates precondition
|
||||||
if ((a === true) && (b === true)) return !document.body.firstChild;
|
if ((a === true) && (b === true)) return !targetBody.firstChild;
|
||||||
if ((a === true) && b.previousSibling) return false;
|
if ((a === true) && b.previousSibling) return false;
|
||||||
if ((b === true) && a.nextSibling) return false;
|
if ((b === true) && a.nextSibling) return false;
|
||||||
if ((a === true) || (b === true)) return true;
|
if ((a === true) || (b === true)) return true;
|
||||||
|
@ -2232,7 +2238,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const isNodeDirty = (n) => {
|
const isNodeDirty = (n) => {
|
||||||
if (n.parentNode !== document.body) return true;
|
if (n.parentNode !== targetBody) return true;
|
||||||
const data = getAssoc(n, 'dirtiness');
|
const data = getAssoc(n, 'dirtiness');
|
||||||
if (!data) return true;
|
if (!data) return true;
|
||||||
if (n.id !== data.nodeId) return true;
|
if (n.id !== data.nodeId) return true;
|
||||||
|
@ -2856,7 +2862,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
updateBrowserSelectionFromRep();
|
updateBrowserSelectionFromRep();
|
||||||
// get the current caret selection, can't use rep. here because that only gives
|
// get the current caret selection, can't use rep. here because that only gives
|
||||||
// us the start position not the current
|
// us the start position not the current
|
||||||
const myselection = document.getSelection();
|
const myselection = targetDoc.getSelection();
|
||||||
// get the carets selection offset in px IE 214
|
// get the carets selection offset in px IE 214
|
||||||
let caretOffsetTop = myselection.focusNode.parentNode.offsetTop ||
|
let caretOffsetTop = myselection.focusNode.parentNode.offsetTop ||
|
||||||
myselection.focusNode.offsetTop;
|
myselection.focusNode.offsetTop;
|
||||||
|
@ -2970,13 +2976,13 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// with background doesn't seem to show up...
|
// with background doesn't seem to show up...
|
||||||
if (isNodeText(p.node) && p.index === p.maxIndex) {
|
if (isNodeText(p.node) && p.index === p.maxIndex) {
|
||||||
let n = p.node;
|
let n = p.node;
|
||||||
while (!n.nextSibling && n !== document.body && n.parentNode !== document.body) {
|
while (!n.nextSibling && n !== targetBody && n.parentNode !== targetBody) {
|
||||||
n = n.parentNode;
|
n = n.parentNode;
|
||||||
}
|
}
|
||||||
if (n.nextSibling &&
|
if (n.nextSibling &&
|
||||||
!(typeof n.nextSibling.tagName === 'string' &&
|
!(typeof n.nextSibling.tagName === 'string' &&
|
||||||
n.nextSibling.tagName.toLowerCase() === 'br') &&
|
n.nextSibling.tagName.toLowerCase() === 'br') &&
|
||||||
n !== p.node && n !== document.body && n.parentNode !== document.body) {
|
n !== p.node && n !== targetBody && n.parentNode !== targetBody) {
|
||||||
// found a parent, go to next node and dive in
|
// found a parent, go to next node and dive in
|
||||||
p.node = n.nextSibling;
|
p.node = n.nextSibling;
|
||||||
p.maxIndex = nodeMaxIndex(p.node);
|
p.maxIndex = nodeMaxIndex(p.node);
|
||||||
|
@ -3078,7 +3084,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// each of which has node (a magicdom node), index, and maxIndex. If the node
|
// each of which has node (a magicdom node), index, and maxIndex. If the node
|
||||||
// is a text node, maxIndex is the length of the text; else maxIndex is 1.
|
// is a text node, maxIndex is the length of the text; else maxIndex is 1.
|
||||||
// index is between 0 and maxIndex, inclusive.
|
// index is between 0 and maxIndex, inclusive.
|
||||||
const browserSelection = window.getSelection();
|
const browserSelection = targetDoc.getSelection();
|
||||||
if (!browserSelection || browserSelection.type === 'None' ||
|
if (!browserSelection || browserSelection.type === 'None' ||
|
||||||
browserSelection.rangeCount === 0) {
|
browserSelection.rangeCount === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -3096,7 +3102,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
if (!isInBody(container)) {
|
if (!isInBody(container)) {
|
||||||
// command-click in Firefox selects whole document, HEAD and BODY!
|
// command-click in Firefox selects whole document, HEAD and BODY!
|
||||||
return {
|
return {
|
||||||
node: document.body,
|
node: targetBody,
|
||||||
index: 0,
|
index: 0,
|
||||||
maxIndex: 1,
|
maxIndex: 1,
|
||||||
};
|
};
|
||||||
|
@ -3191,7 +3197,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// If non-nullish, pasting on a link should be suppressed.
|
// If non-nullish, pasting on a link should be suppressed.
|
||||||
let suppressPasteOnLink = null;
|
let suppressPasteOnLink = null;
|
||||||
|
|
||||||
$(document.body).on('auxclick', (e) => {
|
$(targetBody).on('auxclick', (e) => {
|
||||||
if (e.originalEvent.button === 1 && (e.target.a || e.target.localName === 'a')) {
|
if (e.originalEvent.button === 1 && (e.target.a || e.target.localName === 'a')) {
|
||||||
// The user middle-clicked on a link. Usually users do this to open a link in a new tab, but
|
// The user middle-clicked on a link. Usually users do this to open a link in a new tab, but
|
||||||
// in X11 (Linux) this will instead paste the contents of the primary selection at the mouse
|
// in X11 (Linux) this will instead paste the contents of the primary selection at the mouse
|
||||||
|
@ -3213,7 +3219,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document.body).on('paste', (e) => {
|
$(targetBody).on('paste', (e) => {
|
||||||
if (suppressPasteOnLink != null && (e.target.a || e.target.localName === 'a')) {
|
if (suppressPasteOnLink != null && (e.target.a || e.target.localName === 'a')) {
|
||||||
scheduler.clearTimeout(suppressPasteOnLink);
|
scheduler.clearTimeout(suppressPasteOnLink);
|
||||||
suppressPasteOnLink = null;
|
suppressPasteOnLink = null;
|
||||||
|
@ -3233,7 +3239,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// We reference document here, this is because if we don't this will expose a bug
|
// We reference document here, this is because if we don't this will expose a bug
|
||||||
// in Google Chrome. This bug will cause the last character on the last line to
|
// in Google Chrome. This bug will cause the last character on the last line to
|
||||||
// not fire an event when dropped into..
|
// not fire an event when dropped into..
|
||||||
$(document).on('drop', (e) => {
|
$(targetBody).on('drop', (e) => {
|
||||||
if (e.target.a || e.target.localName === 'a') {
|
if (e.target.a || e.target.localName === 'a') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
@ -3251,7 +3257,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const lineAfterSelection = lastLineSelected.nextSibling;
|
const lineAfterSelection = lastLineSelected.nextSibling;
|
||||||
|
|
||||||
const neighbor = lineBeforeSelection || lineAfterSelection;
|
const neighbor = lineBeforeSelection || lineAfterSelection;
|
||||||
neighbor.appendChild(document.createElement('style'));
|
neighbor.appendChild(targetDoc.createElement('style'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call drop hook
|
// Call drop hook
|
||||||
|
@ -3263,10 +3269,10 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document.documentElement).on('compositionstart', () => {
|
$(targetDoc.documentElement).on('compositionstart', () => {
|
||||||
if (inInternationalComposition) return;
|
if (inInternationalComposition) return;
|
||||||
inInternationalComposition = new Promise((resolve) => {
|
inInternationalComposition = new Promise((resolve) => {
|
||||||
$(document.documentElement).one('compositionend', () => {
|
$(targetDoc.documentElement).one('compositionend', () => {
|
||||||
inInternationalComposition = null;
|
inInternationalComposition = null;
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
@ -3275,8 +3281,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const topLevel = (n) => {
|
const topLevel = (n) => {
|
||||||
if ((!n) || n === document.body) return null;
|
if ((!n) || n === targetBody) return null;
|
||||||
while (n.parentNode !== document.body) {
|
while (n.parentNode !== targetBody) {
|
||||||
n = n.parentNode;
|
n = n.parentNode;
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
|
@ -3436,10 +3442,10 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// but as it's non-text type the line-height/margins might not be present and it
|
// but as it's non-text type the line-height/margins might not be present and it
|
||||||
// could be that this breaks a theme that has a different default line height..
|
// could be that this breaks a theme that has a different default line height..
|
||||||
// So instead of using an integer here we get the value from the Editor CSS.
|
// So instead of using an integer here we get the value from the Editor CSS.
|
||||||
const innerdocbodyStyles = getComputedStyle(document.body);
|
const innerdocbodyStyles = getComputedStyle(targetBody);
|
||||||
const defaultLineHeight = parseInt(innerdocbodyStyles['line-height']);
|
const defaultLineHeight = parseInt(innerdocbodyStyles['line-height']);
|
||||||
|
|
||||||
for (const docLine of document.body.children) {
|
for (const docLine of targetBody.children) {
|
||||||
let h;
|
let h;
|
||||||
const nextDocLine = docLine.nextElementSibling;
|
const nextDocLine = docLine.nextElementSibling;
|
||||||
if (nextDocLine) {
|
if (nextDocLine) {
|
||||||
|
@ -3450,7 +3456,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// included on the first line. The default stylesheet doesn't add
|
// included on the first line. The default stylesheet doesn't add
|
||||||
// extra margins/padding, but plugins might.
|
// extra margins/padding, but plugins might.
|
||||||
h = nextDocLine.offsetTop - parseInt(
|
h = nextDocLine.offsetTop - parseInt(
|
||||||
window.getComputedStyle(document.body)
|
window.getComputedStyle(targetBody)
|
||||||
.getPropertyValue('padding-top').split('px')[0]);
|
.getPropertyValue('padding-top').split('px')[0]);
|
||||||
} else {
|
} else {
|
||||||
h = nextDocLine.offsetTop - docLine.offsetTop;
|
h = nextDocLine.offsetTop - docLine.offsetTop;
|
||||||
|
@ -3496,15 +3502,15 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
this.init = async () => {
|
this.init = async () => {
|
||||||
await $.ready;
|
await $.ready;
|
||||||
inCallStack('setup', () => {
|
inCallStack('setup', () => {
|
||||||
if (browser.firefox) $(document.body).addClass('mozilla');
|
if (browser.firefox) $(targetBody).addClass('mozilla');
|
||||||
if (browser.safari) $(document.body).addClass('safari');
|
if (browser.safari) $(targetBody).addClass('safari');
|
||||||
document.body.classList.toggle('authorColors', true);
|
targetBody.classList.toggle('authorColors', true);
|
||||||
document.body.classList.toggle('doesWrap', doesWrap);
|
targetBody.classList.toggle('doesWrap', doesWrap);
|
||||||
|
|
||||||
enforceEditability();
|
enforceEditability();
|
||||||
|
|
||||||
// set up dom and rep
|
// set up dom and rep
|
||||||
while (document.body.firstChild) document.body.removeChild(document.body.firstChild);
|
while (targetBody.firstChild) targetBody.removeChild(targetBody.firstChild);
|
||||||
const oneEntry = createDomLineEntry('');
|
const oneEntry = createDomLineEntry('');
|
||||||
doRepLineSplice(0, rep.lines.length(), [oneEntry]);
|
doRepLineSplice(0, rep.lines.length(), [oneEntry]);
|
||||||
insertDomLines(null, [oneEntry.domInfo]);
|
insertDomLines(null, [oneEntry.domInfo]);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// is represented by the browser
|
// is represented by the browser
|
||||||
exports.getPosition = () => {
|
exports.getPosition = () => {
|
||||||
const range = getSelectionRange();
|
const range = getSelectionRange();
|
||||||
|
console.log("Getting range", range)
|
||||||
if (!range || $(range.endContainer).closest('body')[0].id !== 'innerdocbody') return null;
|
if (!range || $(range.endContainer).closest('body')[0].id !== 'innerdocbody') return null;
|
||||||
// When there's a <br> or any element that has no height, we can't get the dimension of the
|
// 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. As we can't get the element height, we create a text node to get
|
// element where the caret is. As we can't get the element height, we create a text node to get
|
||||||
|
|
|
@ -24,9 +24,9 @@
|
||||||
const Cookies = require('./pad_utils').Cookies;
|
const Cookies = require('./pad_utils').Cookies;
|
||||||
const padcookie = require('./pad_cookie').padcookie;
|
const padcookie = require('./pad_cookie').padcookie;
|
||||||
const padutils = require('./pad_utils').padutils;
|
const padutils = require('./pad_utils').padutils;
|
||||||
|
const Ace2Editor = require('./ace').Ace2Editor;
|
||||||
|
|
||||||
const padeditor = (() => {
|
const padeditor = (() => {
|
||||||
let Ace2Editor = undefined;
|
|
||||||
let pad = undefined;
|
let pad = undefined;
|
||||||
let settings = undefined;
|
let settings = undefined;
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ const padeditor = (() => {
|
||||||
// this is accessed directly from other files
|
// this is accessed directly from other files
|
||||||
viewZoom: 100,
|
viewZoom: 100,
|
||||||
init: async (initialViewOptions, _pad) => {
|
init: async (initialViewOptions, _pad) => {
|
||||||
Ace2Editor = require('./ace').Ace2Editor;
|
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
settings = pad.settings;
|
settings = pad.settings;
|
||||||
self.ace = new Ace2Editor();
|
self.ace = new Ace2Editor();
|
||||||
|
|
|
@ -443,7 +443,7 @@ const inThirdPartyIframe = () => {
|
||||||
// This file is included from Node so that it can reuse randomString, but Node doesn't have a global
|
// This file is included from Node so that it can reuse randomString, but Node doesn't have a global
|
||||||
// window object.
|
// window object.
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
exports.Cookies = require('js-cookie/dist/js.cookie').withAttributes({
|
exports.Cookies = require('js-cookie').withAttributes({
|
||||||
// Use `SameSite=Lax`, unless Etherpad is embedded in an iframe from another site in which case
|
// Use `SameSite=Lax`, unless Etherpad is embedded in an iframe from another site in which case
|
||||||
// use `SameSite=None`. For iframes from another site, only `None` has a chance of working
|
// use `SameSite=None`. For iframes from another site, only `None` has a chance of working
|
||||||
// because the cookies are third-party (not same-site). Many browsers/users block third-party
|
// because the cookies are third-party (not same-site). Many browsers/users block third-party
|
||||||
|
|
|
@ -7,24 +7,13 @@ exports.baseURL = '';
|
||||||
|
|
||||||
exports.ensure = (cb) => !defs.loaded ? exports.update(cb) : cb();
|
exports.ensure = (cb) => !defs.loaded ? exports.update(cb) : cb();
|
||||||
|
|
||||||
exports.update = (cb) => {
|
exports.update = async (modules) => {
|
||||||
// It appears that this response (see #620) may interrupt the current thread
|
const data = await jQuery.getJSON(
|
||||||
// of execution on Firefox. This schedules the response in the run-loop,
|
`${exports.baseURL}pluginfw/plugin-definitions.json?v=${clientVars.randomVersionString}`);
|
||||||
// which appears to fix the issue.
|
defs.plugins = data.plugins;
|
||||||
const callback = () => setTimeout(cb, 0);
|
defs.parts = data.parts;
|
||||||
|
defs.hooks = pluginUtils.extractHooks(defs.parts, 'client_hooks', null, modules);
|
||||||
jQuery.getJSON(
|
defs.loaded = true;
|
||||||
`${exports.baseURL}pluginfw/plugin-definitions.json?v=${clientVars.randomVersionString}`
|
|
||||||
).done((data) => {
|
|
||||||
defs.plugins = data.plugins;
|
|
||||||
defs.parts = data.parts;
|
|
||||||
defs.hooks = pluginUtils.extractHooks(defs.parts, 'client_hooks');
|
|
||||||
defs.loaded = true;
|
|
||||||
callback();
|
|
||||||
}).fail((err) => {
|
|
||||||
console.error(`Failed to load plugin-definitions: ${err}`);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const adoptPluginsFromAncestorsOf = (frame) => {
|
const adoptPluginsFromAncestorsOf = (frame) => {
|
||||||
|
|
|
@ -15,7 +15,7 @@ function Scroll(outerWin) {
|
||||||
|
|
||||||
// DOM reference
|
// DOM reference
|
||||||
this.outerWin = outerWin;
|
this.outerWin = outerWin;
|
||||||
this.doc = this.outerWin.document;
|
this.doc = this.outerWin.contentDocument;
|
||||||
this.rootDocument = parent.parent.document;
|
this.rootDocument = parent.parent.document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
src/static/js/vendors/farbtastic.js
vendored
6
src/static/js/vendors/farbtastic.js
vendored
|
@ -172,7 +172,7 @@ $._farbtastic = function (container, options) {
|
||||||
angle2 = d2 * Math.PI * 2,
|
angle2 = d2 * Math.PI * 2,
|
||||||
// Endpoints
|
// Endpoints
|
||||||
x1 = Math.sin(angle1), y1 = -Math.cos(angle1);
|
x1 = Math.sin(angle1), y1 = -Math.cos(angle1);
|
||||||
x2 = Math.sin(angle2), y2 = -Math.cos(angle2),
|
let x2 = Math.sin(angle2), y2 = -Math.cos(angle2),
|
||||||
// Midpoint chosen so that the endpoints are tangent to the circle.
|
// Midpoint chosen so that the endpoints are tangent to the circle.
|
||||||
am = (angle1 + angle2) / 2,
|
am = (angle1 + angle2) / 2,
|
||||||
tan = 1 / Math.cos((angle2 - angle1) / 2),
|
tan = 1 / Math.cos((angle2 - angle1) / 2),
|
||||||
|
@ -329,8 +329,8 @@ $._farbtastic = function (container, options) {
|
||||||
|
|
||||||
// Update the overlay canvas.
|
// Update the overlay canvas.
|
||||||
fb.ctxOverlay.clearRect(-fb.mid, -fb.mid, sz, sz);
|
fb.ctxOverlay.clearRect(-fb.mid, -fb.mid, sz, sz);
|
||||||
for (i in circles) {
|
for (let i in circles) {
|
||||||
var c = circles[i];
|
const c = circles[i];
|
||||||
fb.ctxOverlay.lineWidth = c.lw;
|
fb.ctxOverlay.lineWidth = c.lw;
|
||||||
fb.ctxOverlay.strokeStyle = c.c;
|
fb.ctxOverlay.strokeStyle = c.c;
|
||||||
fb.ctxOverlay.beginPath();
|
fb.ctxOverlay.beginPath();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
window.clientVars = {
|
window.clientVars = {
|
||||||
// This is needed to fetch /pluginfw/plugin-definitions.json, which happens before the server
|
// This is needed to fetch /pluginfw/plugin-definitions.json, which happens before the server
|
||||||
|
@ -6,7 +7,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allow other frames to access this frame's modules.
|
// Allow other frames to access this frame's modules.
|
||||||
window.require.resolveTmp = require.resolve('ep_etherpad-lite/static/js/pad_cookie');
|
//window.require.resolveTmp = require.resolve('ep_etherpad-lite/static/js/pad_cookie');
|
||||||
|
|
||||||
const basePath = new URL('..', window.location.href).pathname;
|
const basePath = new URL('..', window.location.href).pathname;
|
||||||
window.$ = window.jQuery = require('../../src/static/js/rjquery').jQuery;
|
window.$ = window.jQuery = require('../../src/static/js/rjquery').jQuery;
|
||||||
|
|
2
var/js/.gitignore
vendored
2
var/js/.gitignore
vendored
|
@ -0,0 +1,2 @@
|
||||||
|
*.js
|
||||||
|
*.map
|
Loading…
Add table
Add a link
Reference in a new issue