Merge branch 'develop'
This commit is contained in:
commit
ad84c8a5a4
17 changed files with 240 additions and 247 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
||||||
|
# 1.8.11
|
||||||
|
|
||||||
|
### Notable fixes
|
||||||
|
|
||||||
|
* Fix server crash issue within PadMessageHandler due to SocketIO handling
|
||||||
|
* Fix editor issue with drop downs not being visible
|
||||||
|
* Ensure correct version is passed when loading front end resources
|
||||||
|
* Ensure underscore and jquery are available in original location for plugin comptability
|
||||||
|
|
||||||
|
### Notable enhancements
|
||||||
|
|
||||||
|
* Improved page load speeds
|
||||||
|
|
||||||
# 1.8.10
|
# 1.8.10
|
||||||
|
|
||||||
### Security Patches
|
### Security Patches
|
||||||
|
|
|
@ -1409,16 +1409,14 @@ const composePadChangesets = async (padId, startNum, endNum) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const _getRoomSockets = (padID) => {
|
const _getRoomSockets = (padID) => {
|
||||||
const roomSockets = [];
|
const ns = socketio.sockets; // Default namespace.
|
||||||
const room = socketio.sockets.adapter.rooms[padID];
|
const adapter = ns.adapter;
|
||||||
|
// We could call adapter.clients(), but that method is unnecessarily asynchronous. Replicate what
|
||||||
if (room) {
|
// it does here, but synchronously to avoid a race condition. This code will have to change when
|
||||||
for (const id of Object.keys(room.sockets)) {
|
// we update to socket.io v3.
|
||||||
roomSockets.push(socketio.sockets.sockets[id]);
|
const room = adapter.rooms[padID];
|
||||||
}
|
if (!room) return [];
|
||||||
}
|
return Object.keys(room.sockets).map((id) => ns.connected[id]).filter((s) => s);
|
||||||
|
|
||||||
return roomSockets;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1438,14 +1436,13 @@ exports.padUsers = async (padID) => {
|
||||||
await Promise.all(_getRoomSockets(padID).map(async (roomSocket) => {
|
await Promise.all(_getRoomSockets(padID).map(async (roomSocket) => {
|
||||||
const s = sessioninfos[roomSocket.id];
|
const s = sessioninfos[roomSocket.id];
|
||||||
if (s) {
|
if (s) {
|
||||||
return authorManager.getAuthor(s.author).then((author) => {
|
const author = await authorManager.getAuthor(s.author);
|
||||||
// Fixes: https://github.com/ether/etherpad-lite/issues/4120
|
// Fixes: https://github.com/ether/etherpad-lite/issues/4120
|
||||||
// On restart author might not be populated?
|
// On restart author might not be populated?
|
||||||
if (author) {
|
if (author) {
|
||||||
author.id = s.author;
|
author.id = s.author;
|
||||||
padUsers.push(author);
|
padUsers.push(author);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ const getTar = async () => {
|
||||||
exports.expressCreateServer = async (hookName, args) => {
|
exports.expressCreateServer = async (hookName, args) => {
|
||||||
// Cache both minified and static.
|
// Cache both minified and static.
|
||||||
const assetCache = new CachingMiddleware();
|
const assetCache = new CachingMiddleware();
|
||||||
args.app.all(/\/javascripts\/(.*)/, assetCache.handle);
|
args.app.all(/\/javascripts\/(.*)/, assetCache.handle.bind(assetCache));
|
||||||
|
|
||||||
// Minify will serve static files compressed (minify enabled). It also has
|
// Minify will serve static files compressed (minify enabled). It also has
|
||||||
// file-specific hacks for ace/require-kernel/etc.
|
// file-specific hacks for ace/require-kernel/etc.
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const async = require('async');
|
|
||||||
const Buffer = require('buffer').Buffer;
|
const Buffer = require('buffer').Buffer;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const fsp = fs.promises;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
const settings = require('./Settings');
|
const settings = require('./Settings');
|
||||||
const existsSync = require('./path_exists');
|
const existsSync = require('./path_exists');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The crypto module can be absent on reduced node installations.
|
* The crypto module can be absent on reduced node installations.
|
||||||
|
@ -77,146 +78,126 @@ if (_crypto) {
|
||||||
should replace this.
|
should replace this.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function CachingMiddleware() {
|
module.exports = class CachingMiddleware {
|
||||||
}
|
handle(req, res, next) {
|
||||||
CachingMiddleware.prototype = new function () {
|
this._handle(req, res, next).catch((err) => next(err || new Error(err)));
|
||||||
const handle = (req, res, next) => {
|
}
|
||||||
|
|
||||||
|
async _handle(req, res, next) {
|
||||||
if (!(req.method === 'GET' || req.method === 'HEAD') || !CACHE_DIR) {
|
if (!(req.method === 'GET' || req.method === 'HEAD') || !CACHE_DIR) {
|
||||||
return next(undefined, req, res);
|
return next(undefined, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const old_req = {};
|
const oldReq = {};
|
||||||
const old_res = {};
|
const oldRes = {};
|
||||||
|
|
||||||
const supportsGzip =
|
const supportsGzip =
|
||||||
(req.get('Accept-Encoding') || '').indexOf('gzip') !== -1;
|
(req.get('Accept-Encoding') || '').indexOf('gzip') !== -1;
|
||||||
|
|
||||||
const path = require('url').parse(req.url).path;
|
const url = new URL(req.url, 'http://localhost');
|
||||||
const cacheKey = generateCacheKey(path);
|
const cacheKey = generateCacheKey(url.pathname + url.search);
|
||||||
|
|
||||||
fs.stat(`${CACHE_DIR}minified_${cacheKey}`, (error, stats) => {
|
const stats = await fsp.stat(`${CACHE_DIR}minified_${cacheKey}`).catch(() => {});
|
||||||
const modifiedSince = (req.headers['if-modified-since'] &&
|
const modifiedSince =
|
||||||
new Date(req.headers['if-modified-since']));
|
req.headers['if-modified-since'] && new Date(req.headers['if-modified-since']);
|
||||||
const lastModifiedCache = !error && stats.mtime;
|
if (stats != null && stats.mtime && responseCache[cacheKey]) {
|
||||||
if (lastModifiedCache && responseCache[cacheKey]) {
|
req.headers['if-modified-since'] = stats.mtime.toUTCString();
|
||||||
req.headers['if-modified-since'] = lastModifiedCache.toUTCString();
|
} else {
|
||||||
|
delete req.headers['if-modified-since'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always issue get to downstream.
|
||||||
|
oldReq.method = req.method;
|
||||||
|
req.method = 'GET';
|
||||||
|
|
||||||
|
// This handles read/write synchronization as well as its predecessor,
|
||||||
|
// which is to say, not at all.
|
||||||
|
// TODO: Implement locking on write or ditch caching of gzip and use
|
||||||
|
// existing middlewares.
|
||||||
|
const respond = () => {
|
||||||
|
req.method = oldReq.method || req.method;
|
||||||
|
res.write = oldRes.write || res.write;
|
||||||
|
res.end = oldRes.end || res.end;
|
||||||
|
|
||||||
|
const headers = {};
|
||||||
|
Object.assign(headers, (responseCache[cacheKey].headers || {}));
|
||||||
|
const statusCode = responseCache[cacheKey].statusCode;
|
||||||
|
|
||||||
|
let pathStr = `${CACHE_DIR}minified_${cacheKey}`;
|
||||||
|
if (supportsGzip && /application\/javascript/.test(headers['content-type'])) {
|
||||||
|
pathStr += '.gz';
|
||||||
|
headers['content-encoding'] = 'gzip';
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastModified = headers['last-modified'] && new Date(headers['last-modified']);
|
||||||
|
|
||||||
|
if (statusCode === 200 && lastModified <= modifiedSince) {
|
||||||
|
res.writeHead(304, headers);
|
||||||
|
res.end();
|
||||||
|
} else if (req.method === 'GET') {
|
||||||
|
const readStream = fs.createReadStream(pathStr);
|
||||||
|
res.writeHead(statusCode, headers);
|
||||||
|
readStream.pipe(res);
|
||||||
} else {
|
} else {
|
||||||
delete req.headers['if-modified-since'];
|
res.writeHead(statusCode, headers);
|
||||||
|
res.end();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Always issue get to downstream.
|
const expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {}).expires);
|
||||||
old_req.method = req.method;
|
if (expirationDate > new Date()) {
|
||||||
req.method = 'GET';
|
// Our cached version is still valid.
|
||||||
|
return respond();
|
||||||
|
}
|
||||||
|
|
||||||
const expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {}).expires);
|
const _headers = {};
|
||||||
if (expirationDate > new Date()) {
|
oldRes.setHeader = res.setHeader;
|
||||||
// Our cached version is still valid.
|
res.setHeader = (key, value) => {
|
||||||
return respond();
|
// Don't set cookies, see issue #707
|
||||||
|
if (key.toLowerCase() === 'set-cookie') return;
|
||||||
|
|
||||||
|
_headers[key.toLowerCase()] = value;
|
||||||
|
oldRes.setHeader.call(res, key, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
oldRes.writeHead = res.writeHead;
|
||||||
|
res.writeHead = (status, headers) => {
|
||||||
|
res.writeHead = oldRes.writeHead;
|
||||||
|
if (status === 200) {
|
||||||
|
// Update cache
|
||||||
|
let buffer = '';
|
||||||
|
|
||||||
|
Object.keys(headers || {}).forEach((key) => {
|
||||||
|
res.setHeader(key, headers[key]);
|
||||||
|
});
|
||||||
|
headers = _headers;
|
||||||
|
|
||||||
|
oldRes.write = res.write;
|
||||||
|
oldRes.end = res.end;
|
||||||
|
res.write = (data, encoding) => {
|
||||||
|
buffer += data.toString(encoding);
|
||||||
|
};
|
||||||
|
res.end = async (data, encoding) => {
|
||||||
|
await Promise.all([
|
||||||
|
fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}`, buffer).catch(() => {}),
|
||||||
|
util.promisify(zlib.gzip)(buffer)
|
||||||
|
.then((content) => fsp.writeFile(`${CACHE_DIR}minified_${cacheKey}.gz`, content))
|
||||||
|
.catch(() => {}),
|
||||||
|
]);
|
||||||
|
responseCache[cacheKey] = {statusCode: status, headers};
|
||||||
|
respond();
|
||||||
|
};
|
||||||
|
} else if (status === 304) {
|
||||||
|
// Nothing new changed from the cached version.
|
||||||
|
oldRes.write = res.write;
|
||||||
|
oldRes.end = res.end;
|
||||||
|
res.write = (data, encoding) => {};
|
||||||
|
res.end = (data, encoding) => { respond(); };
|
||||||
|
} else {
|
||||||
|
res.writeHead(status, headers);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const _headers = {};
|
next(undefined, req, res);
|
||||||
old_res.setHeader = res.setHeader;
|
}
|
||||||
res.setHeader = (key, value) => {
|
};
|
||||||
// Don't set cookies, see issue #707
|
|
||||||
if (key.toLowerCase() === 'set-cookie') return;
|
|
||||||
|
|
||||||
_headers[key.toLowerCase()] = value;
|
|
||||||
old_res.setHeader.call(res, key, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
old_res.writeHead = res.writeHead;
|
|
||||||
res.writeHead = function (status, headers) {
|
|
||||||
res.writeHead = old_res.writeHead;
|
|
||||||
if (status === 200) {
|
|
||||||
// Update cache
|
|
||||||
let buffer = '';
|
|
||||||
|
|
||||||
Object.keys(headers || {}).forEach((key) => {
|
|
||||||
res.setHeader(key, headers[key]);
|
|
||||||
});
|
|
||||||
headers = _headers;
|
|
||||||
|
|
||||||
old_res.write = res.write;
|
|
||||||
old_res.end = res.end;
|
|
||||||
res.write = function (data, encoding) {
|
|
||||||
buffer += data.toString(encoding);
|
|
||||||
};
|
|
||||||
res.end = function (data, encoding) {
|
|
||||||
async.parallel([
|
|
||||||
function (callback) {
|
|
||||||
const path = `${CACHE_DIR}minified_${cacheKey}`;
|
|
||||||
fs.writeFile(path, buffer, (error, stats) => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function (callback) {
|
|
||||||
const path = `${CACHE_DIR}minified_${cacheKey}.gz`;
|
|
||||||
zlib.gzip(buffer, (error, content) => {
|
|
||||||
if (error) {
|
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
fs.writeFile(path, content, (error, stats) => {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
], () => {
|
|
||||||
responseCache[cacheKey] = {statusCode: status, headers};
|
|
||||||
respond();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
} else if (status === 304) {
|
|
||||||
// Nothing new changed from the cached version.
|
|
||||||
old_res.write = res.write;
|
|
||||||
old_res.end = res.end;
|
|
||||||
res.write = function (data, encoding) {};
|
|
||||||
res.end = function (data, encoding) { respond(); };
|
|
||||||
} else {
|
|
||||||
res.writeHead(status, headers);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
next(undefined, req, res);
|
|
||||||
|
|
||||||
// This handles read/write synchronization as well as its predecessor,
|
|
||||||
// which is to say, not at all.
|
|
||||||
// TODO: Implement locking on write or ditch caching of gzip and use
|
|
||||||
// existing middlewares.
|
|
||||||
function respond() {
|
|
||||||
req.method = old_req.method || req.method;
|
|
||||||
res.write = old_res.write || res.write;
|
|
||||||
res.end = old_res.end || res.end;
|
|
||||||
|
|
||||||
const headers = {};
|
|
||||||
Object.assign(headers, (responseCache[cacheKey].headers || {}));
|
|
||||||
const statusCode = responseCache[cacheKey].statusCode;
|
|
||||||
|
|
||||||
let pathStr = `${CACHE_DIR}minified_${cacheKey}`;
|
|
||||||
if (supportsGzip && /application\/javascript/.test(headers['content-type'])) {
|
|
||||||
pathStr += '.gz';
|
|
||||||
headers['content-encoding'] = 'gzip';
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastModified = (headers['last-modified'] &&
|
|
||||||
new Date(headers['last-modified']));
|
|
||||||
|
|
||||||
if (statusCode === 200 && lastModified <= modifiedSince) {
|
|
||||||
res.writeHead(304, headers);
|
|
||||||
res.end();
|
|
||||||
} else if (req.method === 'GET') {
|
|
||||||
const readStream = fs.createReadStream(pathStr);
|
|
||||||
res.writeHead(statusCode, headers);
|
|
||||||
readStream.pipe(res);
|
|
||||||
} else {
|
|
||||||
res.writeHead(statusCode, headers);
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.handle = handle;
|
|
||||||
}();
|
|
||||||
|
|
||||||
module.exports = CachingMiddleware;
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
"pad.js": [
|
"pad.js": [
|
||||||
"pad.js"
|
"pad.js"
|
||||||
, "pad_utils.js"
|
, "pad_utils.js"
|
||||||
|
, "$js-cookie/src/js.cookie.js"
|
||||||
|
, "security.js"
|
||||||
|
, "$security.js"
|
||||||
, "vendors/browser.js"
|
, "vendors/browser.js"
|
||||||
, "pad_cookie.js"
|
, "pad_cookie.js"
|
||||||
, "pad_editor.js"
|
, "pad_editor.js"
|
||||||
|
@ -22,12 +25,14 @@
|
||||||
, "vendors/farbtastic.js"
|
, "vendors/farbtastic.js"
|
||||||
, "skin_variants.js"
|
, "skin_variants.js"
|
||||||
, "socketio.js"
|
, "socketio.js"
|
||||||
|
, "colorutils.js"
|
||||||
]
|
]
|
||||||
, "timeslider.js": [
|
, "timeslider.js": [
|
||||||
"timeslider.js"
|
"timeslider.js"
|
||||||
, "colorutils.js"
|
, "colorutils.js"
|
||||||
, "draggable.js"
|
, "draggable.js"
|
||||||
, "pad_utils.js"
|
, "pad_utils.js"
|
||||||
|
, "$js-cookie/src/js.cookie.js"
|
||||||
, "vendors/browser.js"
|
, "vendors/browser.js"
|
||||||
, "pad_cookie.js"
|
, "pad_cookie.js"
|
||||||
, "pad_editor.js"
|
, "pad_editor.js"
|
||||||
|
@ -46,6 +51,8 @@
|
||||||
, "broadcast_slider.js"
|
, "broadcast_slider.js"
|
||||||
, "broadcast_revisions.js"
|
, "broadcast_revisions.js"
|
||||||
, "socketio.js"
|
, "socketio.js"
|
||||||
|
, "AttributeManager.js"
|
||||||
|
, "ChangesetUtils.js"
|
||||||
]
|
]
|
||||||
, "ace2_inner.js": [
|
, "ace2_inner.js": [
|
||||||
"ace2_inner.js"
|
"ace2_inner.js"
|
||||||
|
@ -65,6 +72,10 @@
|
||||||
, "AttributeManager.js"
|
, "AttributeManager.js"
|
||||||
, "scroll.js"
|
, "scroll.js"
|
||||||
, "caretPosition.js"
|
, "caretPosition.js"
|
||||||
|
, "pad_utils.js"
|
||||||
|
, "$js-cookie/src/js.cookie.js"
|
||||||
|
, "security.js"
|
||||||
|
, "$security.js"
|
||||||
]
|
]
|
||||||
, "ace2_common.js": [
|
, "ace2_common.js": [
|
||||||
"ace2_common.js"
|
"ace2_common.js"
|
||||||
|
@ -72,7 +83,7 @@
|
||||||
, "vendors/jquery.js"
|
, "vendors/jquery.js"
|
||||||
, "rjquery.js"
|
, "rjquery.js"
|
||||||
, "$async.js"
|
, "$async.js"
|
||||||
, "vendors/underscore.js"
|
, "underscore.js"
|
||||||
, "$underscore.js"
|
, "$underscore.js"
|
||||||
, "$underscore/underscore.js"
|
, "$underscore/underscore.js"
|
||||||
, "security.js"
|
, "security.js"
|
||||||
|
@ -82,5 +93,4 @@
|
||||||
, "pluginfw/shared.js"
|
, "pluginfw/shared.js"
|
||||||
, "pluginfw/hooks.js"
|
, "pluginfw/hooks.js"
|
||||||
]
|
]
|
||||||
, "jquery.js": ["jquery.js"]
|
|
||||||
}
|
}
|
||||||
|
|
2
src/package-lock.json
generated
2
src/package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ep_etherpad-lite",
|
"name": "ep_etherpad-lite",
|
||||||
"version": "1.8.10",
|
"version": "1.8.11",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -237,6 +237,6 @@
|
||||||
"test": "mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
|
"test": "mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
|
||||||
"test-container": "mocha --timeout 5000 tests/container/specs/api"
|
"test-container": "mocha --timeout 5000 tests/container/specs/api"
|
||||||
},
|
},
|
||||||
"version": "1.8.10",
|
"version": "1.8.11",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const ChangesetUtils = require('./ChangesetUtils');
|
const ChangesetUtils = require('./ChangesetUtils');
|
||||||
const _ = require('./vendors/underscore');
|
const _ = require('./underscore');
|
||||||
|
|
||||||
const lineMarkerAttribute = 'lmkr';
|
const lineMarkerAttribute = 'lmkr';
|
||||||
|
|
||||||
|
|
|
@ -195,6 +195,9 @@ const Ace2Editor = function () {
|
||||||
|
|
||||||
pushStyleTagsFor(iframeHTML, includedCSS);
|
pushStyleTagsFor(iframeHTML, includedCSS);
|
||||||
iframeHTML.push(`<script type="text/javascript" src="../static/js/require-kernel.js?v=${clientVars.randomVersionString}"></script>`);
|
iframeHTML.push(`<script type="text/javascript" src="../static/js/require-kernel.js?v=${clientVars.randomVersionString}"></script>`);
|
||||||
|
// fill the cache
|
||||||
|
iframeHTML.push(`<script type="text/javascript" src="../javascripts/lib/ep_etherpad-lite/static/js/ace2_inner.js?callback=require.define&v=${clientVars.randomVersionString}"></script>`);
|
||||||
|
iframeHTML.push(`<script type="text/javascript" src="../javascripts/lib/ep_etherpad-lite/static/js/ace2_common.js?callback=require.define&v=${clientVars.randomVersionString}"></script>`);
|
||||||
|
|
||||||
iframeHTML.push(scriptTag(
|
iframeHTML.push(scriptTag(
|
||||||
`\n\
|
`\n\
|
||||||
|
@ -202,11 +205,12 @@ require.setRootURI("../javascripts/src");\n\
|
||||||
require.setLibraryURI("../javascripts/lib");\n\
|
require.setLibraryURI("../javascripts/lib");\n\
|
||||||
require.setGlobalKeyPath("require");\n\
|
require.setGlobalKeyPath("require");\n\
|
||||||
\n\
|
\n\
|
||||||
|
// intentially moved before requiring client_plugins to save a 307
|
||||||
|
var Ace2Inner = require("ep_etherpad-lite/static/js/ace2_inner");\n\
|
||||||
var plugins = require("ep_etherpad-lite/static/js/pluginfw/client_plugins");\n\
|
var plugins = require("ep_etherpad-lite/static/js/pluginfw/client_plugins");\n\
|
||||||
plugins.adoptPluginsFromAncestorsOf(window);\n\
|
plugins.adoptPluginsFromAncestorsOf(window);\n\
|
||||||
\n\
|
\n\
|
||||||
$ = jQuery = require("ep_etherpad-lite/static/js/rjquery").jQuery; // Expose jQuery #HACK\n\
|
$ = jQuery = require("ep_etherpad-lite/static/js/rjquery").jQuery; // Expose jQuery #HACK\n\
|
||||||
var Ace2Inner = require("ep_etherpad-lite/static/js/ace2_inner");\n\
|
|
||||||
\n\
|
\n\
|
||||||
plugins.ensure(function () {\n\
|
plugins.ensure(function () {\n\
|
||||||
Ace2Inner.init();\n\
|
Ace2Inner.init();\n\
|
||||||
|
|
|
@ -28,7 +28,7 @@ const AttribPool = require('./AttributePool');
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const linestylefilter = require('./linestylefilter').linestylefilter;
|
const linestylefilter = require('./linestylefilter').linestylefilter;
|
||||||
const colorutils = require('./colorutils').colorutils;
|
const colorutils = require('./colorutils').colorutils;
|
||||||
const _ = require('./vendors/underscore');
|
const _ = require('./underscore');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
|
|
||||||
// These parameters were global, now they are injected. A reference to the
|
// These parameters were global, now they are injected. A reference to the
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
// These parameters were global, now they are injected. A reference to the
|
// These parameters were global, now they are injected. A reference to the
|
||||||
// Timeslider controller would probably be more appropriate.
|
// Timeslider controller would probably be more appropriate.
|
||||||
const _ = require('./vendors/underscore');
|
const _ = require('./underscore');
|
||||||
const padmodals = require('./pad_modals').padmodals;
|
const padmodals = require('./pad_modals').padmodals;
|
||||||
const colorutils = require('./colorutils').colorutils;
|
const colorutils = require('./colorutils').colorutils;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
const Security = require('./security');
|
const Security = require('./security');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
const _ = require('./vendors/underscore');
|
const _ = require('./underscore');
|
||||||
const lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
|
const lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Ace2Common = require('./ace2_common');
|
const Ace2Common = require('./ace2_common');
|
||||||
const _ = require('./vendors/underscore');
|
const _ = require('./underscore');
|
||||||
|
|
||||||
const noop = Ace2Common.noop;
|
const noop = Ace2Common.noop;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const _ = require('./vendors/underscore');
|
const _ = require('./underscore');
|
||||||
|
|
||||||
const undoModule = (() => {
|
const undoModule = (() => {
|
||||||
const stack = (() => {
|
const stack = (() => {
|
||||||
|
|
21
src/static/js/vendors/nice-select.js
vendored
21
src/static/js/vendors/nice-select.js
vendored
|
@ -63,7 +63,7 @@
|
||||||
.addClass($select.attr('class') || '')
|
.addClass($select.attr('class') || '')
|
||||||
.addClass($select.attr('disabled') ? 'disabled' : '')
|
.addClass($select.attr('disabled') ? 'disabled' : '')
|
||||||
.attr('tabindex', $select.attr('disabled') ? null : '0')
|
.attr('tabindex', $select.attr('disabled') ? null : '0')
|
||||||
.html('<span class="current"></span><ul class="list"></ul>')
|
.html('<span class="current"></span><ul class="list thin-scrollbar"></ul>')
|
||||||
);
|
);
|
||||||
|
|
||||||
var $dropdown = $select.next();
|
var $dropdown = $select.next();
|
||||||
|
@ -97,12 +97,31 @@
|
||||||
var $dropdown = $(this);
|
var $dropdown = $(this);
|
||||||
|
|
||||||
$('.nice-select').not($dropdown).removeClass('open');
|
$('.nice-select').not($dropdown).removeClass('open');
|
||||||
|
|
||||||
$dropdown.toggleClass('open');
|
$dropdown.toggleClass('open');
|
||||||
|
|
||||||
if ($dropdown.hasClass('open')) {
|
if ($dropdown.hasClass('open')) {
|
||||||
$dropdown.find('.option');
|
$dropdown.find('.option');
|
||||||
$dropdown.find('.focus').removeClass('focus');
|
$dropdown.find('.focus').removeClass('focus');
|
||||||
$dropdown.find('.selected').addClass('focus');
|
$dropdown.find('.selected').addClass('focus');
|
||||||
|
if ($dropdown.closest('.toolbar').length > 0) {
|
||||||
|
$dropdown.find('.list').css('left', $dropdown.offset().left);
|
||||||
|
$dropdown.find('.list').css('top', $dropdown.offset().top + $dropdown.outerHeight());
|
||||||
|
$dropdown.find('.list').css('min-width', $dropdown.outerWidth() + 'px');
|
||||||
|
}
|
||||||
|
|
||||||
|
$listHeight = $dropdown.find('.list').outerHeight();
|
||||||
|
$top = $dropdown.parent().offset().top;
|
||||||
|
$bottom = $('body').height() - $top;
|
||||||
|
$maxListHeight = $bottom - $dropdown.outerHeight() - 20;
|
||||||
|
if ($maxListHeight < 200) {
|
||||||
|
$dropdown.addClass('reverse');
|
||||||
|
$maxListHeight = 250;
|
||||||
|
} else {
|
||||||
|
$dropdown.removeClass('reverse')
|
||||||
|
}
|
||||||
|
$dropdown.find('.list').css('max-height', $maxListHeight + 'px');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$dropdown.focus();
|
$dropdown.focus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ let agent;
|
||||||
* @param {URI} resource resource URI
|
* @param {URI} resource resource URI
|
||||||
* @returns {boolean} if it is plaintext
|
* @returns {boolean} if it is plaintext
|
||||||
*/
|
*/
|
||||||
function isPlaintextResponse(fileContent, resource) {
|
const isPlaintextResponse = (fileContent, resource) => {
|
||||||
// callback=require.define&v=1234
|
// callback=require.define&v=1234
|
||||||
const query = (new URL(resource, 'http://localhost')).search.slice(1);
|
const query = (new URL(resource, 'http://localhost')).search.slice(1);
|
||||||
// require.define
|
// require.define
|
||||||
|
@ -30,18 +30,16 @@ function isPlaintextResponse(fileContent, resource) {
|
||||||
|
|
||||||
// returns true if the first letters in fileContent equal the content of `jsonp`
|
// returns true if the first letters in fileContent equal the content of `jsonp`
|
||||||
return fileContent.substring(0, jsonp.length) === jsonp;
|
return fileContent.substring(0, jsonp.length) === jsonp;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A hack to disable `superagent`'s auto unzip functionality
|
* A hack to disable `superagent`'s auto unzip functionality
|
||||||
*
|
*
|
||||||
* @param {Request} request
|
* @param {Request} request
|
||||||
*/
|
*/
|
||||||
function disableAutoDeflate(request) {
|
const disableAutoDeflate = (request) => {
|
||||||
request._shouldUnzip = function () {
|
request._shouldUnzip = () => false;
|
||||||
return false;
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
const backups = {};
|
const backups = {};
|
||||||
|
@ -55,94 +53,65 @@ describe(__filename, function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
agent = await common.init();
|
agent = await common.init();
|
||||||
});
|
|
||||||
beforeEach(async function () {
|
|
||||||
backups.settings = {};
|
backups.settings = {};
|
||||||
backups.settings.minify = settings.minify;
|
backups.settings.minify = settings.minify;
|
||||||
});
|
});
|
||||||
afterEach(async function () {
|
after(async function () {
|
||||||
Object.assign(settings, backups.settings);
|
Object.assign(settings, backups.settings);
|
||||||
});
|
});
|
||||||
|
|
||||||
context('when minify is false', function () {
|
for (const minify of [false, true]) {
|
||||||
before(async function () {
|
context(`when minify is ${minify}`, function () {
|
||||||
settings.minify = false;
|
before(async function () {
|
||||||
});
|
settings.minify = minify;
|
||||||
it('gets packages uncompressed without Accept-Encoding gzip', async function () {
|
});
|
||||||
await Promise.all(packages.map(async (resource) => agent.get(resource)
|
|
||||||
.set('Accept-Encoding', fantasyEncoding)
|
|
||||||
.use(disableAutoDeflate)
|
|
||||||
.then((res) => {
|
|
||||||
assert.match(res.header['content-type'], /application\/javascript/);
|
|
||||||
assert.equal(res.header['content-encoding'], undefined);
|
|
||||||
assert.equal(isPlaintextResponse(res.text, resource), true);
|
|
||||||
return;
|
|
||||||
})));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('gets packages compressed with Accept-Encoding gzip', async function () {
|
describe('gets packages uncompressed without Accept-Encoding gzip', function () {
|
||||||
await Promise.all(packages.map(async (resource) => agent.get(resource)
|
for (const resource of packages) {
|
||||||
.set('Accept-Encoding', 'gzip')
|
it(resource, async function () {
|
||||||
.use(disableAutoDeflate)
|
await agent.get(resource)
|
||||||
.then((res) => {
|
.set('Accept-Encoding', fantasyEncoding)
|
||||||
assert.match(res.header['content-type'], /application\/javascript/);
|
.use(disableAutoDeflate)
|
||||||
assert.equal(res.header['content-encoding'], 'gzip');
|
.expect(200)
|
||||||
assert.equal(isPlaintextResponse(res.text, resource), false);
|
.expect('Content-Type', /application\/javascript/)
|
||||||
return;
|
.expect((res) => {
|
||||||
})));
|
assert.equal(res.header['content-encoding'], undefined);
|
||||||
});
|
assert(isPlaintextResponse(res.text, resource));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('does not cache content-encoding headers', async function () {
|
describe('gets packages compressed with Accept-Encoding gzip', function () {
|
||||||
await agent.get(packages[0])
|
for (const resource of packages) {
|
||||||
.set('Accept-Encoding', fantasyEncoding)
|
it(resource, async function () {
|
||||||
.then((res) => assert.equal(res.header['content-encoding'], undefined));
|
await agent.get(resource)
|
||||||
await agent.get(packages[0])
|
.set('Accept-Encoding', 'gzip')
|
||||||
.set('Accept-Encoding', 'gzip')
|
.use(disableAutoDeflate)
|
||||||
.then((res) => assert.equal(res.header['content-encoding'], 'gzip'));
|
.expect(200)
|
||||||
await agent.get(packages[0])
|
.expect('Content-Type', /application\/javascript/)
|
||||||
.set('Accept-Encoding', fantasyEncoding)
|
.expect('Content-Encoding', 'gzip')
|
||||||
.then((res) => assert.equal(res.header['content-encoding'], undefined));
|
.expect((res) => {
|
||||||
});
|
assert(!isPlaintextResponse(res.text, resource));
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
context('when minify is true', function () {
|
it('does not cache content-encoding headers', async function () {
|
||||||
before(async function () {
|
await agent.get(packages[0])
|
||||||
settings.minify = true;
|
.set('Accept-Encoding', fantasyEncoding)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => assert.equal(res.header['content-encoding'], undefined));
|
||||||
|
await agent.get(packages[0])
|
||||||
|
.set('Accept-Encoding', 'gzip')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Encoding', 'gzip');
|
||||||
|
await agent.get(packages[0])
|
||||||
|
.set('Accept-Encoding', fantasyEncoding)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => assert.equal(res.header['content-encoding'], undefined));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('gets packages uncompressed without Accept-Encoding gzip', async function () {
|
}
|
||||||
await Promise.all(packages.map(async (resource) => agent.get(resource)
|
|
||||||
.set('Accept-Encoding', fantasyEncoding)
|
|
||||||
.use(disableAutoDeflate)
|
|
||||||
.then((res) => {
|
|
||||||
assert.match(res.header['content-type'], /application\/javascript/);
|
|
||||||
assert.equal(res.header['content-encoding'], undefined);
|
|
||||||
assert.equal(isPlaintextResponse(res.text, resource), true);
|
|
||||||
return;
|
|
||||||
})));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('gets packages compressed with Accept-Encoding gzip', async function () {
|
|
||||||
await Promise.all(packages.map(async (resource) => agent.get(resource)
|
|
||||||
.set('Accept-Encoding', 'gzip')
|
|
||||||
.use(disableAutoDeflate)
|
|
||||||
.then((res) => {
|
|
||||||
assert.match(res.header['content-type'], /application\/javascript/);
|
|
||||||
assert.equal(res.header['content-encoding'], 'gzip');
|
|
||||||
assert.equal(isPlaintextResponse(res.text, resource), false);
|
|
||||||
return;
|
|
||||||
})));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not cache content-encoding headers', async function () {
|
|
||||||
await agent.get(packages[0])
|
|
||||||
.set('Accept-Encoding', fantasyEncoding)
|
|
||||||
.then((res) => assert.equal(res.header['content-encoding'], undefined));
|
|
||||||
await agent.get(packages[0])
|
|
||||||
.set('Accept-Encoding', 'gzip')
|
|
||||||
.then((res) => assert.equal(res.header['content-encoding'], 'gzip'));
|
|
||||||
await agent.get(packages[0])
|
|
||||||
.set('Accept-Encoding', fantasyEncoding)
|
|
||||||
.then((res) => assert.equal(res.header['content-encoding'], undefined));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue