mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 14:13:34 +01:00
Moved to ts (#6593)
* Moved to ts * Fixed type check * Removed js suffixes * Migrated to ts * Fixed ts. * Fixed type check * Installed missing d ts
This commit is contained in:
parent
5ee2c4e7f8
commit
7e3ad03e2f
81 changed files with 961 additions and 830 deletions
|
@ -291,6 +291,9 @@ importers:
|
||||||
'@types/jquery':
|
'@types/jquery':
|
||||||
specifier: ^3.5.30
|
specifier: ^3.5.30
|
||||||
version: 3.5.30
|
version: 3.5.30
|
||||||
|
'@types/js-cookie':
|
||||||
|
specifier: ^3.0.6
|
||||||
|
version: 3.0.6
|
||||||
'@types/jsdom':
|
'@types/jsdom':
|
||||||
specifier: ^21.1.7
|
specifier: ^21.1.7
|
||||||
version: 21.1.7
|
version: 21.1.7
|
||||||
|
@ -1501,6 +1504,9 @@ packages:
|
||||||
'@types/jquery@3.5.30':
|
'@types/jquery@3.5.30':
|
||||||
resolution: {integrity: sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==}
|
resolution: {integrity: sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==}
|
||||||
|
|
||||||
|
'@types/js-cookie@3.0.6':
|
||||||
|
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
|
||||||
|
|
||||||
'@types/jsdom@21.1.7':
|
'@types/jsdom@21.1.7':
|
||||||
resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==}
|
resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==}
|
||||||
|
|
||||||
|
@ -5685,6 +5691,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/sizzle': 2.3.8
|
'@types/sizzle': 2.3.8
|
||||||
|
|
||||||
|
'@types/js-cookie@3.0.6': {}
|
||||||
|
|
||||||
'@types/jsdom@21.1.7':
|
'@types/jsdom@21.1.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.4.0
|
'@types/node': 22.4.0
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Changeset = require('../../static/js/Changeset');
|
const Changeset = require('../../static/js/Changeset');
|
||||||
const ChatMessage = require('../../static/js/ChatMessage');
|
import ChatMessage from '../../static/js/ChatMessage';
|
||||||
const CustomError = require('../utils/customError');
|
const CustomError = require('../utils/customError');
|
||||||
const padManager = require('./PadManager');
|
const padManager = require('./PadManager');
|
||||||
const padMessageHandler = require('../handler/PadMessageHandler');
|
const padMessageHandler = require('../handler/PadMessageHandler');
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
|
|
||||||
const db = require('./DB');
|
const db = require('./DB');
|
||||||
const CustomError = require('../utils/customError');
|
const CustomError = require('../utils/customError');
|
||||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
const hooks = require('../../static/js/pluginfw/hooks');
|
||||||
const {randomString, padutils: {warnDeprecated}} = require('../../static/js/pad_utils');
|
import padutils, {randomString} from "../../static/js/pad_utils";
|
||||||
|
|
||||||
exports.getColorPalette = () => [
|
exports.getColorPalette = () => [
|
||||||
'#ffc7c7',
|
'#ffc7c7',
|
||||||
|
@ -169,7 +169,7 @@ exports.getAuthorId = async (token: string, user: object) => {
|
||||||
* @param {String} token The token
|
* @param {String} token The token
|
||||||
*/
|
*/
|
||||||
exports.getAuthor4Token = async (token: string) => {
|
exports.getAuthor4Token = async (token: string) => {
|
||||||
warnDeprecated(
|
padutils.warnDeprecated(
|
||||||
'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead');
|
'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead');
|
||||||
return await getAuthor4Token(token);
|
return await getAuthor4Token(token);
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CustomError = require('../utils/customError');
|
const CustomError = require('../utils/customError');
|
||||||
const randomString = require('../../static/js/pad_utils').randomString;
|
import {randomString} from "../../static/js/pad_utils";
|
||||||
const db = require('./DB');
|
const db = require('./DB');
|
||||||
const padManager = require('./PadManager');
|
const padManager = require('./PadManager');
|
||||||
const sessionManager = require('./SessionManager');
|
const sessionManager = require('./SessionManager');
|
||||||
|
|
|
@ -7,10 +7,10 @@ import {MapArrayType} from "../types/MapType";
|
||||||
* The pad object, defined with joose
|
* The pad object, defined with joose
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AttributeMap = require('../../static/js/AttributeMap');
|
import AttributeMap from '../../static/js/AttributeMap';
|
||||||
const Changeset = require('../../static/js/Changeset');
|
const Changeset = require('../../static/js/Changeset');
|
||||||
const ChatMessage = require('../../static/js/ChatMessage');
|
import ChatMessage from '../../static/js/ChatMessage';
|
||||||
const AttributePool = require('../../static/js/AttributePool');
|
import AttributePool from '../../static/js/AttributePool';
|
||||||
const Stream = require('../utils/Stream');
|
const Stream = require('../utils/Stream');
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const db = require('./DB');
|
const db = require('./DB');
|
||||||
|
@ -23,7 +23,7 @@ const CustomError = require('../utils/customError');
|
||||||
const readOnlyManager = require('./ReadOnlyManager');
|
const readOnlyManager = require('./ReadOnlyManager');
|
||||||
const randomString = require('../utils/randomstring');
|
const randomString = require('../utils/randomstring');
|
||||||
const hooks = require('../../static/js/pluginfw/hooks');
|
const hooks = require('../../static/js/pluginfw/hooks');
|
||||||
const {padutils: {warnDeprecated}} = require('../../static/js/pad_utils');
|
import pad_utils from "../../static/js/pad_utils";
|
||||||
const promises = require('../utils/promises');
|
const promises = require('../utils/promises');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +40,7 @@ exports.cleanText = (txt:string): string => txt.replace(/\r\n/g, '\n')
|
||||||
class Pad {
|
class Pad {
|
||||||
private db: Database;
|
private db: Database;
|
||||||
private atext: AText;
|
private atext: AText;
|
||||||
private pool: APool;
|
private pool: AttributePool;
|
||||||
private head: number;
|
private head: number;
|
||||||
private chatHead: number;
|
private chatHead: number;
|
||||||
private publicStatus: boolean;
|
private publicStatus: boolean;
|
||||||
|
@ -126,11 +126,11 @@ class Pad {
|
||||||
pad: this,
|
pad: this,
|
||||||
authorId,
|
authorId,
|
||||||
get author() {
|
get author() {
|
||||||
warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`);
|
pad_utils.warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`);
|
||||||
return this.authorId;
|
return this.authorId;
|
||||||
},
|
},
|
||||||
set author(authorId) {
|
set author(authorId) {
|
||||||
warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`);
|
pad_utils.warnDeprecated(`${hook} hook author context is deprecated; use authorId instead`);
|
||||||
this.authorId = authorId;
|
this.authorId = authorId;
|
||||||
},
|
},
|
||||||
...this.head === 0 ? {} : {
|
...this.head === 0 ? {} : {
|
||||||
|
@ -330,7 +330,7 @@ class Pad {
|
||||||
* @param {?number} [time] - Message timestamp (milliseconds since epoch). Deprecated; use
|
* @param {?number} [time] - Message timestamp (milliseconds since epoch). Deprecated; use
|
||||||
* `msgOrText.time` instead.
|
* `msgOrText.time` instead.
|
||||||
*/
|
*/
|
||||||
async appendChatMessage(msgOrText: string|typeof ChatMessage, authorId = null, time = null) {
|
async appendChatMessage(msgOrText: string| ChatMessage, authorId = null, time = null) {
|
||||||
const msg =
|
const msg =
|
||||||
msgOrText instanceof ChatMessage ? msgOrText : new ChatMessage(msgOrText, authorId, time);
|
msgOrText instanceof ChatMessage ? msgOrText : new ChatMessage(msgOrText, authorId, time);
|
||||||
this.chatHead++;
|
this.chatHead++;
|
||||||
|
@ -437,11 +437,11 @@ class Pad {
|
||||||
// let the plugins know the pad was copied
|
// let the plugins know the pad was copied
|
||||||
await hooks.aCallAll('padCopy', {
|
await hooks.aCallAll('padCopy', {
|
||||||
get originalPad() {
|
get originalPad() {
|
||||||
warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead');
|
pad_utils.warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead');
|
||||||
return this.srcPad;
|
return this.srcPad;
|
||||||
},
|
},
|
||||||
get destinationID() {
|
get destinationID() {
|
||||||
warnDeprecated(
|
pad_utils.warnDeprecated(
|
||||||
'padCopy destinationID context property is deprecated; use dstPad.id instead');
|
'padCopy destinationID context property is deprecated; use dstPad.id instead');
|
||||||
return this.dstPad.id;
|
return this.dstPad.id;
|
||||||
},
|
},
|
||||||
|
@ -538,11 +538,11 @@ class Pad {
|
||||||
|
|
||||||
await hooks.aCallAll('padCopy', {
|
await hooks.aCallAll('padCopy', {
|
||||||
get originalPad() {
|
get originalPad() {
|
||||||
warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead');
|
pad_utils.warnDeprecated('padCopy originalPad context property is deprecated; use srcPad instead');
|
||||||
return this.srcPad;
|
return this.srcPad;
|
||||||
},
|
},
|
||||||
get destinationID() {
|
get destinationID() {
|
||||||
warnDeprecated(
|
pad_utils.warnDeprecated(
|
||||||
'padCopy destinationID context property is deprecated; use dstPad.id instead');
|
'padCopy destinationID context property is deprecated; use dstPad.id instead');
|
||||||
return this.dstPad.id;
|
return this.dstPad.id;
|
||||||
},
|
},
|
||||||
|
@ -603,7 +603,7 @@ class Pad {
|
||||||
p.push(padManager.removePad(padID));
|
p.push(padManager.removePad(padID));
|
||||||
p.push(hooks.aCallAll('padRemove', {
|
p.push(hooks.aCallAll('padRemove', {
|
||||||
get padID() {
|
get padID() {
|
||||||
warnDeprecated('padRemove padID context property is deprecated; use pad.id instead');
|
pad_utils.warnDeprecated('padRemove padID context property is deprecated; use pad.id instead');
|
||||||
return this.pad.id;
|
return this.pad.id;
|
||||||
},
|
},
|
||||||
pad: this,
|
pad: this,
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
import {UserSettingsObject} from "../types/UserSettingsObject";
|
import {UserSettingsObject} from "../types/UserSettingsObject";
|
||||||
|
|
||||||
const authorManager = require('./AuthorManager');
|
const authorManager = require('./AuthorManager');
|
||||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
const hooks = require('../../static/js/pluginfw/hooks');
|
||||||
const padManager = require('./PadManager');
|
const padManager = require('./PadManager');
|
||||||
const readOnlyManager = require('./ReadOnlyManager');
|
const readOnlyManager = require('./ReadOnlyManager');
|
||||||
const sessionManager = require('./SessionManager');
|
const sessionManager = require('./SessionManager');
|
||||||
|
@ -30,7 +30,7 @@ const settings = require('../utils/Settings');
|
||||||
const webaccess = require('../hooks/express/webaccess');
|
const webaccess = require('../hooks/express/webaccess');
|
||||||
const log4js = require('log4js');
|
const log4js = require('log4js');
|
||||||
const authLogger = log4js.getLogger('auth');
|
const authLogger = log4js.getLogger('auth');
|
||||||
const {padutils} = require('../../static/js/pad_utils');
|
import padutils from '../../static/js/pad_utils'
|
||||||
|
|
||||||
const DENY = Object.freeze({accessStatus: 'deny'});
|
const DENY = Object.freeze({accessStatus: 'deny'});
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
const ejs = require('ejs');
|
const ejs = require('ejs');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
const hooks = require('../../static/js/pluginfw/hooks');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const resolve = require('resolve');
|
const resolve = require('resolve');
|
||||||
const settings = require('../utils/Settings');
|
const settings = require('../utils/Settings');
|
||||||
|
|
|
@ -31,7 +31,7 @@ import os from 'os';
|
||||||
const importHtml = require('../utils/ImportHtml');
|
const importHtml = require('../utils/ImportHtml');
|
||||||
const importEtherpad = require('../utils/ImportEtherpad');
|
const importEtherpad = require('../utils/ImportEtherpad');
|
||||||
import log4js from 'log4js';
|
import log4js from 'log4js';
|
||||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
const hooks = require('../../static/js/pluginfw/hooks');
|
||||||
|
|
||||||
const logger = log4js.getLogger('ImportHandler');
|
const logger = log4js.getLogger('ImportHandler');
|
||||||
|
|
||||||
|
|
|
@ -21,28 +21,29 @@
|
||||||
|
|
||||||
import {MapArrayType} from "../types/MapType";
|
import {MapArrayType} from "../types/MapType";
|
||||||
|
|
||||||
const AttributeMap = require('../../static/js/AttributeMap');
|
import AttributeMap from '../../static/js/AttributeMap';
|
||||||
const padManager = require('../db/PadManager');
|
const padManager = require('../db/PadManager');
|
||||||
const Changeset = require('../../static/js/Changeset');
|
const Changeset = require('../../static/js/Changeset');
|
||||||
const ChatMessage = require('../../static/js/ChatMessage');
|
import ChatMessage from '../../static/js/ChatMessage';
|
||||||
const AttributePool = require('../../static/js/AttributePool');
|
import AttributePool from '../../static/js/AttributePool';
|
||||||
const AttributeManager = require('../../static/js/AttributeManager');
|
const AttributeManager = require('../../static/js/AttributeManager');
|
||||||
const authorManager = require('../db/AuthorManager');
|
const authorManager = require('../db/AuthorManager');
|
||||||
const {padutils} = require('../../static/js/pad_utils');
|
import padutils from '../../static/js/pad_utils';
|
||||||
const readOnlyManager = require('../db/ReadOnlyManager');
|
const readOnlyManager = require('../db/ReadOnlyManager');
|
||||||
const settings = require('../utils/Settings');
|
const settings = require('../utils/Settings');
|
||||||
const securityManager = require('../db/SecurityManager');
|
const securityManager = require('../db/SecurityManager');
|
||||||
const plugins = require('../../static/js/pluginfw/plugin_defs.js');
|
const plugins = require('../../static/js/pluginfw/plugin_defs');
|
||||||
import log4js from 'log4js';
|
import log4js from 'log4js';
|
||||||
const messageLogger = log4js.getLogger('message');
|
const messageLogger = log4js.getLogger('message');
|
||||||
const accessLogger = log4js.getLogger('access');
|
const accessLogger = log4js.getLogger('access');
|
||||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
const hooks = require('../../static/js/pluginfw/hooks');
|
||||||
const stats = require('../stats')
|
const stats = require('../stats')
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
import {RateLimiterMemory} from 'rate-limiter-flexible';
|
import {RateLimiterMemory} from 'rate-limiter-flexible';
|
||||||
import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/SocketClientRequest";
|
import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/SocketClientRequest";
|
||||||
import {APool, AText, PadAuthor, PadType} from "../types/PadType";
|
import {APool, AText, PadAuthor, PadType} from "../types/PadType";
|
||||||
import {ChangeSet} from "../types/ChangeSet";
|
import {ChangeSet} from "../types/ChangeSet";
|
||||||
|
import {ChatMessageMessage, ClientReadyMessage, ClientSaveRevisionMessage, ClientSuggestUserName, ClientUserChangesMessage, ClientVarMessage, CustomMessage, UserNewInfoMessage} from "../../static/js/types/SocketIOMessage";
|
||||||
const webaccess = require('../hooks/express/webaccess');
|
const webaccess = require('../hooks/express/webaccess');
|
||||||
const { checkValidRev } = require('../utils/checkValidRev');
|
const { checkValidRev } = require('../utils/checkValidRev');
|
||||||
|
|
||||||
|
@ -214,7 +215,7 @@ exports.handleDisconnect = async (socket:any) => {
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
exports.handleMessage = async (socket:any, message:typeof ChatMessage) => {
|
exports.handleMessage = async (socket:any, message: ClientVarMessage) => {
|
||||||
const env = process.env.NODE_ENV || 'development';
|
const env = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
if (env === 'production') {
|
if (env === 'production') {
|
||||||
|
@ -348,15 +349,15 @@ exports.handleMessage = async (socket:any, message:typeof ChatMessage) => {
|
||||||
stats.counter('pendingEdits').inc();
|
stats.counter('pendingEdits').inc();
|
||||||
await padChannels.enqueue(thisSession.padId, {socket, message});
|
await padChannels.enqueue(thisSession.padId, {socket, message});
|
||||||
break;
|
break;
|
||||||
case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message); break;
|
case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message as unknown as UserNewInfoMessage); break;
|
||||||
case 'CHAT_MESSAGE': await handleChatMessage(socket, message); break;
|
case 'CHAT_MESSAGE': await handleChatMessage(socket, message as unknown as ChatMessageMessage); break;
|
||||||
case 'GET_CHAT_MESSAGES': await handleGetChatMessages(socket, message); break;
|
case 'GET_CHAT_MESSAGES': await handleGetChatMessages(socket, message); break;
|
||||||
case 'SAVE_REVISION': await handleSaveRevisionMessage(socket, message); break;
|
case 'SAVE_REVISION': await handleSaveRevisionMessage(socket, message as unknown as ClientSaveRevisionMessage); break;
|
||||||
case 'CLIENT_MESSAGE': {
|
case 'CLIENT_MESSAGE': {
|
||||||
const {type} = message.data.payload;
|
const {type} = message.data.payload;
|
||||||
try {
|
try {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'suggestUserName': handleSuggestUserName(socket, message); break;
|
case 'suggestUserName': handleSuggestUserName(socket, message as unknown as ClientSuggestUserName); break;
|
||||||
default: throw new Error('unknown message type');
|
default: throw new Error('unknown message type');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -384,7 +385,7 @@ exports.handleMessage = async (socket:any, message:typeof ChatMessage) => {
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
const handleSaveRevisionMessage = async (socket:any, message: string) => {
|
const handleSaveRevisionMessage = async (socket:any, message: ClientSaveRevisionMessage) => {
|
||||||
const {padId, author: authorId} = sessioninfos[socket.id];
|
const {padId, author: authorId} = sessioninfos[socket.id];
|
||||||
const pad = await padManager.getPad(padId, null, authorId);
|
const pad = await padManager.getPad(padId, null, authorId);
|
||||||
await pad.addSavedRevision(pad.head, authorId);
|
await pad.addSavedRevision(pad.head, authorId);
|
||||||
|
@ -397,7 +398,7 @@ const handleSaveRevisionMessage = async (socket:any, message: string) => {
|
||||||
* @param msg {Object} the message we're sending
|
* @param msg {Object} the message we're sending
|
||||||
* @param sessionID {string} the socketIO session to which we're sending this message
|
* @param sessionID {string} the socketIO session to which we're sending this message
|
||||||
*/
|
*/
|
||||||
exports.handleCustomObjectMessage = (msg: typeof ChatMessage, sessionID: string) => {
|
exports.handleCustomObjectMessage = (msg: CustomMessage, sessionID: string) => {
|
||||||
if (msg.data.type === 'CUSTOM') {
|
if (msg.data.type === 'CUSTOM') {
|
||||||
if (sessionID) {
|
if (sessionID) {
|
||||||
// a sessionID is targeted: directly to this sessionID
|
// a sessionID is targeted: directly to this sessionID
|
||||||
|
@ -432,7 +433,7 @@ exports.handleCustomMessage = (padID: string, msgString:string) => {
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
const handleChatMessage = async (socket:any, message: typeof ChatMessage) => {
|
const handleChatMessage = async (socket:any, message: ChatMessageMessage) => {
|
||||||
const chatMessage = ChatMessage.fromObject(message.data.message);
|
const chatMessage = ChatMessage.fromObject(message.data.message);
|
||||||
const {padId, author: authorId} = sessioninfos[socket.id];
|
const {padId, author: authorId} = sessioninfos[socket.id];
|
||||||
// Don't trust the user-supplied values.
|
// Don't trust the user-supplied values.
|
||||||
|
@ -452,7 +453,7 @@ const handleChatMessage = async (socket:any, message: typeof ChatMessage) => {
|
||||||
* @param {string} [padId] - The destination pad ID. Deprecated; pass a chat message
|
* @param {string} [padId] - The destination pad ID. Deprecated; pass a chat message
|
||||||
* object as the first argument and the destination pad ID as the second argument instead.
|
* object as the first argument and the destination pad ID as the second argument instead.
|
||||||
*/
|
*/
|
||||||
exports.sendChatMessageToPadClients = async (mt: typeof ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => {
|
exports.sendChatMessageToPadClients = async (mt: ChatMessage|number, puId: string, text:string|null = null, padId:string|null = null) => {
|
||||||
const message = mt instanceof ChatMessage ? mt : new ChatMessage(text, puId, mt);
|
const message = mt instanceof ChatMessage ? mt : new ChatMessage(text, puId, mt);
|
||||||
padId = mt instanceof ChatMessage ? puId : padId;
|
padId = mt instanceof ChatMessage ? puId : padId;
|
||||||
const pad = await padManager.getPad(padId, null, message.authorId);
|
const pad = await padManager.getPad(padId, null, message.authorId);
|
||||||
|
@ -499,7 +500,7 @@ const handleGetChatMessages = async (socket:any, {data: {start, end}}:any) => {
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
const handleSuggestUserName = (socket:any, message: typeof ChatMessage) => {
|
const handleSuggestUserName = (socket:any, message: ClientSuggestUserName) => {
|
||||||
const {newName, unnamedId} = message.data.payload;
|
const {newName, unnamedId} = message.data.payload;
|
||||||
if (newName == null) throw new Error('missing newName');
|
if (newName == null) throw new Error('missing newName');
|
||||||
if (unnamedId == null) throw new Error('missing unnamedId');
|
if (unnamedId == null) throw new Error('missing unnamedId');
|
||||||
|
@ -519,7 +520,7 @@ const handleSuggestUserName = (socket:any, message: typeof ChatMessage) => {
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId}}}: PadUserInfo) => {
|
const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId}}}: UserNewInfoMessage) => {
|
||||||
if (colorId == null) throw new Error('missing colorId');
|
if (colorId == null) throw new Error('missing colorId');
|
||||||
if (!name) name = null;
|
if (!name) name = null;
|
||||||
const session = sessioninfos[socket.id];
|
const session = sessioninfos[socket.id];
|
||||||
|
@ -567,7 +568,9 @@ const handleUserInfoUpdate = async (socket:any, {data: {userInfo: {name, colorId
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
const handleUserChanges = async (socket:any, message: typeof ChatMessage) => {
|
const handleUserChanges = async (socket:any, message: {
|
||||||
|
data: ClientUserChangesMessage
|
||||||
|
}) => {
|
||||||
// This one's no longer pending, as we're gonna process it now
|
// This one's no longer pending, as we're gonna process it now
|
||||||
stats.counter('pendingEdits').dec();
|
stats.counter('pendingEdits').dec();
|
||||||
|
|
||||||
|
@ -738,7 +741,7 @@ exports.updatePadClients = async (pad: PadType) => {
|
||||||
/**
|
/**
|
||||||
* Copied from the Etherpad Source Code. Don't know what this method does excatly...
|
* Copied from the Etherpad Source Code. Don't know what this method does excatly...
|
||||||
*/
|
*/
|
||||||
const _correctMarkersInPad = (atext: AText, apool: APool) => {
|
const _correctMarkersInPad = (atext: AText, apool: AttributePool) => {
|
||||||
const text = atext.text;
|
const text = atext.text;
|
||||||
|
|
||||||
// collect char positions of line markers (e.g. bullets) in new atext
|
// collect char positions of line markers (e.g. bullets) in new atext
|
||||||
|
@ -785,7 +788,7 @@ const _correctMarkersInPad = (atext: AText, apool: APool) => {
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
const handleClientReady = async (socket:any, message: typeof ChatMessage) => {
|
const handleClientReady = async (socket:any, message: ClientReadyMessage) => {
|
||||||
const sessionInfo = sessioninfos[socket.id];
|
const sessionInfo = sessioninfos[socket.id];
|
||||||
if (sessionInfo == null) throw new Error('client disconnected');
|
if (sessionInfo == null) throw new Error('client disconnected');
|
||||||
assert(sessionInfo.author);
|
assert(sessionInfo.author);
|
||||||
|
@ -793,8 +796,9 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => {
|
||||||
await hooks.aCallAll('clientReady', message); // Deprecated due to awkward context.
|
await hooks.aCallAll('clientReady', message); // Deprecated due to awkward context.
|
||||||
|
|
||||||
let {colorId: authorColorId, name: authorName} = message.userInfo || {};
|
let {colorId: authorColorId, name: authorName} = message.userInfo || {};
|
||||||
if (authorColorId && !/^#(?:[0-9A-F]{3}){1,2}$/i.test(authorColorId)) {
|
if (authorColorId && !/^#(?:[0-9A-F]{3}){1,2}$/i.test(authorColorId as string)) {
|
||||||
messageLogger.warn(`Ignoring invalid colorId in CLIENT_READY message: ${authorColorId}`);
|
messageLogger.warn(`Ignoring invalid colorId in CLIENT_READY message: ${authorColorId}`);
|
||||||
|
// @ts-ignore
|
||||||
authorColorId = null;
|
authorColorId = null;
|
||||||
}
|
}
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
@ -872,7 +876,7 @@ const handleClientReady = async (socket:any, message: typeof ChatMessage) => {
|
||||||
const revisionsNeeded = [];
|
const revisionsNeeded = [];
|
||||||
const changesets:MapArrayType<any> = {};
|
const changesets:MapArrayType<any> = {};
|
||||||
|
|
||||||
let startNum = message.client_rev + 1;
|
let startNum = message.client_rev! + 1;
|
||||||
let endNum = pad.getHeadRevisionNumber() + 1;
|
let endNum = pad.getHeadRevisionNumber() + 1;
|
||||||
|
|
||||||
const headNum = pad.getHeadRevisionNumber();
|
const headNum = pad.getHeadRevisionNumber();
|
||||||
|
|
|
@ -7,7 +7,7 @@ const languages = require('languages4translatewiki');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
||||||
const pluginDefs = require('../../static/js/pluginfw/plugin_defs.js');
|
const pluginDefs = require('../../static/js/pluginfw/plugin_defs');
|
||||||
const existsSync = require('../utils/path_exists');
|
const existsSync = require('../utils/path_exists');
|
||||||
const settings = require('../utils/Settings');
|
const settings = require('../utils/Settings');
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AttributeMap = require('../../static/js/AttributeMap');
|
import AttributeMap from '../../static/js/AttributeMap';
|
||||||
|
import AttributePool from "../../static/js/AttributePool";
|
||||||
const Changeset = require('../../static/js/Changeset');
|
const Changeset = require('../../static/js/Changeset');
|
||||||
const { checkValidRev } = require('./checkValidRev');
|
const { checkValidRev } = require('./checkValidRev');
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ type LineModel = {
|
||||||
[id:string]:string|number|LineModel
|
[id:string]:string|number|LineModel
|
||||||
}
|
}
|
||||||
|
|
||||||
exports._analyzeLine = (text:string, aline: LineModel, apool: Function) => {
|
exports._analyzeLine = (text:string, aline: LineModel, apool: AttributePool) => {
|
||||||
const line: LineModel = {};
|
const line: LineModel = {};
|
||||||
|
|
||||||
// identify list
|
// identify list
|
||||||
|
|
|
@ -27,7 +27,7 @@ const hooks = require('../../static/js/pluginfw/hooks');
|
||||||
const eejs = require('../eejs');
|
const eejs = require('../eejs');
|
||||||
const _analyzeLine = require('./ExportHelper')._analyzeLine;
|
const _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||||
const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
|
const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
|
||||||
const padutils = require('../../static/js/pad_utils').padutils;
|
import padutils from "../../static/js/pad_utils";
|
||||||
|
|
||||||
const getPadHTML = async (pad: PadType, revNum: string) => {
|
const getPadHTML = async (pad: PadType, revNum: string) => {
|
||||||
let atext = pad.atext;
|
let atext = pad.atext;
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {APool} from "../types/PadType";
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AttributePool = require('../../static/js/AttributePool');
|
import AttributePool from '../../static/js/AttributePool';
|
||||||
const {Pad} = require('../db/Pad');
|
const {Pad} = require('../db/Pad');
|
||||||
const Stream = require('./Stream');
|
const Stream = require('./Stream');
|
||||||
const authorManager = require('../db/AuthorManager');
|
const authorManager = require('../db/AuthorManager');
|
||||||
|
@ -61,7 +61,7 @@ exports.setPadRaw = async (padId: string, r: string, authorId = '') => {
|
||||||
try {
|
try {
|
||||||
const processRecord = async (key:string, value: null|{
|
const processRecord = async (key:string, value: null|{
|
||||||
padIDs: string|Record<string, unknown>,
|
padIDs: string|Record<string, unknown>,
|
||||||
pool: APool
|
pool: AttributePool
|
||||||
}) => {
|
}) => {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
const keyParts = key.split(':');
|
const keyParts = key.split(':');
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import {PadAuthor, PadType} from "../types/PadType";
|
import {PadAuthor, PadType} from "../types/PadType";
|
||||||
import {MapArrayType} from "../types/MapType";
|
import {MapArrayType} from "../types/MapType";
|
||||||
|
|
||||||
const AttributeMap = require('../../static/js/AttributeMap');
|
import AttributeMap from '../../static/js/AttributeMap';
|
||||||
const Changeset = require('../../static/js/Changeset');
|
const Changeset = require('../../static/js/Changeset');
|
||||||
const attributes = require('../../static/js/attributes');
|
const attributes = require('../../static/js/attributes');
|
||||||
const exportHtml = require('./ExportHtml');
|
const exportHtml = require('./ExportHtml');
|
||||||
|
|
|
@ -86,6 +86,7 @@
|
||||||
"@types/formidable": "^3.4.5",
|
"@types/formidable": "^3.4.5",
|
||||||
"@types/http-errors": "^2.0.4",
|
"@types/http-errors": "^2.0.4",
|
||||||
"@types/jquery": "^3.5.30",
|
"@types/jquery": "^3.5.30",
|
||||||
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/jsdom": "^21.1.7",
|
"@types/jsdom": "^21.1.7",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const AttributeMap = require('./AttributeMap');
|
import AttributeMap from './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');
|
|
@ -1,6 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const attributes = require('./attributes');
|
import AttributePool from "./AttributePool";
|
||||||
|
import {Attribute} from "./types/Attribute";
|
||||||
|
|
||||||
|
import attributes from './attributes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `[key, value]` pair of strings describing a text attribute.
|
* A `[key, value]` pair of strings describing a text attribute.
|
||||||
|
@ -21,6 +24,7 @@ const attributes = require('./attributes');
|
||||||
* Convenience class to convert an Op's attribute string to/from a Map of key, value pairs.
|
* Convenience class to convert an Op's attribute string to/from a Map of key, value pairs.
|
||||||
*/
|
*/
|
||||||
class AttributeMap extends Map {
|
class AttributeMap extends Map {
|
||||||
|
private readonly pool? : AttributePool|null
|
||||||
/**
|
/**
|
||||||
* Converts an attribute string into an AttributeMap.
|
* Converts an attribute string into an AttributeMap.
|
||||||
*
|
*
|
||||||
|
@ -28,14 +32,14 @@ class AttributeMap extends Map {
|
||||||
* @param {AttributePool} pool - Attribute pool.
|
* @param {AttributePool} pool - Attribute pool.
|
||||||
* @returns {AttributeMap}
|
* @returns {AttributeMap}
|
||||||
*/
|
*/
|
||||||
static fromString(str, pool) {
|
public static fromString(str: string, pool?: AttributePool|null): AttributeMap {
|
||||||
return new AttributeMap(pool).updateFromString(str);
|
return new AttributeMap(pool).updateFromString(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AttributePool} pool - Attribute pool.
|
* @param {AttributePool} pool - Attribute pool.
|
||||||
*/
|
*/
|
||||||
constructor(pool) {
|
constructor(pool?: AttributePool|null) {
|
||||||
super();
|
super();
|
||||||
/** @public */
|
/** @public */
|
||||||
this.pool = pool;
|
this.pool = pool;
|
||||||
|
@ -46,15 +50,15 @@ class AttributeMap extends Map {
|
||||||
* @param {string} v - Attribute value.
|
* @param {string} v - Attribute value.
|
||||||
* @returns {AttributeMap} `this` (for chaining).
|
* @returns {AttributeMap} `this` (for chaining).
|
||||||
*/
|
*/
|
||||||
set(k, v) {
|
set(k: string, v: string):this {
|
||||||
k = k == null ? '' : String(k);
|
k = k == null ? '' : String(k);
|
||||||
v = v == null ? '' : String(v);
|
v = v == null ? '' : String(v);
|
||||||
this.pool.putAttrib([k, v]);
|
this.pool!.putAttrib([k, v]);
|
||||||
return super.set(k, v);
|
return super.set(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return attributes.attribsToString(attributes.sort([...this]), this.pool);
|
return attributes.attribsToString(attributes.sort([...this]), this.pool!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,7 +67,7 @@ class AttributeMap extends Map {
|
||||||
* key is removed from this map (if present).
|
* key is removed from this map (if present).
|
||||||
* @returns {AttributeMap} `this` (for chaining).
|
* @returns {AttributeMap} `this` (for chaining).
|
||||||
*/
|
*/
|
||||||
update(entries, emptyValueIsDelete = false) {
|
update(entries: Iterable<Attribute>, emptyValueIsDelete: boolean = false): AttributeMap {
|
||||||
for (let [k, v] of entries) {
|
for (let [k, v] of entries) {
|
||||||
k = k == null ? '' : String(k);
|
k = k == null ? '' : String(k);
|
||||||
v = v == null ? '' : String(v);
|
v = v == null ? '' : String(v);
|
||||||
|
@ -83,9 +87,9 @@ class AttributeMap extends Map {
|
||||||
* key is removed from this map (if present).
|
* key is removed from this map (if present).
|
||||||
* @returns {AttributeMap} `this` (for chaining).
|
* @returns {AttributeMap} `this` (for chaining).
|
||||||
*/
|
*/
|
||||||
updateFromString(str, emptyValueIsDelete = false) {
|
updateFromString(str: string, emptyValueIsDelete: boolean = false): AttributeMap {
|
||||||
return this.update(attributes.attribsFromString(str, this.pool), emptyValueIsDelete);
|
return this.update(attributes.attribsFromString(str, this.pool!), emptyValueIsDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AttributeMap;
|
export default AttributeMap
|
|
@ -44,6 +44,8 @@
|
||||||
* @property {number} nextNum - The attribute ID to assign to the next new attribute.
|
* @property {number} nextNum - The attribute ID to assign to the next new attribute.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {Attribute} from "./types/Attribute";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an attribute pool, which is a collection of attributes (pairs of key and value
|
* Represents an attribute pool, which is a collection of attributes (pairs of key and value
|
||||||
* strings) along with their identifiers (non-negative integers).
|
* strings) along with their identifiers (non-negative integers).
|
||||||
|
@ -55,6 +57,14 @@
|
||||||
* in the pad.
|
* in the pad.
|
||||||
*/
|
*/
|
||||||
class AttributePool {
|
class AttributePool {
|
||||||
|
numToAttrib: {
|
||||||
|
[key: number]: [string, string]
|
||||||
|
}
|
||||||
|
private attribToNum: {
|
||||||
|
[key: number]: [string, string]
|
||||||
|
}
|
||||||
|
private nextNum: number
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
/**
|
/**
|
||||||
* Maps an attribute identifier to the attribute's `[key, value]` string pair.
|
* Maps an attribute identifier to the attribute's `[key, value]` string pair.
|
||||||
|
@ -96,7 +106,10 @@ class AttributePool {
|
||||||
*/
|
*/
|
||||||
clone() {
|
clone() {
|
||||||
const c = new AttributePool();
|
const c = new AttributePool();
|
||||||
for (const [n, a] of Object.entries(this.numToAttrib)) c.numToAttrib[n] = [a[0], a[1]];
|
for (const [n, a] of Object.entries(this.numToAttrib)){
|
||||||
|
// @ts-ignore
|
||||||
|
c.numToAttrib[n] = [a[0], a[1]];
|
||||||
|
}
|
||||||
Object.assign(c.attribToNum, this.attribToNum);
|
Object.assign(c.attribToNum, this.attribToNum);
|
||||||
c.nextNum = this.nextNum;
|
c.nextNum = this.nextNum;
|
||||||
return c;
|
return c;
|
||||||
|
@ -111,15 +124,17 @@ class AttributePool {
|
||||||
* membership in the pool without mutating the pool.
|
* membership in the pool without mutating the pool.
|
||||||
* @returns {number} The attribute's identifier, or -1 if the attribute is not in the pool.
|
* @returns {number} The attribute's identifier, or -1 if the attribute is not in the pool.
|
||||||
*/
|
*/
|
||||||
putAttrib(attrib, dontAddIfAbsent = false) {
|
putAttrib(attrib: Attribute, dontAddIfAbsent = false) {
|
||||||
const str = String(attrib);
|
const str = String(attrib);
|
||||||
if (str in this.attribToNum) {
|
if (str in this.attribToNum) {
|
||||||
|
// @ts-ignore
|
||||||
return this.attribToNum[str];
|
return this.attribToNum[str];
|
||||||
}
|
}
|
||||||
if (dontAddIfAbsent) {
|
if (dontAddIfAbsent) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
const num = this.nextNum++;
|
const num = this.nextNum++;
|
||||||
|
// @ts-ignore
|
||||||
this.attribToNum[str] = num;
|
this.attribToNum[str] = num;
|
||||||
this.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')];
|
this.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')];
|
||||||
return num;
|
return num;
|
||||||
|
@ -130,7 +145,7 @@ class AttributePool {
|
||||||
* @returns {Attribute} The attribute with the given identifier, or nullish if there is no such
|
* @returns {Attribute} The attribute with the given identifier, or nullish if there is no such
|
||||||
* attribute.
|
* attribute.
|
||||||
*/
|
*/
|
||||||
getAttrib(num) {
|
getAttrib(num: number): Attribute {
|
||||||
const pair = this.numToAttrib[num];
|
const pair = this.numToAttrib[num];
|
||||||
if (!pair) {
|
if (!pair) {
|
||||||
return pair;
|
return pair;
|
||||||
|
@ -143,7 +158,7 @@ class AttributePool {
|
||||||
* @returns {string} Eqivalent to `getAttrib(num)[0]` if the attribute exists, otherwise the empty
|
* @returns {string} Eqivalent to `getAttrib(num)[0]` if the attribute exists, otherwise the empty
|
||||||
* string.
|
* string.
|
||||||
*/
|
*/
|
||||||
getAttribKey(num) {
|
getAttribKey(num: number): string {
|
||||||
const pair = this.numToAttrib[num];
|
const pair = this.numToAttrib[num];
|
||||||
if (!pair) return '';
|
if (!pair) return '';
|
||||||
return pair[0];
|
return pair[0];
|
||||||
|
@ -154,7 +169,7 @@ class AttributePool {
|
||||||
* @returns {string} Eqivalent to `getAttrib(num)[1]` if the attribute exists, otherwise the empty
|
* @returns {string} Eqivalent to `getAttrib(num)[1]` if the attribute exists, otherwise the empty
|
||||||
* string.
|
* string.
|
||||||
*/
|
*/
|
||||||
getAttribValue(num) {
|
getAttribValue(num: number) {
|
||||||
const pair = this.numToAttrib[num];
|
const pair = this.numToAttrib[num];
|
||||||
if (!pair) return '';
|
if (!pair) return '';
|
||||||
return pair[1];
|
return pair[1];
|
||||||
|
@ -166,8 +181,8 @@ class AttributePool {
|
||||||
* @param {Function} func - Callback to call with two arguments: key and value. Its return value
|
* @param {Function} func - Callback to call with two arguments: key and value. Its return value
|
||||||
* is ignored.
|
* is ignored.
|
||||||
*/
|
*/
|
||||||
eachAttrib(func) {
|
eachAttrib(func: (k: string, v: string)=>void) {
|
||||||
for (const n of Object.keys(this.numToAttrib)) {
|
for (const n in this.numToAttrib) {
|
||||||
const pair = this.numToAttrib[n];
|
const pair = this.numToAttrib[n];
|
||||||
func(pair[0], pair[1]);
|
func(pair[0], pair[1]);
|
||||||
}
|
}
|
||||||
|
@ -196,11 +211,12 @@ class AttributePool {
|
||||||
* `new AttributePool().fromJsonable(pool.toJsonable())` to copy because the resulting shared
|
* `new AttributePool().fromJsonable(pool.toJsonable())` to copy because the resulting shared
|
||||||
* state will lead to pool corruption.
|
* state will lead to pool corruption.
|
||||||
*/
|
*/
|
||||||
fromJsonable(obj) {
|
fromJsonable(obj: this) {
|
||||||
this.numToAttrib = obj.numToAttrib;
|
this.numToAttrib = obj.numToAttrib;
|
||||||
this.nextNum = obj.nextNum;
|
this.nextNum = obj.nextNum;
|
||||||
this.attribToNum = {};
|
this.attribToNum = {};
|
||||||
for (const n of Object.keys(this.numToAttrib)) {
|
for (const n of Object.keys(this.numToAttrib)) {
|
||||||
|
// @ts-ignore
|
||||||
this.attribToNum[String(this.numToAttrib[n])] = Number(n);
|
this.attribToNum[String(this.numToAttrib[n])] = Number(n);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
@ -213,6 +229,7 @@ class AttributePool {
|
||||||
if (!Number.isInteger(this.nextNum)) throw new Error('nextNum property is not an integer');
|
if (!Number.isInteger(this.nextNum)) throw new Error('nextNum property is not an integer');
|
||||||
if (this.nextNum < 0) throw new Error('nextNum property is negative');
|
if (this.nextNum < 0) throw new Error('nextNum property is negative');
|
||||||
for (const prop of ['numToAttrib', 'attribToNum']) {
|
for (const prop of ['numToAttrib', 'attribToNum']) {
|
||||||
|
// @ts-ignore
|
||||||
const obj = this[prop];
|
const obj = this[prop];
|
||||||
if (obj == null) throw new Error(`${prop} property is null`);
|
if (obj == null) throw new Error(`${prop} property is null`);
|
||||||
if (typeof obj !== 'object') throw new TypeError(`${prop} property is not an object`);
|
if (typeof obj !== 'object') throw new TypeError(`${prop} property is not an object`);
|
||||||
|
@ -231,9 +248,10 @@ class AttributePool {
|
||||||
if (v == null) throw new TypeError(`attrib ${i} value is null`);
|
if (v == null) throw new TypeError(`attrib ${i} value is null`);
|
||||||
if (typeof v !== 'string') throw new TypeError(`attrib ${i} value is not a string`);
|
if (typeof v !== 'string') throw new TypeError(`attrib ${i} value is not a string`);
|
||||||
const attrStr = String(attr);
|
const attrStr = String(attr);
|
||||||
|
// @ts-ignore
|
||||||
if (this.attribToNum[attrStr] !== i) throw new Error(`attribToNum for ${attrStr} !== ${i}`);
|
if (this.attribToNum[attrStr] !== i) throw new Error(`attribToNum for ${attrStr} !== ${i}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = AttributePool;
|
export default AttributePool
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -22,10 +23,10 @@
|
||||||
* https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
* https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AttributeMap = require('./AttributeMap');
|
import AttributeMap from './AttributeMap';
|
||||||
const AttributePool = require('./AttributePool');
|
import AttributePool from './AttributePool';
|
||||||
const attributes = require('./attributes');
|
const attributes = require('./attributes');
|
||||||
const {padutils} = require('./pad_utils');
|
import padutils from './pad_utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `[key, value]` pair of strings describing a text attribute.
|
* A `[key, value]` pair of strings describing a text attribute.
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const {padutils: {warnDeprecated}} = require('./pad_utils');
|
import padUtils from './pad_utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a chat message stored in the database and transmitted among users. Plugins can extend
|
* Represents a chat message stored in the database and transmitted among users. Plugins can extend
|
||||||
|
@ -8,14 +8,25 @@ const {padutils: {warnDeprecated}} = require('./pad_utils');
|
||||||
*
|
*
|
||||||
* Supports serialization to JSON.
|
* Supports serialization to JSON.
|
||||||
*/
|
*/
|
||||||
class ChatMessage {
|
export class ChatMessage {
|
||||||
static fromObject(obj) {
|
customMetadata: any
|
||||||
|
text: string|null
|
||||||
|
public authorId: string|null
|
||||||
|
displayName: string|null
|
||||||
|
time: number|null
|
||||||
|
static fromObject(obj: ChatMessage) {
|
||||||
// The userId property was renamed to authorId, and userName was renamed to displayName. Accept
|
// The userId property was renamed to authorId, and userName was renamed to displayName. Accept
|
||||||
// the old names in case the db record was written by an older version of Etherpad.
|
// the old names in case the db record was written by an older version of Etherpad.
|
||||||
obj = Object.assign({}, obj); // Don't mutate the caller's object.
|
obj = Object.assign({}, obj); // Don't mutate the caller's object.
|
||||||
if ('userId' in obj && !('authorId' in obj)) obj.authorId = obj.userId;
|
if ('userId' in obj && !('authorId' in obj)) { // @ts-ignore
|
||||||
|
obj.authorId = obj.userId;
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
delete obj.userId;
|
delete obj.userId;
|
||||||
if ('userName' in obj && !('displayName' in obj)) obj.displayName = obj.userName;
|
if ('userName' in obj && !('displayName' in obj)) { // @ts-ignore
|
||||||
|
obj.displayName = obj.userName;
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
delete obj.userName;
|
delete obj.userName;
|
||||||
return Object.assign(new ChatMessage(), obj);
|
return Object.assign(new ChatMessage(), obj);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +36,7 @@ class ChatMessage {
|
||||||
* @param {?string} [authorId] - Initial value of the `authorId` property.
|
* @param {?string} [authorId] - Initial value of the `authorId` property.
|
||||||
* @param {?number} [time] - Initial value of the `time` property.
|
* @param {?number} [time] - Initial value of the `time` property.
|
||||||
*/
|
*/
|
||||||
constructor(text = null, authorId = null, time = null) {
|
constructor(text: string | null = null, authorId: string | null = null, time: number | null = null) {
|
||||||
/**
|
/**
|
||||||
* The raw text of the user's chat message (before any rendering or processing).
|
* The raw text of the user's chat message (before any rendering or processing).
|
||||||
*
|
*
|
||||||
|
@ -62,11 +73,11 @@ class ChatMessage {
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
get userId() {
|
get userId() {
|
||||||
warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead');
|
padUtils.warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead');
|
||||||
return this.authorId;
|
return this.authorId;
|
||||||
}
|
}
|
||||||
set userId(val) {
|
set userId(val) {
|
||||||
warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead');
|
padUtils.warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead');
|
||||||
this.authorId = val;
|
this.authorId = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,11 +88,11 @@ class ChatMessage {
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
get userName() {
|
get userName() {
|
||||||
warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead');
|
padUtils.warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead');
|
||||||
return this.displayName;
|
return this.displayName;
|
||||||
}
|
}
|
||||||
set userName(val) {
|
set userName(val) {
|
||||||
warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead');
|
padUtils.warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead');
|
||||||
this.displayName = val;
|
this.displayName = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,10 +100,12 @@ class ChatMessage {
|
||||||
// doesn't support authorId and displayName.
|
// doesn't support authorId and displayName.
|
||||||
toJSON() {
|
toJSON() {
|
||||||
const {authorId, displayName, ...obj} = this;
|
const {authorId, displayName, ...obj} = this;
|
||||||
|
// @ts-ignore
|
||||||
obj.userId = authorId;
|
obj.userId = authorId;
|
||||||
|
// @ts-ignore
|
||||||
obj.userName = displayName;
|
obj.userName = displayName;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ChatMessage;
|
export default ChatMessage
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'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.
|
|
@ -6,6 +6,8 @@
|
||||||
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
|
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {MapArrayType} from "../../node/types/MapType";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
|
@ -22,11 +24,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const isNodeText = (node) => (node.nodeType === 3);
|
export const isNodeText = (node: {
|
||||||
|
nodeType: number
|
||||||
|
}) => (node.nodeType === 3);
|
||||||
|
|
||||||
const getAssoc = (obj, name) => obj[`_magicdom_${name}`];
|
export const getAssoc = (obj: MapArrayType<any>, name: string) => obj[`_magicdom_${name}`];
|
||||||
|
|
||||||
const setAssoc = (obj, name, value) => {
|
export const setAssoc = (obj: MapArrayType<any>, name: string, value: string) => {
|
||||||
// note that in IE designMode, properties of a node can get
|
// note that in IE designMode, properties of a node can get
|
||||||
// copied to new nodes that are spawned during editing; also,
|
// copied to new nodes that are spawned during editing; also,
|
||||||
// properties representable in HTML text can survive copy-and-paste
|
// properties representable in HTML text can survive copy-and-paste
|
||||||
|
@ -38,7 +42,7 @@ const setAssoc = (obj, name, value) => {
|
||||||
// between false and true, a number between 0 and numItems inclusive.
|
// between false and true, a number between 0 and numItems inclusive.
|
||||||
|
|
||||||
|
|
||||||
const binarySearch = (numItems, func) => {
|
export const binarySearch = (numItems: number, func: (num: number)=>boolean) => {
|
||||||
if (numItems < 1) return 0;
|
if (numItems < 1) return 0;
|
||||||
if (func(0)) return 0;
|
if (func(0)) return 0;
|
||||||
if (!func(numItems - 1)) return numItems;
|
if (!func(numItems - 1)) return numItems;
|
||||||
|
@ -52,17 +56,10 @@ const binarySearch = (numItems, func) => {
|
||||||
return high;
|
return high;
|
||||||
};
|
};
|
||||||
|
|
||||||
const binarySearchInfinite = (expectedLength, func) => {
|
export const binarySearchInfinite = (expectedLength: number, func: (num: number)=>boolean) => {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (!func(i)) i += expectedLength;
|
while (!func(i)) i += expectedLength;
|
||||||
return binarySearch(i, func);
|
return binarySearch(i, func);
|
||||||
};
|
};
|
||||||
|
|
||||||
const noop = () => {};
|
export const noop = () => {};
|
||||||
|
|
||||||
exports.isNodeText = isNodeText;
|
|
||||||
exports.getAssoc = getAssoc;
|
|
||||||
exports.setAssoc = setAssoc;
|
|
||||||
exports.binarySearch = binarySearch;
|
|
||||||
exports.binarySearchInfinite = binarySearchInfinite;
|
|
||||||
exports.noop = noop;
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,9 +19,9 @@
|
||||||
*/
|
*/
|
||||||
let documentAttributeManager;
|
let documentAttributeManager;
|
||||||
|
|
||||||
const AttributeMap = require('./AttributeMap');
|
import AttributeMap from './AttributeMap';
|
||||||
const browser = require('./vendors/browser');
|
const browser = require('./vendors/browser');
|
||||||
const padutils = require('./pad_utils').padutils;
|
import padutils from './pad_utils'
|
||||||
const Ace2Common = require('./ace2_common');
|
const Ace2Common = require('./ace2_common');
|
||||||
const $ = require('./rjquery').$;
|
const $ = require('./rjquery').$;
|
||||||
|
|
||||||
|
@ -29,19 +30,18 @@ const getAssoc = Ace2Common.getAssoc;
|
||||||
const setAssoc = Ace2Common.setAssoc;
|
const setAssoc = Ace2Common.setAssoc;
|
||||||
const noop = Ace2Common.noop;
|
const noop = Ace2Common.noop;
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
|
import SkipList from "./skiplist";
|
||||||
import Scroll from './scroll'
|
import Scroll from './scroll'
|
||||||
|
import AttribPool from './AttributePool'
|
||||||
|
|
||||||
function Ace2Inner(editorInfo, cssManagers) {
|
function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const makeChangesetTracker = require('./changesettracker').makeChangesetTracker;
|
const makeChangesetTracker = require('./changesettracker').makeChangesetTracker;
|
||||||
const colorutils = require('./colorutils').colorutils;
|
const colorutils = require('./colorutils').colorutils;
|
||||||
const makeContentCollector = require('./contentcollector').makeContentCollector;
|
const makeContentCollector = require('./contentcollector').makeContentCollector;
|
||||||
const domline = require('./domline').domline;
|
const domline = require('./domline').domline;
|
||||||
const AttribPool = require('./AttributePool');
|
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const ChangesetUtils = require('./ChangesetUtils');
|
const ChangesetUtils = require('./ChangesetUtils');
|
||||||
const linestylefilter = require('./linestylefilter').linestylefilter;
|
const linestylefilter = require('./linestylefilter').linestylefilter;
|
||||||
const SkipList = require('./skiplist');
|
|
||||||
const undoModule = require('./undomodule').undoModule;
|
const undoModule = require('./undomodule').undoModule;
|
||||||
const AttributeManager = require('./AttributeManager');
|
const AttributeManager = require('./AttributeManager');
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
|
@ -17,6 +17,9 @@
|
||||||
* @typedef {string} AttributeString
|
* @typedef {string} AttributeString
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import AttributePool from "./AttributePool";
|
||||||
|
import {Attribute} from "./types/Attribute";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an attribute string into a sequence of attribute identifier numbers.
|
* Converts an attribute string into a sequence of attribute identifier numbers.
|
||||||
*
|
*
|
||||||
|
@ -28,7 +31,7 @@
|
||||||
* appear in `str`.
|
* appear in `str`.
|
||||||
* @returns {Generator<number>}
|
* @returns {Generator<number>}
|
||||||
*/
|
*/
|
||||||
exports.decodeAttribString = function* (str) {
|
export const decodeAttribString = function* (str: string): Generator<number> {
|
||||||
const re = /\*([0-9a-z]+)|./gy;
|
const re = /\*([0-9a-z]+)|./gy;
|
||||||
let match;
|
let match;
|
||||||
while ((match = re.exec(str)) != null) {
|
while ((match = re.exec(str)) != null) {
|
||||||
|
@ -38,7 +41,7 @@ exports.decodeAttribString = function* (str) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkAttribNum = (n) => {
|
const checkAttribNum = (n: number|object) => {
|
||||||
if (typeof n !== 'number') throw new TypeError(`not a number: ${n}`);
|
if (typeof n !== 'number') throw new TypeError(`not a number: ${n}`);
|
||||||
if (n < 0) throw new Error(`attribute number is negative: ${n}`);
|
if (n < 0) throw new Error(`attribute number is negative: ${n}`);
|
||||||
if (n !== Math.trunc(n)) throw new Error(`attribute number is not an integer: ${n}`);
|
if (n !== Math.trunc(n)) throw new Error(`attribute number is not an integer: ${n}`);
|
||||||
|
@ -50,7 +53,7 @@ const checkAttribNum = (n) => {
|
||||||
* @param {Iterable<number>} attribNums - Sequence of attribute numbers.
|
* @param {Iterable<number>} attribNums - Sequence of attribute numbers.
|
||||||
* @returns {AttributeString}
|
* @returns {AttributeString}
|
||||||
*/
|
*/
|
||||||
exports.encodeAttribString = (attribNums) => {
|
export const encodeAttribString = (attribNums: Iterable<number>): string => {
|
||||||
let str = '';
|
let str = '';
|
||||||
for (const n of attribNums) {
|
for (const n of attribNums) {
|
||||||
checkAttribNum(n);
|
checkAttribNum(n);
|
||||||
|
@ -67,7 +70,7 @@ exports.encodeAttribString = (attribNums) => {
|
||||||
* @yields {Attribute} The identified attributes, in the same order as `attribNums`.
|
* @yields {Attribute} The identified attributes, in the same order as `attribNums`.
|
||||||
* @returns {Generator<Attribute>}
|
* @returns {Generator<Attribute>}
|
||||||
*/
|
*/
|
||||||
exports.attribsFromNums = function* (attribNums, pool) {
|
export const attribsFromNums = function* (attribNums: Iterable<number>, pool: AttributePool): Generator<Attribute> {
|
||||||
for (const n of attribNums) {
|
for (const n of attribNums) {
|
||||||
checkAttribNum(n);
|
checkAttribNum(n);
|
||||||
const attrib = pool.getAttrib(n);
|
const attrib = pool.getAttrib(n);
|
||||||
|
@ -87,7 +90,7 @@ exports.attribsFromNums = function* (attribNums, pool) {
|
||||||
* @yields {number} The attribute number of each attribute in `attribs`, in order.
|
* @yields {number} The attribute number of each attribute in `attribs`, in order.
|
||||||
* @returns {Generator<number>}
|
* @returns {Generator<number>}
|
||||||
*/
|
*/
|
||||||
exports.attribsToNums = function* (attribs, pool) {
|
export const attribsToNums = function* (attribs: Iterable<Attribute>, pool: AttributePool) {
|
||||||
for (const attrib of attribs) yield pool.putAttrib(attrib);
|
for (const attrib of attribs) yield pool.putAttrib(attrib);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,8 +105,8 @@ exports.attribsToNums = function* (attribs, pool) {
|
||||||
* @yields {Attribute} The attributes identified in `str`, in order.
|
* @yields {Attribute} The attributes identified in `str`, in order.
|
||||||
* @returns {Generator<Attribute>}
|
* @returns {Generator<Attribute>}
|
||||||
*/
|
*/
|
||||||
exports.attribsFromString = function* (str, pool) {
|
export const attribsFromString = function* (str: string, pool: AttributePool): Generator<Attribute> {
|
||||||
yield* exports.attribsFromNums(exports.decodeAttribString(str), pool);
|
yield* attribsFromNums(decodeAttribString(str), pool);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,8 +119,8 @@ exports.attribsFromString = function* (str, pool) {
|
||||||
* @param {AttributePool} pool - Attribute pool.
|
* @param {AttributePool} pool - Attribute pool.
|
||||||
* @returns {AttributeString}
|
* @returns {AttributeString}
|
||||||
*/
|
*/
|
||||||
exports.attribsToString =
|
export const attribsToString =
|
||||||
(attribs, pool) => exports.encodeAttribString(exports.attribsToNums(attribs, pool));
|
(attribs: Iterable<Attribute>, pool: AttributePool): string => encodeAttribString(attribsToNums(attribs, pool));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts the attributes in canonical order. The order of entries with the same attribute name is
|
* Sorts the attributes in canonical order. The order of entries with the same attribute name is
|
||||||
|
@ -126,5 +129,14 @@ exports.attribsToString =
|
||||||
* @param {Attribute[]} attribs - Attributes to sort in place.
|
* @param {Attribute[]} attribs - Attributes to sort in place.
|
||||||
* @returns {Attribute[]} `attribs` (for chaining).
|
* @returns {Attribute[]} `attribs` (for chaining).
|
||||||
*/
|
*/
|
||||||
exports.sort =
|
export const sort = (attribs: Attribute[]): Attribute[] => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0));
|
||||||
(attribs) => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0));
|
|
||||||
|
export default {
|
||||||
|
decodeAttribString,
|
||||||
|
encodeAttribString,
|
||||||
|
attribsFromNums,
|
||||||
|
attribsToNums,
|
||||||
|
attribsFromString,
|
||||||
|
attribsToString,
|
||||||
|
sort,
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
// @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt Apache-2.0
|
// @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt Apache-2.0
|
||||||
|
|
||||||
/* Copyright 2021 Richard Hansen <rhansen@rhansen.org> */
|
/* Copyright 2021 Richard Hansen <rhansen@rhansen.org> */
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
|
|
||||||
const makeCSSManager = require('./cssmanager').makeCSSManager;
|
const makeCSSManager = require('./cssmanager').makeCSSManager;
|
||||||
const domline = require('./domline').domline;
|
const domline = require('./domline').domline;
|
||||||
const AttribPool = require('./AttributePool');
|
import AttribPool from './AttributePool';
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const attributes = require('./attributes');
|
const attributes = require('./attributes');
|
||||||
const linestylefilter = require('./linestylefilter').linestylefilter;
|
const linestylefilter = require('./linestylefilter').linestylefilter;
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'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.
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,8 +23,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AttributeMap = require('./AttributeMap');
|
import AttributeMap from './AttributeMap';
|
||||||
const AttributePool = require('./AttributePool');
|
import AttributePool from './AttributePool';
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
|
|
||||||
const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => {
|
const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => {
|
5
src/static/js/chat.js → src/static/js/chat.ts
Executable file → Normal file
5
src/static/js/chat.js → src/static/js/chat.ts
Executable file → Normal file
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
@ -15,8 +16,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const ChatMessage = require('./ChatMessage');
|
import ChatMessage from './ChatMessage';
|
||||||
const padutils = require('./pad_utils').padutils;
|
import padutils from './pad_utils'
|
||||||
const padcookie = require('./pad_cookie').padcookie;
|
const padcookie = require('./pad_cookie').padcookie;
|
||||||
const Tinycon = require('tinycon/tinycon');
|
const Tinycon = require('tinycon/tinycon');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'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.
|
||||||
|
@ -26,7 +27,7 @@
|
||||||
|
|
||||||
const _MAX_LIST_LEVEL = 16;
|
const _MAX_LIST_LEVEL = 16;
|
||||||
|
|
||||||
const AttributeMap = require('./AttributeMap');
|
import AttributeMap from './AttributeMap';
|
||||||
const UNorm = require('unorm');
|
const UNorm = require('unorm');
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
|
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +36,7 @@ const attributes = require('./attributes');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
const linestylefilter = {};
|
const linestylefilter = {};
|
||||||
const AttributeManager = require('./AttributeManager');
|
const AttributeManager = require('./AttributeManager');
|
||||||
const padutils = require('./pad_utils').padutils;
|
import padutils from './pad_utils'
|
||||||
|
|
||||||
linestylefilter.ATTRIB_CLASSES = {
|
linestylefilter.ATTRIB_CLASSES = {
|
||||||
bold: 'tag:b',
|
bold: 'tag:b',
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +34,8 @@ require('./vendors/gritter');
|
||||||
|
|
||||||
import html10n from './vendors/html10n'
|
import html10n from './vendors/html10n'
|
||||||
|
|
||||||
const Cookies = require('./pad_utils').Cookies;
|
import {Cookies} from "./pad_utils";
|
||||||
|
|
||||||
const chat = require('./chat').chat;
|
const chat = require('./chat').chat;
|
||||||
const getCollabClient = require('./collab_client').getCollabClient;
|
const getCollabClient = require('./collab_client').getCollabClient;
|
||||||
const padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus;
|
const padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus;
|
||||||
|
@ -44,9 +46,9 @@ const padimpexp = require('./pad_impexp').padimpexp;
|
||||||
const padmodals = require('./pad_modals').padmodals;
|
const padmodals = require('./pad_modals').padmodals;
|
||||||
const padsavedrevs = require('./pad_savedrevs');
|
const padsavedrevs = require('./pad_savedrevs');
|
||||||
const paduserlist = require('./pad_userlist').paduserlist;
|
const paduserlist = require('./pad_userlist').paduserlist;
|
||||||
const padutils = require('./pad_utils').padutils;
|
import padutils from './pad_utils'
|
||||||
const colorutils = require('./colorutils').colorutils;
|
const colorutils = require('./colorutils').colorutils;
|
||||||
const randomString = require('./pad_utils').randomString;
|
import {randomString} from "./pad_utils";
|
||||||
const socketio = require('./socketio');
|
const socketio = require('./socketio');
|
||||||
|
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
import html10n from './vendors/html10n';
|
import html10n from './vendors/html10n';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +17,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Cookies = require('./pad_utils').Cookies;
|
import {Cookies} from "./pad_utils";
|
||||||
|
|
||||||
exports.padcookie = new class {
|
exports.padcookie = new class {
|
||||||
constructor() {
|
constructor() {
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
|
|
||||||
const browser = require('./vendors/browser');
|
const browser = require('./vendors/browser');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
const padutils = require('./pad_utils').padutils;
|
import padutils from "./pad_utils";
|
||||||
const padeditor = require('./pad_editor').padeditor;
|
const padeditor = require('./pad_editor').padeditor;
|
||||||
const padsavedrevs = require('./pad_savedrevs');
|
const padsavedrevs = require('./pad_savedrevs');
|
||||||
const _ = require('underscore');
|
const _ = require('underscore');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'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.
|
||||||
|
@ -21,9 +22,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Cookies = require('./pad_utils').Cookies;
|
import padutils,{Cookies} from "./pad_utils";
|
||||||
const padcookie = require('./pad_cookie').padcookie;
|
const padcookie = require('./pad_cookie').padcookie;
|
||||||
const padutils = require('./pad_utils').padutils;
|
|
||||||
const Ace2Editor = require('./ace').Ace2Editor;
|
const Ace2Editor = require('./ace').Ace2Editor;
|
||||||
import html10n from '../js/vendors/html10n'
|
import html10n from '../js/vendors/html10n'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +17,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const padutils = require('./pad_utils').padutils;
|
import padutils from './pad_utils'
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
import html10n from './vendors/html10n';
|
import html10n from './vendors/html10n';
|
||||||
let myUserInfo = {};
|
let myUserInfo = {};
|
|
@ -6,6 +6,8 @@
|
||||||
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
|
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {binarySearch} from "./ace2_common";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
|
@ -22,13 +24,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Security = require('./security');
|
const Security = require('security');
|
||||||
|
import jsCookie, {CookiesStatic} from 'js-cookie'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random String with the given length. Is needed to generate the Author, Group,
|
* Generates a random String with the given length. Is needed to generate the Author, Group,
|
||||||
* readonly, session Ids
|
* readonly, session Ids
|
||||||
*/
|
*/
|
||||||
const randomString = (len) => {
|
export const randomString = (len?: number) => {
|
||||||
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||||
let randomstring = '';
|
let randomstring = '';
|
||||||
len = len || 20;
|
len = len || 20;
|
||||||
|
@ -91,7 +94,35 @@ const urlRegex = (() => {
|
||||||
// https://stackoverflow.com/a/68957976
|
// https://stackoverflow.com/a/68957976
|
||||||
const base64url = /^(?=(?:.{4})*$)[A-Za-z0-9_-]*(?:[AQgw]==|[AEIMQUYcgkosw048]=)?$/;
|
const base64url = /^(?=(?:.{4})*$)[A-Za-z0-9_-]*(?:[AQgw]==|[AEIMQUYcgkosw048]=)?$/;
|
||||||
|
|
||||||
const padutils = {
|
type PadEvent = {
|
||||||
|
which: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type JQueryNode = JQuery<HTMLElement>
|
||||||
|
|
||||||
|
class PadUtils {
|
||||||
|
public urlRegex: RegExp
|
||||||
|
public wordCharRegex: RegExp
|
||||||
|
public warnDeprecatedFlags: {
|
||||||
|
disabledForTestingOnly: boolean,
|
||||||
|
_rl?: {
|
||||||
|
prevs: Map<string, number>,
|
||||||
|
now: () => number,
|
||||||
|
period: number
|
||||||
|
}
|
||||||
|
logger?: any
|
||||||
|
}
|
||||||
|
public globalExceptionHandler: null | any = null;
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.warnDeprecatedFlags = {
|
||||||
|
disabledForTestingOnly: false
|
||||||
|
}
|
||||||
|
this.wordCharRegex = wordCharRegex
|
||||||
|
this.urlRegex = urlRegex
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints a warning message followed by a stack trace (to make it easier to figure out what code
|
* Prints a warning message followed by a stack trace (to make it easier to figure out what code
|
||||||
* is using the deprecated function).
|
* is using the deprecated function).
|
||||||
|
@ -107,41 +138,41 @@ const padutils = {
|
||||||
* @param {...*} args - Passed to `padutils.warnDeprecated.logger.warn` (or `console.warn` if no
|
* @param {...*} args - Passed to `padutils.warnDeprecated.logger.warn` (or `console.warn` if no
|
||||||
* logger is set), with a stack trace appended if available.
|
* logger is set), with a stack trace appended if available.
|
||||||
*/
|
*/
|
||||||
warnDeprecated: (...args) => {
|
warnDeprecated = (...args: any[]) => {
|
||||||
if (padutils.warnDeprecated.disabledForTestingOnly) return;
|
if (this.warnDeprecatedFlags.disabledForTestingOnly) return;
|
||||||
const err = new Error();
|
const err = new Error();
|
||||||
if (Error.captureStackTrace) Error.captureStackTrace(err, padutils.warnDeprecated);
|
if (Error.captureStackTrace) Error.captureStackTrace(err, this.warnDeprecated);
|
||||||
err.name = '';
|
err.name = '';
|
||||||
// Rate limit identical deprecation warnings (as determined by the stack) to avoid log spam.
|
// Rate limit identical deprecation warnings (as determined by the stack) to avoid log spam.
|
||||||
if (typeof err.stack === 'string') {
|
if (typeof err.stack === 'string') {
|
||||||
if (padutils.warnDeprecated._rl == null) {
|
if (this.warnDeprecatedFlags._rl == null) {
|
||||||
padutils.warnDeprecated._rl =
|
this.warnDeprecatedFlags._rl =
|
||||||
{prevs: new Map(), now: () => Date.now(), period: 10 * 60 * 1000};
|
{prevs: new Map(), now: () => Date.now(), period: 10 * 60 * 1000};
|
||||||
}
|
}
|
||||||
const rl = padutils.warnDeprecated._rl;
|
const rl = this.warnDeprecatedFlags._rl;
|
||||||
const now = rl.now();
|
const now = rl.now();
|
||||||
const prev = rl.prevs.get(err.stack);
|
const prev = rl.prevs.get(err.stack);
|
||||||
if (prev != null && now - prev < rl.period) return;
|
if (prev != null && now - prev < rl.period) return;
|
||||||
rl.prevs.set(err.stack, now);
|
rl.prevs.set(err.stack, now);
|
||||||
}
|
}
|
||||||
if (err.stack) args.push(err.stack);
|
if (err.stack) args.push(err.stack);
|
||||||
(padutils.warnDeprecated.logger || console).warn(...args);
|
(this.warnDeprecatedFlags.logger || console).warn(...args);
|
||||||
},
|
}
|
||||||
|
escapeHtml = (x: string) => Security.escapeHTML(String(x))
|
||||||
escapeHtml: (x) => Security.escapeHTML(String(x)),
|
uniqueId = () => {
|
||||||
uniqueId: () => {
|
|
||||||
const pad = require('./pad').pad; // Sidestep circular dependency
|
const pad = require('./pad').pad; // Sidestep circular dependency
|
||||||
// returns string that is exactly 'width' chars, padding with zeros and taking rightmost digits
|
// returns string that is exactly 'width' chars, padding with zeros and taking rightmost digits
|
||||||
const encodeNum =
|
const encodeNum =
|
||||||
(n, width) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
|
(n: number, width: number) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
|
||||||
return [
|
return [
|
||||||
pad.getClientIp(),
|
pad.getClientIp(),
|
||||||
encodeNum(+new Date(), 7),
|
encodeNum(+new Date(), 7),
|
||||||
encodeNum(Math.floor(Math.random() * 1e9), 4),
|
encodeNum(Math.floor(Math.random() * 1e9), 4),
|
||||||
].join('.');
|
].join('.');
|
||||||
},
|
}
|
||||||
|
|
||||||
// e.g. "Thu Jun 18 2009 13:09"
|
// e.g. "Thu Jun 18 2009 13:09"
|
||||||
simpleDateTime: (date) => {
|
simpleDateTime = (date: string) => {
|
||||||
const d = new Date(+date); // accept either number or date
|
const d = new Date(+date); // accept either number or date
|
||||||
const dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()];
|
const dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()];
|
||||||
const month = ([
|
const month = ([
|
||||||
|
@ -162,16 +193,14 @@ const padutils = {
|
||||||
const year = d.getFullYear();
|
const year = d.getFullYear();
|
||||||
const hourmin = `${d.getHours()}:${(`0${d.getMinutes()}`).slice(-2)}`;
|
const hourmin = `${d.getHours()}:${(`0${d.getMinutes()}`).slice(-2)}`;
|
||||||
return `${dayOfWeek} ${month} ${dayOfMonth} ${year} ${hourmin}`;
|
return `${dayOfWeek} ${month} ${dayOfMonth} ${year} ${hourmin}`;
|
||||||
},
|
}
|
||||||
wordCharRegex,
|
|
||||||
urlRegex,
|
|
||||||
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
||||||
findURLs: (text) => {
|
findURLs = (text: string) => {
|
||||||
// Copy padutils.urlRegex so that the use of .exec() below (which mutates the RegExp object)
|
// Copy padutils.urlRegex so that the use of .exec() below (which mutates the RegExp object)
|
||||||
// does not break other concurrent uses of padutils.urlRegex.
|
// does not break other concurrent uses of padutils.urlRegex.
|
||||||
const urlRegex = new RegExp(padutils.urlRegex, 'g');
|
const urlRegex = new RegExp(this.urlRegex, 'g');
|
||||||
urlRegex.lastIndex = 0;
|
urlRegex.lastIndex = 0;
|
||||||
let urls = null;
|
let urls: [number, string][] | null = null;
|
||||||
let execResult;
|
let execResult;
|
||||||
// TODO: Switch to String.prototype.matchAll() after support for Node.js < 12.0.0 is dropped.
|
// TODO: Switch to String.prototype.matchAll() after support for Node.js < 12.0.0 is dropped.
|
||||||
while ((execResult = urlRegex.exec(text))) {
|
while ((execResult = urlRegex.exec(text))) {
|
||||||
|
@ -181,18 +210,19 @@ const padutils = {
|
||||||
urls.push([startIndex, url]);
|
urls.push([startIndex, url]);
|
||||||
}
|
}
|
||||||
return urls;
|
return urls;
|
||||||
},
|
}
|
||||||
escapeHtmlWithClickableLinks: (text, target) => {
|
escapeHtmlWithClickableLinks = (text: string, target: string) => {
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
const pieces = [];
|
const pieces = [];
|
||||||
const urls = padutils.findURLs(text);
|
const urls = this.findURLs(text);
|
||||||
|
|
||||||
const advanceTo = (i) => {
|
const advanceTo = (i: number) => {
|
||||||
if (i > idx) {
|
if (i > idx) {
|
||||||
pieces.push(Security.escapeHTML(text.substring(idx, i)));
|
pieces.push(Security.escapeHTML(text.substring(idx, i)));
|
||||||
idx = i;
|
idx = i;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
;
|
||||||
if (urls) {
|
if (urls) {
|
||||||
for (let j = 0; j < urls.length; j++) {
|
for (let j = 0; j < urls.length; j++) {
|
||||||
const startIndex = urls[j][0];
|
const startIndex = urls[j][0];
|
||||||
|
@ -217,14 +247,14 @@ const padutils = {
|
||||||
}
|
}
|
||||||
advanceTo(text.length);
|
advanceTo(text.length);
|
||||||
return pieces.join('');
|
return pieces.join('');
|
||||||
},
|
}
|
||||||
bindEnterAndEscape: (node, onEnter, onEscape) => {
|
bindEnterAndEscape = (node: JQueryNode, onEnter: Function, onEscape: Function) => {
|
||||||
// Use keypress instead of keyup in bindEnterAndEscape. Keyup event is fired on enter in IME
|
// Use keypress instead of keyup in bindEnterAndEscape. Keyup event is fired on enter in IME
|
||||||
// (Input Method Editor), But keypress is not. So, I changed to use keypress instead of keyup.
|
// (Input Method Editor), But keypress is not. So, I changed to use keypress instead of keyup.
|
||||||
// It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox
|
// It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox
|
||||||
// 3.6.10, Chrome 6.0.472, Safari 5.0).
|
// 3.6.10, Chrome 6.0.472, Safari 5.0).
|
||||||
if (onEnter) {
|
if (onEnter) {
|
||||||
node.on('keypress', (evt) => {
|
node.on('keypress', (evt: { which: number; }) => {
|
||||||
if (evt.which === 13) {
|
if (evt.which === 13) {
|
||||||
onEnter(evt);
|
onEnter(evt);
|
||||||
}
|
}
|
||||||
|
@ -238,13 +268,15 @@ const padutils = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
timediff: (d) => {
|
|
||||||
|
timediff = (d: number) => {
|
||||||
const pad = require('./pad').pad; // Sidestep circular dependency
|
const pad = require('./pad').pad; // Sidestep circular dependency
|
||||||
const format = (n, word) => {
|
const format = (n: number, word: string) => {
|
||||||
n = Math.round(n);
|
n = Math.round(n);
|
||||||
return (`${n} ${word}${n !== 1 ? 's' : ''} ago`);
|
return (`${n} ${word}${n !== 1 ? 's' : ''} ago`);
|
||||||
};
|
}
|
||||||
|
;
|
||||||
d = Math.max(0, (+(new Date()) - (+d) - pad.clientTimeOffset) / 1000);
|
d = Math.max(0, (+(new Date()) - (+d) - pad.clientTimeOffset) / 1000);
|
||||||
if (d < 60) {
|
if (d < 60) {
|
||||||
return format(d, 'second');
|
return format(d, 'second');
|
||||||
|
@ -259,13 +291,14 @@ const padutils = {
|
||||||
}
|
}
|
||||||
d /= 24;
|
d /= 24;
|
||||||
return format(d, 'day');
|
return format(d, 'day');
|
||||||
},
|
}
|
||||||
makeAnimationScheduler: (funcToAnimateOneStep, stepTime, stepsAtOnce) => {
|
makeAnimationScheduler =
|
||||||
|
(funcToAnimateOneStep: any, stepTime: number, stepsAtOnce?: number) => {
|
||||||
if (stepsAtOnce === undefined) {
|
if (stepsAtOnce === undefined) {
|
||||||
stepsAtOnce = 1;
|
stepsAtOnce = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationTimer = null;
|
let animationTimer: any = null;
|
||||||
|
|
||||||
const scheduleAnimation = () => {
|
const scheduleAnimation = () => {
|
||||||
if (!animationTimer) {
|
if (!animationTimer) {
|
||||||
|
@ -285,14 +318,18 @@ const padutils = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return {scheduleAnimation};
|
return {scheduleAnimation};
|
||||||
},
|
}
|
||||||
makeFieldLabeledWhenEmpty: (field, labelText) => {
|
|
||||||
|
makeFieldLabeledWhenEmpty
|
||||||
|
=
|
||||||
|
(field: JQueryNode, labelText: string) => {
|
||||||
field = $(field);
|
field = $(field);
|
||||||
|
|
||||||
const clear = () => {
|
const clear = () => {
|
||||||
field.addClass('editempty');
|
field.addClass('editempty');
|
||||||
field.val(labelText);
|
field.val(labelText);
|
||||||
};
|
}
|
||||||
|
;
|
||||||
field.focus(() => {
|
field.focus(() => {
|
||||||
if (field.hasClass('editempty')) {
|
if (field.hasClass('editempty')) {
|
||||||
field.val('');
|
field.val('');
|
||||||
|
@ -307,30 +344,36 @@ const padutils = {
|
||||||
return {
|
return {
|
||||||
clear,
|
clear,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
getCheckbox: (node) => $(node).is(':checked'),
|
getCheckbox = (node: string) => $(node).is(':checked')
|
||||||
setCheckbox: (node, value) => {
|
setCheckbox =
|
||||||
|
(node: JQueryNode, value: boolean) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
$(node).attr('checked', 'checked');
|
$(node).attr('checked', 'checked');
|
||||||
} else {
|
} else {
|
||||||
$(node).prop('checked', false);
|
$(node).prop('checked', false);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
bindCheckboxChange: (node, func) => {
|
bindCheckboxChange =
|
||||||
$(node).on('change', func);
|
(node: JQueryNode, func: Function) => {
|
||||||
},
|
// @ts-ignore
|
||||||
encodeUserId: (userId) => userId.replace(/[^a-y0-9]/g, (c) => {
|
$(node).on("change", func);
|
||||||
|
}
|
||||||
|
encodeUserId =
|
||||||
|
(userId: string) => userId.replace(/[^a-y0-9]/g, (c) => {
|
||||||
if (c === '.') return '-';
|
if (c === '.') return '-';
|
||||||
return `z${c.charCodeAt(0)}z`;
|
return `z${c.charCodeAt(0)}z`;
|
||||||
}),
|
})
|
||||||
decodeUserId: (encodedUserId) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => {
|
decodeUserId =
|
||||||
if (cc === '-') { return '.'; } else if (cc.charAt(0) === 'z') {
|
(encodedUserId: string) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => {
|
||||||
|
if (cc === '-') {
|
||||||
|
return '.';
|
||||||
|
} else if (cc.charAt(0) === 'z') {
|
||||||
return String.fromCharCode(Number(cc.slice(1, -1)));
|
return String.fromCharCode(Number(cc.slice(1, -1)));
|
||||||
} else {
|
} else {
|
||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether a string has the expected format to be used as a secret token identifying an
|
* Returns whether a string has the expected format to be used as a secret token identifying an
|
||||||
* author. The format is defined as: 't.' followed by a non-empty base64url string (RFC 4648
|
* author. The format is defined as: 't.' followed by a non-empty base64url string (RFC 4648
|
||||||
|
@ -340,23 +383,21 @@ const padutils = {
|
||||||
* conditional transformation of a token to a database key in a way that does not allow a
|
* conditional transformation of a token to a database key in a way that does not allow a
|
||||||
* malicious user to impersonate another user).
|
* malicious user to impersonate another user).
|
||||||
*/
|
*/
|
||||||
isValidAuthorToken: (t) => {
|
isValidAuthorToken = (t: string | object) => {
|
||||||
if (typeof t !== 'string' || !t.startsWith('t.')) return false;
|
if (typeof t !== 'string' || !t.startsWith('t.')) return false;
|
||||||
const v = t.slice(2);
|
const v = t.slice(2);
|
||||||
return v.length > 0 && base64url.test(v);
|
return v.length > 0 && base64url.test(v);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string that can be used in the `token` cookie as a secret that authenticates a
|
* Returns a string that can be used in the `token` cookie as a secret that authenticates a
|
||||||
* particular author.
|
* particular author.
|
||||||
*/
|
*/
|
||||||
generateAuthorToken: () => `t.${randomString()}`,
|
generateAuthorToken = () => `t.${randomString()}`
|
||||||
};
|
setupGlobalExceptionHandler = () => {
|
||||||
|
if (this.globalExceptionHandler == null) {
|
||||||
let globalExceptionHandler = null;
|
this.globalExceptionHandler = (e: any) => {
|
||||||
padutils.setupGlobalExceptionHandler = () => {
|
|
||||||
if (globalExceptionHandler == null) {
|
|
||||||
globalExceptionHandler = (e) => {
|
|
||||||
let type;
|
let type;
|
||||||
let err;
|
let err;
|
||||||
let msg, url, linenumber;
|
let msg, url, linenumber;
|
||||||
|
@ -399,6 +440,7 @@ padutils.setupGlobalExceptionHandler = () => {
|
||||||
.append(txt(`UserAgent: ${navigator.userAgent}`)).append($('<br>')),
|
.append(txt(`UserAgent: ${navigator.userAgent}`)).append($('<br>')),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
$.gritter.add({
|
$.gritter.add({
|
||||||
title: 'An error occurred',
|
title: 'An error occurred',
|
||||||
text: errorMsg,
|
text: errorMsg,
|
||||||
|
@ -423,26 +465,27 @@ padutils.setupGlobalExceptionHandler = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
window.onerror = null; // Clear any pre-existing global error handler.
|
window.onerror = null; // Clear any pre-existing global error handler.
|
||||||
window.addEventListener('error', globalExceptionHandler);
|
window.addEventListener('error', this.globalExceptionHandler);
|
||||||
window.addEventListener('unhandledrejection', globalExceptionHandler);
|
window.addEventListener('unhandledrejection', this.globalExceptionHandler);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
binarySearch = binarySearch
|
||||||
padutils.binarySearch = require('./ace2_common').binarySearch;
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/a/42660748
|
// https://stackoverflow.com/a/42660748
|
||||||
const inThirdPartyIframe = () => {
|
const inThirdPartyIframe = () => {
|
||||||
try {
|
try {
|
||||||
return (!window.top.location.hostname);
|
return (!window.top!.location.hostname);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let Cookies: CookiesStatic<string>
|
||||||
// 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').withAttributes({
|
Cookies = jsCookie.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
|
||||||
|
@ -455,5 +498,5 @@ if (typeof window !== 'undefined') {
|
||||||
secure: window.location.protocol === 'https:',
|
secure: window.location.protocol === 'https:',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.randomString = randomString;
|
|
||||||
exports.padutils = padutils;
|
export default new PadUtils()
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const pluginUtils = require('./shared');
|
const pluginUtils = require('./shared');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const pluginDefs = require('./plugin_defs');
|
const pluginDefs = require('./plugin_defs');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const defs = require('./plugin_defs');
|
const defs = require('./plugin_defs');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
// Provides a require'able version of jQuery without leaking $ and jQuery;
|
// Provides a require'able version of jQuery without leaking $ and jQuery;
|
||||||
window.$ = require('./vendors/jquery');
|
window.$ = require('./vendors/jquery');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Specific hash to display the skin variants builder popup
|
// Specific hash to display the skin variants builder popup
|
|
@ -22,10 +22,24 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const _entryWidth = (e) => (e && e.width) || 0;
|
const _entryWidth = (e: Entry) => (e && e.width) || 0;
|
||||||
|
|
||||||
|
type Entry = {
|
||||||
|
key: string,
|
||||||
|
value?: string
|
||||||
|
width?: number
|
||||||
|
}
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
constructor(entry, levels = 0, downSkips = 1, downSkipWidths = 0) {
|
public key: string|null
|
||||||
|
readonly entry: Entry|null
|
||||||
|
levels: number
|
||||||
|
upPtrs: Node[]
|
||||||
|
downPtrs: Node[]
|
||||||
|
downSkips: number[]
|
||||||
|
readonly downSkipWidths: number[]
|
||||||
|
|
||||||
|
constructor(entry: Entry|null, levels = 0, downSkips: number|null = 1, downSkipWidths:number|null = 0) {
|
||||||
this.key = entry != null ? entry.key : null;
|
this.key = entry != null ? entry.key : null;
|
||||||
this.entry = entry;
|
this.entry = entry;
|
||||||
this.levels = levels;
|
this.levels = levels;
|
||||||
|
@ -37,9 +51,9 @@ class Node {
|
||||||
|
|
||||||
propagateWidthChange() {
|
propagateWidthChange() {
|
||||||
const oldWidth = this.downSkipWidths[0];
|
const oldWidth = this.downSkipWidths[0];
|
||||||
const newWidth = _entryWidth(this.entry);
|
const newWidth = _entryWidth(this.entry!);
|
||||||
const widthChange = newWidth - oldWidth;
|
const widthChange = newWidth - oldWidth;
|
||||||
let n = this;
|
let n: Node = this;
|
||||||
let lvl = 0;
|
let lvl = 0;
|
||||||
while (lvl < n.levels) {
|
while (lvl < n.levels) {
|
||||||
n.downSkipWidths[lvl] += widthChange;
|
n.downSkipWidths[lvl] += widthChange;
|
||||||
|
@ -57,17 +71,23 @@ class Node {
|
||||||
// is still valid and points to the same index in the skiplist. Other operations with other points
|
// is still valid and points to the same index in the skiplist. Other operations with other points
|
||||||
// invalidate this point.
|
// invalidate this point.
|
||||||
class Point {
|
class Point {
|
||||||
constructor(skipList, loc) {
|
private skipList: SkipList
|
||||||
this._skipList = skipList;
|
private readonly loc: number
|
||||||
|
private readonly idxs: number[]
|
||||||
|
private readonly nodes: Node[]
|
||||||
|
private widthSkips: number[]
|
||||||
|
|
||||||
|
constructor(skipList: SkipList, loc: number) {
|
||||||
|
this.skipList = skipList;
|
||||||
this.loc = loc;
|
this.loc = loc;
|
||||||
const numLevels = this._skipList._start.levels;
|
const numLevels = this.skipList.start.levels;
|
||||||
let lvl = numLevels - 1;
|
let lvl = numLevels - 1;
|
||||||
let i = -1;
|
let i = -1;
|
||||||
let ws = 0;
|
let ws = 0;
|
||||||
const nodes = new Array(numLevels);
|
const nodes: Node[] = new Array(numLevels);
|
||||||
const idxs = new Array(numLevels);
|
const idxs: number[] = new Array(numLevels);
|
||||||
const widthSkips = new Array(numLevels);
|
const widthSkips: number[] = new Array(numLevels);
|
||||||
nodes[lvl] = this._skipList._start;
|
nodes[lvl] = this.skipList.start;
|
||||||
idxs[lvl] = -1;
|
idxs[lvl] = -1;
|
||||||
widthSkips[lvl] = 0;
|
widthSkips[lvl] = 0;
|
||||||
while (lvl >= 0) {
|
while (lvl >= 0) {
|
||||||
|
@ -94,9 +114,9 @@ class Point {
|
||||||
return `Point(${this.loc})`;
|
return `Point(${this.loc})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(entry) {
|
insert(entry: Entry) {
|
||||||
if (entry.key == null) throw new Error('entry.key must not be null');
|
if (entry.key == null) throw new Error('entry.key must not be null');
|
||||||
if (this._skipList.containsKey(entry.key)) {
|
if (this.skipList.containsKey(entry.key)) {
|
||||||
throw new Error(`an entry with key ${entry.key} already exists`);
|
throw new Error(`an entry with key ${entry.key} already exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,14 +135,14 @@ class Point {
|
||||||
if (lvl === pNodes.length) {
|
if (lvl === pNodes.length) {
|
||||||
// assume we have just passed the end of this.nodes, and reached one level greater
|
// assume we have just passed the end of this.nodes, and reached one level greater
|
||||||
// than the skiplist currently supports
|
// than the skiplist currently supports
|
||||||
pNodes[lvl] = this._skipList._start;
|
pNodes[lvl] = this.skipList.start;
|
||||||
pIdxs[lvl] = -1;
|
pIdxs[lvl] = -1;
|
||||||
this._skipList._start.levels++;
|
this.skipList.start.levels++;
|
||||||
this._skipList._end.levels++;
|
this.skipList.end.levels++;
|
||||||
this._skipList._start.downPtrs[lvl] = this._skipList._end;
|
this.skipList.start.downPtrs[lvl] = this.skipList.end;
|
||||||
this._skipList._end.upPtrs[lvl] = this._skipList._start;
|
this.skipList.end.upPtrs[lvl] = this.skipList.start;
|
||||||
this._skipList._start.downSkips[lvl] = this._skipList._keyToNodeMap.size + 1;
|
this.skipList.start.downSkips[lvl] = this.skipList.keyToNodeMap.size + 1;
|
||||||
this._skipList._start.downSkipWidths[lvl] = this._skipList._totalWidth;
|
this.skipList.start.downSkipWidths[lvl] = this.skipList._totalWidth;
|
||||||
this.widthSkips[lvl] = 0;
|
this.widthSkips[lvl] = 0;
|
||||||
}
|
}
|
||||||
const me = newNode;
|
const me = newNode;
|
||||||
|
@ -146,13 +166,13 @@ class Point {
|
||||||
up.downSkips[lvl]++;
|
up.downSkips[lvl]++;
|
||||||
up.downSkipWidths[lvl] += newWidth;
|
up.downSkipWidths[lvl] += newWidth;
|
||||||
}
|
}
|
||||||
this._skipList._keyToNodeMap.set(newNode.key, newNode);
|
this.skipList.keyToNodeMap.set(newNode.key as string, newNode);
|
||||||
this._skipList._totalWidth += newWidth;
|
this.skipList._totalWidth += newWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
const elem = this.nodes[0].downPtrs[0];
|
const elem = this.nodes[0].downPtrs[0];
|
||||||
const elemWidth = _entryWidth(elem.entry);
|
const elemWidth = _entryWidth(elem.entry!);
|
||||||
for (let i = 0; i < this.nodes.length; i++) {
|
for (let i = 0; i < this.nodes.length; i++) {
|
||||||
if (i < elem.levels) {
|
if (i < elem.levels) {
|
||||||
const up = elem.upPtrs[i];
|
const up = elem.upPtrs[i];
|
||||||
|
@ -169,8 +189,8 @@ class Point {
|
||||||
up.downSkipWidths[i] -= elemWidth;
|
up.downSkipWidths[i] -= elemWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._skipList._keyToNodeMap.delete(elem.key);
|
this.skipList.keyToNodeMap.delete(elem.key as string);
|
||||||
this._skipList._totalWidth -= elemWidth;
|
this.skipList._totalWidth -= elemWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNode() {
|
getNode() {
|
||||||
|
@ -183,20 +203,26 @@ class Point {
|
||||||
* property that is a string.
|
* property that is a string.
|
||||||
*/
|
*/
|
||||||
class SkipList {
|
class SkipList {
|
||||||
|
start: Node
|
||||||
|
end: Node
|
||||||
|
_totalWidth: number
|
||||||
|
keyToNodeMap: Map<string, Node>
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// if there are N elements in the skiplist, "start" is element -1 and "end" is element N
|
// if there are N elements in the skiplist, "start" is element -1 and "end" is element N
|
||||||
this._start = new Node(null, 1);
|
this.start = new Node(null, 1);
|
||||||
this._end = new Node(null, 1, null, null);
|
this.end = new Node(null, 1, null, null);
|
||||||
this._totalWidth = 0;
|
this._totalWidth = 0;
|
||||||
this._keyToNodeMap = new Map();
|
this.keyToNodeMap = new Map();
|
||||||
this._start.downPtrs[0] = this._end;
|
this.start.downPtrs[0] = this.end;
|
||||||
this._end.upPtrs[0] = this._start;
|
this.end.upPtrs[0] = this.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNodeAtOffset(targetOffset) {
|
_getNodeAtOffset(targetOffset: number) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let n = this._start;
|
let n = this.start;
|
||||||
let lvl = this._start.levels - 1;
|
let lvl = this.start.levels - 1;
|
||||||
while (lvl >= 0 && n.downPtrs[lvl]) {
|
while (lvl >= 0 && n.downPtrs[lvl]) {
|
||||||
while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset)) {
|
while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset)) {
|
||||||
i += n.downSkipWidths[lvl];
|
i += n.downSkipWidths[lvl];
|
||||||
|
@ -204,17 +230,17 @@ class SkipList {
|
||||||
}
|
}
|
||||||
lvl--;
|
lvl--;
|
||||||
}
|
}
|
||||||
if (n === this._start) return (this._start.downPtrs[0] || null);
|
if (n === this.start) return (this.start.downPtrs[0] || null);
|
||||||
if (n === this._end) {
|
if (n === this.end) {
|
||||||
return targetOffset === this._totalWidth ? (this._end.upPtrs[0] || null) : null;
|
return targetOffset === this._totalWidth ? (this.end.upPtrs[0] || null) : null;
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getNodeIndex(node, byWidth) {
|
_getNodeIndex(node: Node, byWidth?: boolean) {
|
||||||
let dist = (byWidth ? 0 : -1);
|
let dist = (byWidth ? 0 : -1);
|
||||||
let n = node;
|
let n = node;
|
||||||
while (n !== this._start) {
|
while (n !== this.start) {
|
||||||
const lvl = n.levels - 1;
|
const lvl = n.levels - 1;
|
||||||
n = n.upPtrs[lvl];
|
n = n.upPtrs[lvl];
|
||||||
if (byWidth) dist += n.downSkipWidths[lvl];
|
if (byWidth) dist += n.downSkipWidths[lvl];
|
||||||
|
@ -223,17 +249,19 @@ class SkipList {
|
||||||
return dist;
|
return dist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalWidth() { return this._totalWidth; }
|
||||||
|
|
||||||
// Returns index of first entry such that entryFunc(entry) is truthy,
|
// Returns index of first entry such that entryFunc(entry) is truthy,
|
||||||
// or length() if no such entry. Assumes all falsy entries come before
|
// or length() if no such entry. Assumes all falsy entries come before
|
||||||
// all truthy entries.
|
// all truthy entries.
|
||||||
search(entryFunc) {
|
search(entryFunc: Function) {
|
||||||
let low = this._start;
|
let low = this.start;
|
||||||
let lvl = this._start.levels - 1;
|
let lvl = this.start.levels - 1;
|
||||||
let lowIndex = -1;
|
let lowIndex = -1;
|
||||||
|
|
||||||
const f = (node) => {
|
const f = (node: Node) => {
|
||||||
if (node === this._start) return false;
|
if (node === this.start) return false;
|
||||||
else if (node === this._end) return true;
|
else if (node === this.end) return true;
|
||||||
else return entryFunc(node.entry);
|
else return entryFunc(node.entry);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,20 +277,20 @@ class SkipList {
|
||||||
return lowIndex + 1;
|
return lowIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
length() { return this._keyToNodeMap.size; }
|
length() { return this.keyToNodeMap.size; }
|
||||||
|
|
||||||
atIndex(i) {
|
atIndex(i: number) {
|
||||||
if (i < 0) console.warn(`atIndex(${i})`);
|
if (i < 0) console.warn(`atIndex(${i})`);
|
||||||
if (i >= this._keyToNodeMap.size) console.warn(`atIndex(${i}>=${this._keyToNodeMap.size})`);
|
if (i >= this.keyToNodeMap.size) console.warn(`atIndex(${i}>=${this.keyToNodeMap.size})`);
|
||||||
return (new Point(this, i)).getNode().entry;
|
return (new Point(this, i)).getNode().entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// differs from Array.splice() in that new elements are in an array, not varargs
|
// differs from Array.splice() in that new elements are in an array, not varargs
|
||||||
splice(start, deleteCount, newEntryArray) {
|
splice(start: number, deleteCount: number, newEntryArray: Entry[]) {
|
||||||
if (start < 0) console.warn(`splice(${start}, ...)`);
|
if (start < 0) console.warn(`splice(${start}, ...)`);
|
||||||
if (start + deleteCount > this._keyToNodeMap.size) {
|
if (start + deleteCount > this.keyToNodeMap.size) {
|
||||||
console.warn(`splice(${start}, ${deleteCount}, ...), N=${this._keyToNodeMap.size}`);
|
console.warn(`splice(${start}, ${deleteCount}, ...), N=${this.keyToNodeMap.size}`);
|
||||||
console.warn('%s %s %s', typeof start, typeof deleteCount, typeof this._keyToNodeMap.size);
|
console.warn('%s %s %s', typeof start, typeof deleteCount, typeof this.keyToNodeMap.size);
|
||||||
console.trace();
|
console.trace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,56 +303,55 @@ class SkipList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next(entry) { return this._keyToNodeMap.get(entry.key).downPtrs[0].entry || null; }
|
next(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.downPtrs[0].entry || null; }
|
||||||
prev(entry) { return this._keyToNodeMap.get(entry.key).upPtrs[0].entry || null; }
|
prev(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.upPtrs[0].entry || null; }
|
||||||
push(entry) { this.splice(this._keyToNodeMap.size, 0, [entry]); }
|
push(entry: Entry) { this.splice(this.keyToNodeMap.size, 0, [entry]); }
|
||||||
|
|
||||||
slice(start, end) {
|
slice(start: number, end: number) {
|
||||||
// act like Array.slice()
|
// act like Array.slice()
|
||||||
if (start === undefined) start = 0;
|
if (start === undefined) start = 0;
|
||||||
else if (start < 0) start += this._keyToNodeMap.size;
|
else if (start < 0) start += this.keyToNodeMap.size;
|
||||||
if (end === undefined) end = this._keyToNodeMap.size;
|
if (end === undefined) end = this.keyToNodeMap.size;
|
||||||
else if (end < 0) end += this._keyToNodeMap.size;
|
else if (end < 0) end += this.keyToNodeMap.size;
|
||||||
|
|
||||||
if (start < 0) start = 0;
|
if (start < 0) start = 0;
|
||||||
if (start > this._keyToNodeMap.size) start = this._keyToNodeMap.size;
|
if (start > this.keyToNodeMap.size) start = this.keyToNodeMap.size;
|
||||||
if (end < 0) end = 0;
|
if (end < 0) end = 0;
|
||||||
if (end > this._keyToNodeMap.size) end = this._keyToNodeMap.size;
|
if (end > this.keyToNodeMap.size) end = this.keyToNodeMap.size;
|
||||||
|
|
||||||
if (end <= start) return [];
|
if (end <= start) return [];
|
||||||
let n = this.atIndex(start);
|
let n = this.atIndex(start);
|
||||||
const array = [n];
|
const array = [n];
|
||||||
for (let i = 1; i < (end - start); i++) {
|
for (let i = 1; i < (end - start); i++) {
|
||||||
n = this.next(n);
|
n = this.next(n!);
|
||||||
array.push(n);
|
array.push(n);
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
atKey(key) { return this._keyToNodeMap.get(key).entry; }
|
atKey(key: string) { return this.keyToNodeMap.get(key)!.entry; }
|
||||||
indexOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key)); }
|
indexOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!); }
|
||||||
indexOfEntry(entry) { return this.indexOfKey(entry.key); }
|
indexOfEntry(entry: Entry) { return this.indexOfKey(entry.key); }
|
||||||
containsKey(key) { return this._keyToNodeMap.has(key); }
|
containsKey(key: string) { return this.keyToNodeMap.has(key); }
|
||||||
// gets the last entry starting at or before the offset
|
// gets the last entry starting at or before the offset
|
||||||
atOffset(offset) { return this._getNodeAtOffset(offset).entry; }
|
atOffset(offset: number) { return this._getNodeAtOffset(offset)!.entry; }
|
||||||
keyAtOffset(offset) { return this.atOffset(offset).key; }
|
keyAtOffset(offset: number) { return this.atOffset(offset)!.key; }
|
||||||
offsetOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key), true); }
|
offsetOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!, true); }
|
||||||
offsetOfEntry(entry) { return this.offsetOfKey(entry.key); }
|
offsetOfEntry(entry: Entry) { return this.offsetOfKey(entry.key); }
|
||||||
setEntryWidth(entry, width) {
|
setEntryWidth(entry: Entry, width: number) {
|
||||||
entry.width = width;
|
entry.width = width;
|
||||||
this._totalWidth += this._keyToNodeMap.get(entry.key).propagateWidthChange();
|
this._totalWidth += this.keyToNodeMap.get(entry.key)!.propagateWidthChange();
|
||||||
}
|
}
|
||||||
totalWidth() { return this._totalWidth; }
|
offsetOfIndex(i: number) {
|
||||||
offsetOfIndex(i) {
|
|
||||||
if (i < 0) return 0;
|
if (i < 0) return 0;
|
||||||
if (i >= this._keyToNodeMap.size) return this._totalWidth;
|
if (i >= this.keyToNodeMap.size) return this._totalWidth;
|
||||||
return this.offsetOfEntry(this.atIndex(i));
|
return this.offsetOfEntry(this.atIndex(i)!);
|
||||||
}
|
}
|
||||||
indexOfOffset(offset) {
|
indexOfOffset(offset: number) {
|
||||||
if (offset <= 0) return 0;
|
if (offset <= 0) return 0;
|
||||||
if (offset >= this._totalWidth) return this._keyToNodeMap.size;
|
if (offset >= this._totalWidth) return this.keyToNodeMap.size;
|
||||||
return this.indexOfEntry(this.atOffset(offset));
|
return this.indexOfEntry(this.atOffset(offset)!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = SkipList;
|
export default SkipList
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,10 +27,9 @@
|
||||||
// assigns to the global `$` and augments it with plugins.
|
// assigns to the global `$` and augments it with plugins.
|
||||||
require('./vendors/jquery');
|
require('./vendors/jquery');
|
||||||
|
|
||||||
const Cookies = require('./pad_utils').Cookies;
|
import {randomString, Cookies} from "./pad_utils";
|
||||||
const randomString = require('./pad_utils').randomString;
|
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
const padutils = require('./pad_utils').padutils;
|
import padutils from './pad_utils'
|
||||||
const socketio = require('./socketio');
|
const socketio = require('./socketio');
|
||||||
import html10n from '../js/vendors/html10n'
|
import html10n from '../js/vendors/html10n'
|
||||||
let token, padId, exportLinks, socket, changesetLoader, BroadcastSlider;
|
let token, padId, exportLinks, socket, changesetLoader, BroadcastSlider;
|
4
src/static/js/types/AText.ts
Normal file
4
src/static/js/types/AText.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export type AText = {
|
||||||
|
text: string,
|
||||||
|
attribs: string,
|
||||||
|
}
|
1
src/static/js/types/Attribute.ts
Normal file
1
src/static/js/types/Attribute.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export type Attribute = [string, string]
|
7
src/static/js/types/PadRevision.ts
Normal file
7
src/static/js/types/PadRevision.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export type PadRevision = {
|
||||||
|
revNum: number;
|
||||||
|
savedById: string;
|
||||||
|
label: string;
|
||||||
|
timestamp: number;
|
||||||
|
id: string;
|
||||||
|
}
|
317
src/static/js/types/SocketIOMessage.ts
Normal file
317
src/static/js/types/SocketIOMessage.ts
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
import {MapArrayType} from "../../../node/types/MapType";
|
||||||
|
import {AText} from "./AText";
|
||||||
|
import AttributePool from "../AttributePool";
|
||||||
|
import attributePool from "../AttributePool";
|
||||||
|
import ChatMessage from "../ChatMessage";
|
||||||
|
import {PadRevision} from "./PadRevision";
|
||||||
|
|
||||||
|
export type Part = {
|
||||||
|
name: string,
|
||||||
|
client_hooks: MapArrayType<string>,
|
||||||
|
hooks: MapArrayType<string>
|
||||||
|
pre?: string[]
|
||||||
|
post?: string[]
|
||||||
|
plugin?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type MappedPlugin = Part& {
|
||||||
|
plugin: string
|
||||||
|
full_name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SocketIOMessage = {
|
||||||
|
type: string
|
||||||
|
accessStatus: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HistoricalAuthorData = MapArrayType<{
|
||||||
|
name: string;
|
||||||
|
colorId: number;
|
||||||
|
userId?: string
|
||||||
|
}>
|
||||||
|
|
||||||
|
export type ServerVar = {
|
||||||
|
rev: number
|
||||||
|
clientIp: string
|
||||||
|
padId: string
|
||||||
|
historicalAuthorData?: HistoricalAuthorData,
|
||||||
|
initialAttributedText: {
|
||||||
|
attribs: string
|
||||||
|
text: string
|
||||||
|
},
|
||||||
|
apool: AttributePoolWire
|
||||||
|
time: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AttributePoolWire = {numToAttrib: {[p: number]: [string, string]}, nextNum: number}
|
||||||
|
|
||||||
|
|
||||||
|
export type UserInfo = {
|
||||||
|
userId: string
|
||||||
|
colorId: string,
|
||||||
|
name: string|null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientVarPayload = {
|
||||||
|
readOnlyId: string
|
||||||
|
automaticReconnectionTimeout: number
|
||||||
|
sessionRefreshInterval: number,
|
||||||
|
atext?: AText,
|
||||||
|
apool?: AttributePool,
|
||||||
|
userName?: string,
|
||||||
|
userColor: number,
|
||||||
|
hideChat?: boolean,
|
||||||
|
padOptions: PadOption,
|
||||||
|
padId: string,
|
||||||
|
clientIp: string,
|
||||||
|
colorPalette: string[],
|
||||||
|
accountPrivs: {
|
||||||
|
maxRevisions: number,
|
||||||
|
},
|
||||||
|
collab_client_vars: ServerVar,
|
||||||
|
chatHead: number,
|
||||||
|
readonly: boolean,
|
||||||
|
serverTimestamp: number,
|
||||||
|
initialOptions: MapArrayType<string>,
|
||||||
|
userId: string,
|
||||||
|
mode: string,
|
||||||
|
randomVersionString: string,
|
||||||
|
skinName: string
|
||||||
|
skinVariants: string,
|
||||||
|
exportAvailable: string
|
||||||
|
savedRevisions: PadRevision[],
|
||||||
|
initialRevisionList: number[],
|
||||||
|
padShortcutEnabled: MapArrayType<boolean>,
|
||||||
|
initialTitle: string,
|
||||||
|
opts: {}
|
||||||
|
numConnectedUsers: number
|
||||||
|
abiwordAvailable: string
|
||||||
|
sofficeAvailable: string
|
||||||
|
plugins: {
|
||||||
|
plugins: MapArrayType<any>
|
||||||
|
parts: MappedPlugin[]
|
||||||
|
}
|
||||||
|
indentationOnNewLine: boolean
|
||||||
|
scrollWhenFocusLineIsOutOfViewport : {
|
||||||
|
percentage: {
|
||||||
|
editionAboveViewport: number,
|
||||||
|
editionBelowViewport: number
|
||||||
|
}
|
||||||
|
duration: number
|
||||||
|
scrollWhenCaretIsInTheLastLineOfViewport: boolean
|
||||||
|
percentageToScrollWhenUserPressesArrowUp: number
|
||||||
|
}
|
||||||
|
initialChangesets: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientVarData = {
|
||||||
|
type: "CLIENT_VARS"
|
||||||
|
data: ClientVarPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientNewChanges = {
|
||||||
|
type : 'NEW_CHANGES'
|
||||||
|
apool: AttributePool,
|
||||||
|
author: string,
|
||||||
|
changeset: string,
|
||||||
|
newRev: number,
|
||||||
|
payload?: ClientNewChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientAcceptCommitMessage = {
|
||||||
|
type: 'ACCEPT_COMMIT'
|
||||||
|
newRev: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientConnectMessage = {
|
||||||
|
type: 'CLIENT_RECONNECT',
|
||||||
|
noChanges: boolean,
|
||||||
|
headRev: number,
|
||||||
|
newRev: number,
|
||||||
|
changeset: string,
|
||||||
|
author: string
|
||||||
|
apool: AttributePool
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type UserNewInfoMessage = {
|
||||||
|
type: 'USER_NEWINFO',
|
||||||
|
data: {
|
||||||
|
userInfo: UserInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserLeaveMessage = {
|
||||||
|
type: 'USER_LEAVE'
|
||||||
|
userInfo: UserInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type ClientMessageMessage = {
|
||||||
|
type: 'CLIENT_MESSAGE',
|
||||||
|
payload: ClientSendMessages
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChatMessageMessage = {
|
||||||
|
type: 'CHAT_MESSAGE'
|
||||||
|
data: {
|
||||||
|
message: ChatMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChatMessageMessages = {
|
||||||
|
type: 'CHAT_MESSAGES'
|
||||||
|
messages: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientUserChangesMessage = {
|
||||||
|
type: 'USER_CHANGES',
|
||||||
|
baseRev: number,
|
||||||
|
changeset: string,
|
||||||
|
apool: attributePool
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type ClientSendMessages = ClientUserChangesMessage |ClientReadyMessage| ClientSendUserInfoUpdate|ChatMessageMessage| ClientMessageMessage | GetChatMessageMessage |ClientSuggestUserName | NewRevisionListMessage | RevisionLabel | PadOptionsMessage| ClientSaveRevisionMessage
|
||||||
|
|
||||||
|
export type ClientReadyMessage = {
|
||||||
|
type: 'CLIENT_READY',
|
||||||
|
component: string,
|
||||||
|
padId: string,
|
||||||
|
sessionID: string,
|
||||||
|
token: string,
|
||||||
|
userInfo: UserInfo,
|
||||||
|
reconnect?: boolean
|
||||||
|
client_rev?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientSaveRevisionMessage = {
|
||||||
|
type: 'SAVE_REVISION'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetChatMessageMessage = {
|
||||||
|
type: 'GET_CHAT_MESSAGES',
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientSendUserInfoUpdate = {
|
||||||
|
type: 'USERINFO_UPDATE',
|
||||||
|
userInfo: UserInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientSuggestUserName = {
|
||||||
|
type: 'suggestUserName',
|
||||||
|
data: {
|
||||||
|
payload: {
|
||||||
|
unnamedId: string,
|
||||||
|
newName: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NewRevisionListMessage = {
|
||||||
|
type: 'newRevisionList',
|
||||||
|
revisionList: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RevisionLabel = {
|
||||||
|
type: 'revisionLabel'
|
||||||
|
revisionList: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PadOptionsMessage = {
|
||||||
|
type: 'padoptions'
|
||||||
|
options: PadOption
|
||||||
|
changedBy: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PadOption = {
|
||||||
|
"noColors"?: boolean,
|
||||||
|
"showControls"?: boolean,
|
||||||
|
"showChat"?: boolean,
|
||||||
|
"showLineNumbers"?: boolean,
|
||||||
|
"useMonospaceFont"?: boolean,
|
||||||
|
"userName"?: null|string,
|
||||||
|
"userColor"?: null|string,
|
||||||
|
"rtl"?: boolean,
|
||||||
|
"alwaysShowChat"?: boolean,
|
||||||
|
"chatAndUsers"?: boolean,
|
||||||
|
"lang"?: null|string,
|
||||||
|
view? : MapArrayType<boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type SharedMessageType = {
|
||||||
|
payload:{
|
||||||
|
timestamp: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type x = {
|
||||||
|
disconnect: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientDisconnectedMessage = {
|
||||||
|
type: "disconnected"
|
||||||
|
disconnected: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserChanges = {
|
||||||
|
data: ClientUserChangesMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserSuggestUserName = {
|
||||||
|
data: {
|
||||||
|
payload: ClientSuggestUserName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ChangesetRequestMessage = {
|
||||||
|
type: 'CHANGESET_REQ'
|
||||||
|
data: {
|
||||||
|
granularity: number
|
||||||
|
start: number
|
||||||
|
requestID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type CollabroomMessage = {
|
||||||
|
type: 'COLLABROOM'
|
||||||
|
data: ClientSendUserInfoUpdate | ClientUserChangesMessage | ChatMessageMessage | GetChatMessageMessage | ClientSaveRevisionMessage | ClientMessageMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientVarMessage = | ClientVarData | ClientDisconnectedMessage | ClientReadyMessage| ChangesetRequestMessage | CollabroomMessage | CustomMessage
|
||||||
|
|
||||||
|
|
||||||
|
export type CustomMessage = {
|
||||||
|
type: 'CUSTOM'
|
||||||
|
data: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientCustomMessage = {
|
||||||
|
type: 'CUSTOM',
|
||||||
|
action: string,
|
||||||
|
payload: any
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SocketClientReadyMessage = {
|
||||||
|
type: string
|
||||||
|
component: string
|
||||||
|
padId: string
|
||||||
|
sessionID: string
|
||||||
|
token: string
|
||||||
|
userInfo: {
|
||||||
|
colorId: string|null
|
||||||
|
name: string|null
|
||||||
|
},
|
||||||
|
reconnect?: boolean
|
||||||
|
client_rev?: number
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = require('underscore');
|
module.exports = require('underscore');
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
// WARNING: This file may have been modified from original.
|
// WARNING: This file may have been modified from original.
|
||||||
// TODO: Check requirement of this file, this afaik was to cover weird edge cases
|
// TODO: Check requirement of this file, this afaik was to cover weird edge cases
|
||||||
// that have probably been fixed in browsers.
|
// that have probably been fixed in browsers.
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
// WARNING: This file has been modified from original.
|
// WARNING: This file has been modified from original.
|
||||||
// TODO: Replace with https://github.com/Simonwep/pickr
|
// TODO: Replace with https://github.com/Simonwep/pickr
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
// WARNING: This file has been modified from the Original
|
// WARNING: This file has been modified from the Original
|
||||||
|
|
||||||
/*
|
/*
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
/*!
|
/*!
|
||||||
* jQuery JavaScript Library v3.7.1
|
* jQuery JavaScript Library v3.7.1
|
||||||
* https://jquery.com/
|
* https://jquery.com/
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
// WARNING: This file has been modified from the Original
|
// WARNING: This file has been modified from the Original
|
||||||
// TODO: Nice Select seems relatively abandoned, we should consider other options.
|
// TODO: Nice Select seems relatively abandoned, we should consider other options.
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
for the JavaScript code in this page.|
|
for the JavaScript code in this page.|
|
||||||
*/
|
*/
|
||||||
</script>
|
</script>
|
||||||
<script src="../../static/js/basic_error_handler.js?v=<%=settings.randomVersionString%>"></script>
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<meta name="robots" content="noindex, nofollow">
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const AttributeMap = require('../../../static/js/AttributeMap.js');
|
import AttributeMap from '../../../static/js/AttributeMap';
|
||||||
const AttributePool = require('../../../static/js/AttributePool');
|
import AttributePool from '../../../static/js/AttributePool';
|
||||||
const attributes = require('../../../static/js/attributes');
|
import attributes from '../../../static/js/attributes';
|
||||||
import {expect, describe, it, beforeEach} from 'vitest'
|
import {expect, describe, it, beforeEach} from 'vitest'
|
||||||
|
import {Attribute} from "../../../static/js/types/Attribute";
|
||||||
|
|
||||||
describe('AttributeMap', function () {
|
describe('AttributeMap', function () {
|
||||||
const attribs = [
|
const attribs: Attribute[] = [
|
||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
['baz', 'bif'],
|
['baz', 'bif'],
|
||||||
['emptyValue', ''],
|
['emptyValue', ''],
|
||||||
];
|
];
|
||||||
let pool: { eachAttrib: (arg0: () => number) => void; putAttrib: (arg0: string[]) => any; getAttrib: (arg0: number) => any; };
|
let pool: AttributePool;
|
||||||
|
|
||||||
const getPoolSize = () => {
|
const getPoolSize = () => {
|
||||||
let n = 0;
|
let n = 0;
|
||||||
|
@ -70,12 +71,14 @@ describe('AttributeMap', function () {
|
||||||
describe(desc as string, function () {
|
describe(desc as string, function () {
|
||||||
it('key is coerced to string', async function () {
|
it('key is coerced to string', async function () {
|
||||||
const m = new AttributeMap(pool);
|
const m = new AttributeMap(pool);
|
||||||
|
// @ts-ignore
|
||||||
m.set(input, 'value');
|
m.set(input, 'value');
|
||||||
expect(m.get(want)).to.equal('value');
|
expect(m.get(want)).to.equal('value');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('value is coerced to string', async function () {
|
it('value is coerced to string', async function () {
|
||||||
const m = new AttributeMap(pool);
|
const m = new AttributeMap(pool);
|
||||||
|
// @ts-ignore
|
||||||
m.set('key', input);
|
m.set('key', input);
|
||||||
expect(m.get('key')).to.equal(want);
|
expect(m.get('key')).to.equal(want);
|
||||||
});
|
});
|
||||||
|
@ -122,6 +125,7 @@ describe('AttributeMap', function () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
args[0] = attributes.attribsToString(attributes.sort([...args[0]]), pool);
|
args[0] = attributes.attribsToString(attributes.sort([...args[0]]), pool);
|
||||||
}
|
}
|
||||||
|
// @ts-ignore
|
||||||
return AttributeMap.prototype[funcName].call(m, ...args);
|
return AttributeMap.prototype[funcName].call(m, ...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
import {APool} from "../../../node/types/PadType";
|
import {APool} from "../../../node/types/PadType";
|
||||||
|
|
||||||
const AttributePool = require('../../../static/js/AttributePool');
|
import AttributePool from '../../../static/js/AttributePool';
|
||||||
const attributes = require('../../../static/js/attributes');
|
import attributes from '../../../static/js/attributes';
|
||||||
|
|
||||||
import {expect, describe, it, beforeEach} from 'vitest';
|
import {expect, describe, it, beforeEach} from 'vitest';
|
||||||
|
import {Attribute} from "../../../static/js/types/Attribute";
|
||||||
|
|
||||||
describe('attributes', function () {
|
describe('attributes', function () {
|
||||||
const attribs = [['foo', 'bar'], ['baz', 'bif']];
|
const attribs: Attribute[] = [['foo', 'bar'], ['baz', 'bif']];
|
||||||
let pool: APool;
|
let pool: AttributePool;
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
pool = new AttributePool();
|
pool = new AttributePool();
|
||||||
|
@ -45,6 +46,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const got = [...attributes.decodeAttribString(input)];
|
const got = [...attributes.decodeAttribString(input)];
|
||||||
expect(JSON.stringify(got)).to.equal(JSON.stringify(want));
|
expect(JSON.stringify(got)).to.equal(JSON.stringify(want));
|
||||||
});
|
});
|
||||||
|
@ -61,6 +63,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [desc, input] of testCases) {
|
for (const [desc, input] of testCases) {
|
||||||
it(desc as string, async function () {
|
it(desc as string, async function () {
|
||||||
|
// @ts-ignore
|
||||||
expect(attributes.encodeAttribString(input)).to.equal('*0*1');
|
expect(attributes.encodeAttribString(input)).to.equal('*0*1');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -78,6 +81,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, wantErr] of testCases) {
|
for (const [input, wantErr] of testCases) {
|
||||||
it(JSON.stringify(input), async function () {
|
it(JSON.stringify(input), async function () {
|
||||||
|
// @ts-ignore
|
||||||
expect(() => attributes.encodeAttribString(input)).toThrowError(wantErr as RegExp);
|
expect(() => attributes.encodeAttribString(input)).toThrowError(wantErr as RegExp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -97,6 +101,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
expect(attributes.encodeAttribString(input)).to.equal(want);
|
expect(attributes.encodeAttribString(input)).to.equal(want);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -117,6 +122,7 @@ describe('attributes', function () {
|
||||||
|
|
||||||
for (const [desc, input] of testCases) {
|
for (const [desc, input] of testCases) {
|
||||||
it(desc as string, async function () {
|
it(desc as string, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const gotAttribs = [...attributes.attribsFromNums(input, pool)];
|
const gotAttribs = [...attributes.attribsFromNums(input, pool)];
|
||||||
expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(attribs));
|
expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(attribs));
|
||||||
});
|
});
|
||||||
|
@ -136,6 +142,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, wantErr] of testCases) {
|
for (const [input, wantErr] of testCases) {
|
||||||
it(JSON.stringify(input), async function () {
|
it(JSON.stringify(input), async function () {
|
||||||
|
// @ts-ignore
|
||||||
expect(() => [...attributes.attribsFromNums(input, pool)]).toThrowError(wantErr as RegExp);
|
expect(() => [...attributes.attribsFromNums(input, pool)]).toThrowError(wantErr as RegExp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -151,6 +158,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const gotAttribs = [...attributes.attribsFromNums(input, pool)];
|
const gotAttribs = [...attributes.attribsFromNums(input, pool)];
|
||||||
expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want));
|
expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want));
|
||||||
});
|
});
|
||||||
|
@ -172,6 +180,7 @@ describe('attributes', function () {
|
||||||
|
|
||||||
for (const [desc, input] of testCases) {
|
for (const [desc, input] of testCases) {
|
||||||
it(desc as string, async function () {
|
it(desc as string, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const gotNums = [...attributes.attribsToNums(input, pool)];
|
const gotNums = [...attributes.attribsToNums(input, pool)];
|
||||||
expect(JSON.stringify(gotNums)).to.equal(JSON.stringify([0, 1]));
|
expect(JSON.stringify(gotNums)).to.equal(JSON.stringify([0, 1]));
|
||||||
});
|
});
|
||||||
|
@ -182,6 +191,7 @@ describe('attributes', function () {
|
||||||
const testCases = [null, [null]];
|
const testCases = [null, [null]];
|
||||||
for (const input of testCases) {
|
for (const input of testCases) {
|
||||||
it(JSON.stringify(input), async function () {
|
it(JSON.stringify(input), async function () {
|
||||||
|
// @ts-ignore
|
||||||
expect(() => [...attributes.attribsToNums(input, pool)]).toThrowError();
|
expect(() => [...attributes.attribsToNums(input, pool)]).toThrowError();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -197,6 +207,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const got = [...attributes.attribsToNums(input, pool)];
|
const got = [...attributes.attribsToNums(input, pool)];
|
||||||
expect(JSON.stringify(got)).to.equal(JSON.stringify(want));
|
expect(JSON.stringify(got)).to.equal(JSON.stringify(want));
|
||||||
});
|
});
|
||||||
|
@ -211,6 +222,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const got = [...attributes.attribsToNums(input, pool)];
|
const got = [...attributes.attribsToNums(input, pool)];
|
||||||
expect(JSON.stringify(got)).to.equal(JSON.stringify(want));
|
expect(JSON.stringify(got)).to.equal(JSON.stringify(want));
|
||||||
expect(JSON.stringify(pool.getAttrib(attribs.length)))
|
expect(JSON.stringify(pool.getAttrib(attribs.length)))
|
||||||
|
@ -234,6 +246,7 @@ describe('attributes', function () {
|
||||||
['value is coerced to string', [['key', inputVal]], [['key', wantVal]]],
|
['value is coerced to string', [['key', inputVal]], [['key', wantVal]]],
|
||||||
]) {
|
]) {
|
||||||
it(desc as string, async function () {
|
it(desc as string, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const gotNums = [...attributes.attribsToNums(inputAttribs, pool)];
|
const gotNums = [...attributes.attribsToNums(inputAttribs, pool)];
|
||||||
// Each attrib in inputAttribs is expected to be new to the pool.
|
// Each attrib in inputAttribs is expected to be new to the pool.
|
||||||
const wantNums = [...Array(attribs.length + 1).keys()].slice(attribs.length);
|
const wantNums = [...Array(attribs.length + 1).keys()].slice(attribs.length);
|
||||||
|
@ -265,6 +278,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, wantErr] of testCases) {
|
for (const [input, wantErr] of testCases) {
|
||||||
it(JSON.stringify(input), async function () {
|
it(JSON.stringify(input), async function () {
|
||||||
|
// @ts-ignore
|
||||||
expect(() => [...attributes.attribsFromString(input, pool)]).toThrowError(wantErr);
|
expect(() => [...attributes.attribsFromString(input, pool)]).toThrowError(wantErr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -280,6 +294,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const gotAttribs = [...attributes.attribsFromString(input, pool)];
|
const gotAttribs = [...attributes.attribsFromString(input, pool)];
|
||||||
expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want));
|
expect(JSON.stringify(gotAttribs)).to.equal(JSON.stringify(want));
|
||||||
});
|
});
|
||||||
|
@ -297,6 +312,7 @@ describe('attributes', function () {
|
||||||
|
|
||||||
for (const [desc, input] of testCases) {
|
for (const [desc, input] of testCases) {
|
||||||
it(desc as string, async function () {
|
it(desc as string, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const got = attributes.attribsToString(input, pool);
|
const got = attributes.attribsToString(input, pool);
|
||||||
expect(got).to.equal('*0*1');
|
expect(got).to.equal('*0*1');
|
||||||
});
|
});
|
||||||
|
@ -307,6 +323,7 @@ describe('attributes', function () {
|
||||||
const testCases = [null, [null]];
|
const testCases = [null, [null]];
|
||||||
for (const input of testCases) {
|
for (const input of testCases) {
|
||||||
it(JSON.stringify(input), async function () {
|
it(JSON.stringify(input), async function () {
|
||||||
|
// @ts-ignore
|
||||||
expect(() => attributes.attribsToString(input, pool)).toThrowError();
|
expect(() => attributes.attribsToString(input, pool)).toThrowError();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -322,6 +339,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const got = attributes.attribsToString(input, pool);
|
const got = attributes.attribsToString(input, pool);
|
||||||
expect(got).to.equal(want);
|
expect(got).to.equal(want);
|
||||||
});
|
});
|
||||||
|
@ -336,6 +354,7 @@ describe('attributes', function () {
|
||||||
];
|
];
|
||||||
for (const [input, want] of testCases) {
|
for (const [input, want] of testCases) {
|
||||||
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
it(`${JSON.stringify(input)} -> ${JSON.stringify(want)}`, async function () {
|
||||||
|
// @ts-ignore
|
||||||
const got = attributes.attribsToString(input, pool);
|
const got = attributes.attribsToString(input, pool);
|
||||||
expect(got).to.equal(want);
|
expect(got).to.equal(want);
|
||||||
expect(JSON.stringify(pool.getAttrib(attribs.length)))
|
expect(JSON.stringify(pool.getAttrib(attribs.length)))
|
||||||
|
|
|
@ -1,398 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* While importexport tests target the `setHTML` API endpoint, which is nearly identical to what
|
|
||||||
* happens when a user manually imports a document via the UI, the contentcollector tests here don't
|
|
||||||
* use rehype to process the document. Rehype removes spaces and newĺines were applicable, so the
|
|
||||||
* expected results here can differ from importexport.js.
|
|
||||||
*
|
|
||||||
* If you add tests here, please also add them to importexport.js
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {APool} from "../../../node/types/PadType";
|
|
||||||
|
|
||||||
const AttributePool = require('../../../static/js/AttributePool');
|
|
||||||
const Changeset = require('../../../static/js/Changeset');
|
|
||||||
const assert = require('assert').strict;
|
|
||||||
const attributes = require('../../../static/js/attributes');
|
|
||||||
const contentcollector = require('../../../static/js/contentcollector');
|
|
||||||
const jsdom = require('jsdom');
|
|
||||||
|
|
||||||
import {describe, it, beforeAll, test} from 'vitest';
|
|
||||||
|
|
||||||
// All test case `wantAlines` values must only refer to attributes in this list so that the
|
|
||||||
// attribute numbers do not change due to changes in pool insertion order.
|
|
||||||
const knownAttribs = [
|
|
||||||
['insertorder', 'first'],
|
|
||||||
['italic', 'true'],
|
|
||||||
['list', 'bullet1'],
|
|
||||||
['list', 'bullet2'],
|
|
||||||
['list', 'number1'],
|
|
||||||
['list', 'number2'],
|
|
||||||
['lmkr', '1'],
|
|
||||||
['start', '1'],
|
|
||||||
['start', '2'],
|
|
||||||
];
|
|
||||||
|
|
||||||
const testCases = [
|
|
||||||
{
|
|
||||||
description: 'Simple',
|
|
||||||
html: '<html><body><p>foo</p></body></html>',
|
|
||||||
wantAlines: ['+3'],
|
|
||||||
wantText: ['foo'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Line starts with asterisk',
|
|
||||||
html: '<html><body><p>*foo</p></body></html>',
|
|
||||||
wantAlines: ['+4'],
|
|
||||||
wantText: ['*foo'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Complex nested Li',
|
|
||||||
html: '<!doctype html><html><body><ol><li>one</li><li><ol><li>1.1</li></ol></li><li>two</li></ol></body></html>',
|
|
||||||
wantAlines: [
|
|
||||||
'*0*4*6*7+1+3',
|
|
||||||
'*0*5*6*8+1+3',
|
|
||||||
'*0*4*6*8+1+3',
|
|
||||||
],
|
|
||||||
wantText: [
|
|
||||||
'*one', '*1.1', '*two',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Complex list of different types',
|
|
||||||
html: '<!doctype html><html><body><ul class="bullet"><li>one</li><li>two</li><li>0</li><li>1</li><li>2<ul class="bullet"><li>3</li><li>4</li></ul></li></ul><ol class="number"><li>item<ol class="number"><li>item1</li><li>item2</li></ol></li></ol></body></html>',
|
|
||||||
wantAlines: [
|
|
||||||
'*0*2*6+1+3',
|
|
||||||
'*0*2*6+1+3',
|
|
||||||
'*0*2*6+1+1',
|
|
||||||
'*0*2*6+1+1',
|
|
||||||
'*0*2*6+1+1',
|
|
||||||
'*0*3*6+1+1',
|
|
||||||
'*0*3*6+1+1',
|
|
||||||
'*0*4*6*7+1+4',
|
|
||||||
'*0*5*6*8+1+5',
|
|
||||||
'*0*5*6*8+1+5',
|
|
||||||
],
|
|
||||||
wantText: [
|
|
||||||
'*one',
|
|
||||||
'*two',
|
|
||||||
'*0',
|
|
||||||
'*1',
|
|
||||||
'*2',
|
|
||||||
'*3',
|
|
||||||
'*4',
|
|
||||||
'*item',
|
|
||||||
'*item1',
|
|
||||||
'*item2',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Tests if uls properly get attributes',
|
|
||||||
html: '<html><body><ul><li>a</li><li>b</li></ul><div>div</div><p>foo</p></body></html>',
|
|
||||||
wantAlines: [
|
|
||||||
'*0*2*6+1+1',
|
|
||||||
'*0*2*6+1+1',
|
|
||||||
'+3',
|
|
||||||
'+3',
|
|
||||||
],
|
|
||||||
wantText: ['*a', '*b', 'div', 'foo'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Tests if indented uls properly get attributes',
|
|
||||||
html: '<html><body><ul><li>a</li><ul><li>b</li></ul><li>a</li></ul><p>foo</p></body></html>',
|
|
||||||
wantAlines: [
|
|
||||||
'*0*2*6+1+1',
|
|
||||||
'*0*3*6+1+1',
|
|
||||||
'*0*2*6+1+1',
|
|
||||||
'+3',
|
|
||||||
],
|
|
||||||
wantText: ['*a', '*b', '*a', 'foo'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Tests if ols properly get line numbers when in a normal OL',
|
|
||||||
html: '<html><body><ol><li>a</li><li>b</li><li>c</li></ol><p>test</p></body></html>',
|
|
||||||
wantAlines: [
|
|
||||||
'*0*4*6*7+1+1',
|
|
||||||
'*0*4*6*7+1+1',
|
|
||||||
'*0*4*6*7+1+1',
|
|
||||||
'+4',
|
|
||||||
],
|
|
||||||
wantText: ['*a', '*b', '*c', 'test'],
|
|
||||||
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'A single completely empty line break within an ol should reset count if OL is closed off..',
|
|
||||||
html: '<html><body><ol><li>should be 1</li></ol><p>hello</p><ol><li>should be 1</li><li>should be 2</li></ol><p></p></body></html>',
|
|
||||||
wantAlines: [
|
|
||||||
'*0*4*6*7+1+b',
|
|
||||||
'+5',
|
|
||||||
'*0*4*6*8+1+b',
|
|
||||||
'*0*4*6*8+1+b',
|
|
||||||
'',
|
|
||||||
],
|
|
||||||
wantText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''],
|
|
||||||
noteToSelf: "Shouldn't include attribute marker in the <p> line",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'A single <p></p> should create a new line',
|
|
||||||
html: '<html><body><p></p><p></p></body></html>',
|
|
||||||
wantAlines: ['', ''],
|
|
||||||
wantText: ['', ''],
|
|
||||||
noteToSelf: '<p></p>should create a line break but not break numbering',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Tests if ols properly get line numbers when in a normal OL #2',
|
|
||||||
html: '<html><body>a<ol><li>b<ol><li>c</li></ol></ol>notlist<p>foo</p></body></html>',
|
|
||||||
wantAlines: [
|
|
||||||
'+1',
|
|
||||||
'*0*4*6*7+1+1',
|
|
||||||
'*0*5*6*8+1+1',
|
|
||||||
'+7',
|
|
||||||
'+3',
|
|
||||||
],
|
|
||||||
wantText: ['a', '*b', '*c', 'notlist', 'foo'],
|
|
||||||
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'First item being an UL then subsequent being OL will fail',
|
|
||||||
html: '<html><body><ul><li>a<ol><li>b</li><li>c</li></ol></li></ul></body></html>',
|
|
||||||
wantAlines: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'],
|
|
||||||
wantText: ['a', '*b', '*c'],
|
|
||||||
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'A single completely empty line break within an ol should NOT reset count',
|
|
||||||
html: '<html><body><ol><li>should be 1</li><p></p><li>should be 2</li><li>should be 3</li></ol><p></p></body></html>',
|
|
||||||
wantAlines: [],
|
|
||||||
wantText: ['*should be 1', '*should be 2', '*should be 3'],
|
|
||||||
noteToSelf: "<p></p>should create a line break but not break numbering -- This is what I can't get working!",
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Content outside body should be ignored',
|
|
||||||
html: '<html><head><title>title</title><style></style></head><body>empty<br></body></html>',
|
|
||||||
wantAlines: ['+5'],
|
|
||||||
wantText: ['empty'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Multiple spaces should be preserved',
|
|
||||||
html: '<html><body>Text with more than one space.<br></body></html>',
|
|
||||||
wantAlines: ['+10'],
|
|
||||||
wantText: ['Text with more than one space.'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'non-breaking and normal space should be preserved',
|
|
||||||
html: '<html><body>Text with more than one space.<br></body></html>',
|
|
||||||
wantAlines: ['+10'],
|
|
||||||
wantText: ['Text with more than one space.'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Multiple nbsp should be preserved',
|
|
||||||
html: '<html><body> <br></body></html>',
|
|
||||||
wantAlines: ['+2'],
|
|
||||||
wantText: [' '],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Multiple nbsp between words ',
|
|
||||||
html: '<html><body> word1 word2 word3<br></body></html>',
|
|
||||||
wantAlines: ['+m'],
|
|
||||||
wantText: [' word1 word2 word3'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'A non-breaking space preceded by a normal space',
|
|
||||||
html: '<html><body> word1 word2 word3<br></body></html>',
|
|
||||||
wantAlines: ['+l'],
|
|
||||||
wantText: [' word1 word2 word3'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'A non-breaking space followed by a normal space',
|
|
||||||
html: '<html><body> word1 word2 word3<br></body></html>',
|
|
||||||
wantAlines: ['+l'],
|
|
||||||
wantText: [' word1 word2 word3'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Don\'t collapse spaces that follow a newline',
|
|
||||||
html: '<!doctype html><html><body>something<br> something<br></body></html>',
|
|
||||||
wantAlines: ['+9', '+m'],
|
|
||||||
wantText: ['something', ' something'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Don\'t collapse spaces that follow a empty paragraph',
|
|
||||||
html: '<!doctype html><html><body>something<p></p> something<br></body></html>',
|
|
||||||
wantAlines: ['+9', '', '+m'],
|
|
||||||
wantText: ['something', '', ' something'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Don\'t collapse spaces that preceed/follow a newline',
|
|
||||||
html: '<html><body>something <br> something<br></body></html>',
|
|
||||||
wantAlines: ['+l', '+m'],
|
|
||||||
wantText: ['something ', ' something'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Don\'t collapse spaces that preceed/follow a empty paragraph',
|
|
||||||
html: '<html><body>something <p></p> something<br></body></html>',
|
|
||||||
wantAlines: ['+l', '', '+m'],
|
|
||||||
wantText: ['something ', '', ' something'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Don\'t collapse non-breaking spaces that follow a newline',
|
|
||||||
html: '<html><body>something<br> something<br></body></html>',
|
|
||||||
wantAlines: ['+9', '+c'],
|
|
||||||
wantText: ['something', ' something'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Don\'t collapse non-breaking spaces that follow a paragraph',
|
|
||||||
html: '<html><body>something<p></p> something<br></body></html>',
|
|
||||||
wantAlines: ['+9', '', '+c'],
|
|
||||||
wantText: ['something', '', ' something'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Preserve all spaces when multiple are present',
|
|
||||||
html: '<html><body>Need <span> more </span> space<i> s </i> !<br></body></html>',
|
|
||||||
wantAlines: ['+h*1+4+2'],
|
|
||||||
wantText: ['Need more space s !'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Newlines and multiple spaces across newlines should be preserved',
|
|
||||||
html: `
|
|
||||||
<html><body>Need
|
|
||||||
<span> more </span>
|
|
||||||
space
|
|
||||||
<i> s </i>
|
|
||||||
!<br></body></html>`,
|
|
||||||
wantAlines: ['+19*1+4+b'],
|
|
||||||
wantText: ['Need more space s !'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Multiple new lines at the beginning should be preserved',
|
|
||||||
html: '<html><body><br><br><p></p><p></p>first line<br><br>second line<br></body></html>',
|
|
||||||
wantAlines: ['', '', '', '', '+a', '', '+b'],
|
|
||||||
wantText: ['', '', '', '', 'first line', '', 'second line'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'A paragraph with multiple lines should not loose spaces when lines are combined',
|
|
||||||
html: `<html><body><p>
|
|
||||||
а б в г ґ д е є ж з и і ї й к л м н о
|
|
||||||
п р с т у ф х ц ч ш щ ю я ь</p>
|
|
||||||
</body></html>`,
|
|
||||||
wantAlines: ['+1t'],
|
|
||||||
wantText: ['а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'lines in preformatted text should be kept intact',
|
|
||||||
html: `<html><body><p>
|
|
||||||
а б в г ґ д е є ж з и і ї й к л м н о</p><pre>multiple
|
|
||||||
lines
|
|
||||||
in
|
|
||||||
pre
|
|
||||||
</pre><p>п р с т у ф х ц ч ш щ ю я
|
|
||||||
ь</p>
|
|
||||||
</body></html>`,
|
|
||||||
wantAlines: ['+11', '+8', '+5', '+2', '+3', '+r'],
|
|
||||||
wantText: [
|
|
||||||
'а б в г ґ д е є ж з и і ї й к л м н о',
|
|
||||||
'multiple',
|
|
||||||
'lines',
|
|
||||||
'in',
|
|
||||||
'pre',
|
|
||||||
'п р с т у ф х ц ч ш щ ю я ь',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'pre should be on a new line not preceded by a space',
|
|
||||||
html: `<html><body><p>
|
|
||||||
1
|
|
||||||
</p><pre>preline
|
|
||||||
</pre></body></html>`,
|
|
||||||
wantAlines: ['+6', '+7'],
|
|
||||||
wantText: [' 1 ', 'preline'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Preserve spaces on the beginning and end of a element',
|
|
||||||
html: '<html><body>Need<span> more </span>space<i> s </i>!<br></body></html>',
|
|
||||||
wantAlines: ['+f*1+3+1'],
|
|
||||||
wantText: ['Need more space s !'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Preserve spaces outside elements',
|
|
||||||
html: '<html><body>Need <span>more</span> space <i>s</i> !<br></body></html>',
|
|
||||||
wantAlines: ['+g*1+1+2'],
|
|
||||||
wantText: ['Need more space s !'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Preserve spaces at the end of an element',
|
|
||||||
html: '<html><body>Need <span>more </span>space <i>s </i>!<br></body></html>',
|
|
||||||
wantAlines: ['+g*1+2+1'],
|
|
||||||
wantText: ['Need more space s !'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Preserve spaces at the start of an element',
|
|
||||||
html: '<html><body>Need<span> more</span> space<i> s</i> !<br></body></html>',
|
|
||||||
wantAlines: ['+f*1+2+2'],
|
|
||||||
wantText: ['Need more space s !'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
describe(__filename, function () {
|
|
||||||
for (const tc of testCases) {
|
|
||||||
describe(tc.description, function () {
|
|
||||||
let apool: APool;
|
|
||||||
let result: {
|
|
||||||
lines: string[],
|
|
||||||
lineAttribs: string[],
|
|
||||||
};
|
|
||||||
if (tc.disabled) {
|
|
||||||
test.skip('If disabled we do not run the test');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeAll(async function () {
|
|
||||||
|
|
||||||
const {window: {document}} = new jsdom.JSDOM(tc.html);
|
|
||||||
apool = new AttributePool();
|
|
||||||
// To reduce test fragility, the attribute pool is seeded with `knownAttribs`, and all
|
|
||||||
// attributes in `tc.wantAlines` must be in `knownAttribs`. (This guarantees that attribute
|
|
||||||
// numbers do not change if the attribute processing code changes.)
|
|
||||||
for (const attrib of knownAttribs) apool.putAttrib(attrib);
|
|
||||||
for (const aline of tc.wantAlines) {
|
|
||||||
for (const op of Changeset.deserializeOps(aline)) {
|
|
||||||
for (const n of attributes.decodeAttribString(op.attribs)) {
|
|
||||||
assert(n < knownAttribs.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const cc = contentcollector.makeContentCollector(true, null, apool);
|
|
||||||
cc.collectContent(document.body);
|
|
||||||
result = cc.finish();
|
|
||||||
console.log(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('text matches', async function () {
|
|
||||||
assert.deepEqual(result.lines, tc.wantText);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('alines match', async function () {
|
|
||||||
assert.deepEqual(result.lineAttribs, tc.wantAlines);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('attributes are sorted in canonical order', async function () {
|
|
||||||
const gotAttribs:string[][][] = [];
|
|
||||||
const wantAttribs = [];
|
|
||||||
for (const aline of result.lineAttribs) {
|
|
||||||
const gotAlineAttribs:string[][] = [];
|
|
||||||
gotAttribs.push(gotAlineAttribs);
|
|
||||||
const wantAlineAttribs:string[] = [];
|
|
||||||
wantAttribs.push(wantAlineAttribs);
|
|
||||||
for (const op of Changeset.deserializeOps(aline)) {
|
|
||||||
const gotOpAttribs:string[] = [...attributes.attribsFromString(op.attribs, apool)];
|
|
||||||
gotAlineAttribs.push(gotOpAttribs);
|
|
||||||
wantAlineAttribs.push(attributes.sort([...gotOpAttribs]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.deepEqual(gotAttribs, wantAttribs);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,19 +1,19 @@
|
||||||
import {MapArrayType} from "../../../node/types/MapType";
|
import {MapArrayType} from "../../../node/types/MapType";
|
||||||
const {padutils} = require('../../../static/js/pad_utils');
|
import padutils from '../../../static/js/pad_utils';
|
||||||
import {describe, it, expect, afterEach, beforeAll} from "vitest";
|
import {describe, it, expect, afterEach, beforeAll} from "vitest";
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
describe('warnDeprecated', function () {
|
describe('warnDeprecated', function () {
|
||||||
const {warnDeprecated} = padutils;
|
const {warnDeprecatedFlags, warnDeprecated} = padutils;
|
||||||
const backups:MapArrayType<any> = {};
|
const backups:MapArrayType<any> = {};
|
||||||
|
|
||||||
beforeAll(async function () {
|
beforeAll(async function () {
|
||||||
backups.logger = warnDeprecated.logger;
|
backups.logger = warnDeprecatedFlags.logger;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async function () {
|
afterEach(async function () {
|
||||||
warnDeprecated.logger = backups.logger;
|
warnDeprecatedFlags.logger = backups.logger;
|
||||||
delete warnDeprecated._rl; // Reset internal rate limiter state.
|
delete warnDeprecatedFlags._rl; // Reset internal rate limiter state.
|
||||||
});
|
});
|
||||||
|
|
||||||
/*it('includes the stack', async function () {
|
/*it('includes the stack', async function () {
|
||||||
|
@ -25,13 +25,13 @@ describe(__filename, function () {
|
||||||
|
|
||||||
it('rate limited', async function () {
|
it('rate limited', async function () {
|
||||||
let got = 0;
|
let got = 0;
|
||||||
warnDeprecated.logger = {warn: () => ++got};
|
warnDeprecatedFlags.logger = {warn: () => ++got};
|
||||||
warnDeprecated(); // Initialize internal rate limiter state.
|
warnDeprecated(); // Initialize internal rate limiter state.
|
||||||
const {period} = warnDeprecated._rl;
|
const {period} = warnDeprecatedFlags._rl!;
|
||||||
got = 0;
|
got = 0;
|
||||||
const testCases = [[0, 1], [0, 1], [period - 1, 1], [period, 2]];
|
const testCases = [[0, 1], [0, 1], [period - 1, 1], [period, 2]];
|
||||||
for (const [now, want] of testCases) { // In a loop so that the stack trace is the same.
|
for (const [now, want] of testCases) { // In a loop so that the stack trace is the same.
|
||||||
warnDeprecated._rl.now = () => now;
|
warnDeprecatedFlags._rl!.now = () => now;
|
||||||
warnDeprecated();
|
warnDeprecated();
|
||||||
expect(got).toEqual(want);
|
expect(got).toEqual(want);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const SkipList = require('ep_etherpad-lite/static/js/skiplist');
|
import SkipList from 'ep_etherpad-lite/static/js/skiplist';
|
||||||
import {expect, describe, it} from 'vitest';
|
import {expect, describe, it} from 'vitest';
|
||||||
|
|
||||||
describe('skiplist.js', function () {
|
describe('skiplist.js', function () {
|
||||||
it('rejects null keys', async function () {
|
it('rejects null keys', async function () {
|
||||||
const skiplist = new SkipList();
|
const skiplist = new SkipList();
|
||||||
for (const key of [undefined, null]) {
|
for (const key of [undefined, null]) {
|
||||||
|
// @ts-ignore
|
||||||
expect(() => skiplist.push({key})).toThrowError();
|
expect(() => skiplist.push({key})).toThrowError();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
import {MapArrayType} from "../../node/types/MapType";
|
import {MapArrayType} from "../../node/types/MapType";
|
||||||
|
|
||||||
const AttributePool = require('../../static/js/AttributePool');
|
import AttributePool from '../../static/js/AttributePool';
|
||||||
const apiHandler = require('../../node/handler/APIHandler');
|
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const io = require('socket.io-client');
|
const io = require('socket.io-client');
|
||||||
const log4js = require('log4js');
|
const log4js = require('log4js');
|
||||||
const {padutils} = require('../../static/js/pad_utils');
|
import padutils from '../../static/js/pad_utils';
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
const server = require('../../node/server');
|
const server = require('../../node/server');
|
||||||
const setCookieParser = require('set-cookie-parser');
|
const setCookieParser = require('set-cookie-parser');
|
||||||
|
|
|
@ -8,7 +8,7 @@ const db = require('../../../node/db/DB');
|
||||||
const importEtherpad = require('../../../node/utils/ImportEtherpad');
|
const importEtherpad = require('../../../node/utils/ImportEtherpad');
|
||||||
const padManager = require('../../../node/db/PadManager');
|
const padManager = require('../../../node/db/PadManager');
|
||||||
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
||||||
const {randomString} = require('../../../static/js/pad_utils');
|
import {randomString} from '../../../static/js/pad_utils';
|
||||||
|
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
let padId: string;
|
let padId: string;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import {MapArrayType} from "../../../node/types/MapType";
|
import {MapArrayType} from "../../../node/types/MapType";
|
||||||
import {PluginDef} from "../../../node/types/PartType";
|
import {PluginDef} from "../../../node/types/PartType";
|
||||||
|
|
||||||
const ChatMessage = require('../../../static/js/ChatMessage');
|
import ChatMessage from '../../../static/js/ChatMessage';
|
||||||
const {Pad} = require('../../../node/db/Pad');
|
const {Pad} = require('../../../node/db/Pad');
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
|
@ -103,10 +103,14 @@ describe(__filename, function () {
|
||||||
checkHook('chatNewMessage', ({message}) => {
|
checkHook('chatNewMessage', ({message}) => {
|
||||||
assert(message != null);
|
assert(message != null);
|
||||||
assert(message instanceof ChatMessage);
|
assert(message instanceof ChatMessage);
|
||||||
assert.equal(message.authorId, authorId);
|
// @ts-ignore
|
||||||
assert.equal(message.text, this.test!.title);
|
assert.equal(message!.authorId, authorId);
|
||||||
assert(message.time >= start);
|
// @ts-ignore
|
||||||
assert(message.time <= Date.now());
|
assert.equal(message!.text, this.test!.title);
|
||||||
|
// @ts-ignore
|
||||||
|
assert(message!.time >= start);
|
||||||
|
// @ts-ignore
|
||||||
|
assert(message!.time <= Date.now());
|
||||||
}),
|
}),
|
||||||
sendChat(socket, {text: this.test!.title}),
|
sendChat(socket, {text: this.test!.title}),
|
||||||
]);
|
]);
|
||||||
|
@ -153,7 +157,9 @@ describe(__filename, function () {
|
||||||
const customMetadata = {foo: this.test!.title};
|
const customMetadata = {foo: this.test!.title};
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
checkHook('chatNewMessage', ({message}) => {
|
checkHook('chatNewMessage', ({message}) => {
|
||||||
|
// @ts-ignore
|
||||||
message.text = modifiedText;
|
message.text = modifiedText;
|
||||||
|
// @ts-ignore
|
||||||
message.customMetadata = customMetadata;
|
message.customMetadata = customMetadata;
|
||||||
}),
|
}),
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
|
@ -11,16 +11,17 @@
|
||||||
|
|
||||||
import {APool} from "../../../node/types/PadType";
|
import {APool} from "../../../node/types/PadType";
|
||||||
|
|
||||||
const AttributePool = require('../../../static/js/AttributePool');
|
import AttributePool from '../../../static/js/AttributePool';
|
||||||
const Changeset = require('../../../static/js/Changeset');
|
const Changeset = require('../../../static/js/Changeset');
|
||||||
const assert = require('assert').strict;
|
const assert = require('assert').strict;
|
||||||
const attributes = require('../../../static/js/attributes');
|
import attributes from '../../../static/js/attributes';
|
||||||
const contentcollector = require('../../../static/js/contentcollector');
|
const contentcollector = require('../../../static/js/contentcollector');
|
||||||
const jsdom = require('jsdom');
|
import jsdom from 'jsdom';
|
||||||
|
import {Attribute} from "../../../static/js/types/Attribute";
|
||||||
|
|
||||||
// All test case `wantAlines` values must only refer to attributes in this list so that the
|
// All test case `wantAlines` values must only refer to attributes in this list so that the
|
||||||
// attribute numbers do not change due to changes in pool insertion order.
|
// attribute numbers do not change due to changes in pool insertion order.
|
||||||
const knownAttribs = [
|
const knownAttribs: Attribute[] = [
|
||||||
['insertorder', 'first'],
|
['insertorder', 'first'],
|
||||||
['italic', 'true'],
|
['italic', 'true'],
|
||||||
['list', 'bullet1'],
|
['list', 'bullet1'],
|
||||||
|
@ -336,7 +337,7 @@ pre
|
||||||
describe(__filename, function () {
|
describe(__filename, function () {
|
||||||
for (const tc of testCases) {
|
for (const tc of testCases) {
|
||||||
describe(tc.description, function () {
|
describe(tc.description, function () {
|
||||||
let apool: APool;
|
let apool: AttributePool;
|
||||||
let result: {
|
let result: {
|
||||||
lines: string[],
|
lines: string[],
|
||||||
lineAttribs: string[],
|
lineAttribs: string[],
|
||||||
|
@ -376,11 +377,12 @@ describe(__filename, function () {
|
||||||
for (const aline of result.lineAttribs) {
|
for (const aline of result.lineAttribs) {
|
||||||
const gotAlineAttribs:string[][] = [];
|
const gotAlineAttribs:string[][] = [];
|
||||||
gotAttribs.push(gotAlineAttribs);
|
gotAttribs.push(gotAlineAttribs);
|
||||||
const wantAlineAttribs:string[] = [];
|
const wantAlineAttribs:Attribute[] = [];
|
||||||
wantAttribs.push(wantAlineAttribs);
|
wantAttribs.push(wantAlineAttribs);
|
||||||
for (const op of Changeset.deserializeOps(aline)) {
|
for (const op of Changeset.deserializeOps(aline)) {
|
||||||
const gotOpAttribs:string[] = [...attributes.attribsFromString(op.attribs, apool)];
|
const gotOpAttribs = [...attributes.attribsFromString(op.attribs, apool)] as unknown as Attribute;
|
||||||
gotAlineAttribs.push(gotOpAttribs);
|
gotAlineAttribs.push(gotOpAttribs);
|
||||||
|
// @ts-ignore
|
||||||
wantAlineAttribs.push(attributes.sort([...gotOpAttribs]));
|
wantAlineAttribs.push(attributes.sort([...gotOpAttribs]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue