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:
SamTV12345 2024-08-17 20:14:36 +02:00 committed by GitHub
parent 5ee2c4e7f8
commit 7e3ad03e2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 961 additions and 830 deletions

View file

@ -1,6 +1,7 @@
// @ts-nocheck
'use strict';
const AttributeMap = require('./AttributeMap');
import AttributeMap from './AttributeMap';
const Changeset = require('./Changeset');
const ChangesetUtils = require('./ChangesetUtils');
const attributes = require('./attributes');

View file

@ -1,6 +1,9 @@
'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.
@ -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.
*/
class AttributeMap extends Map {
private readonly pool? : AttributePool|null
/**
* Converts an attribute string into an AttributeMap.
*
@ -28,14 +32,14 @@ class AttributeMap extends Map {
* @param {AttributePool} pool - Attribute pool.
* @returns {AttributeMap}
*/
static fromString(str, pool) {
public static fromString(str: string, pool?: AttributePool|null): AttributeMap {
return new AttributeMap(pool).updateFromString(str);
}
/**
* @param {AttributePool} pool - Attribute pool.
*/
constructor(pool) {
constructor(pool?: AttributePool|null) {
super();
/** @public */
this.pool = pool;
@ -46,15 +50,15 @@ class AttributeMap extends Map {
* @param {string} v - Attribute value.
* @returns {AttributeMap} `this` (for chaining).
*/
set(k, v) {
set(k: string, v: string):this {
k = k == null ? '' : String(k);
v = v == null ? '' : String(v);
this.pool.putAttrib([k, v]);
this.pool!.putAttrib([k, v]);
return super.set(k, v);
}
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).
* @returns {AttributeMap} `this` (for chaining).
*/
update(entries, emptyValueIsDelete = false) {
update(entries: Iterable<Attribute>, emptyValueIsDelete: boolean = false): AttributeMap {
for (let [k, v] of entries) {
k = k == null ? '' : String(k);
v = v == null ? '' : String(v);
@ -83,9 +87,9 @@ class AttributeMap extends Map {
* key is removed from this map (if present).
* @returns {AttributeMap} `this` (for chaining).
*/
updateFromString(str, emptyValueIsDelete = false) {
return this.update(attributes.attribsFromString(str, this.pool), emptyValueIsDelete);
updateFromString(str: string, emptyValueIsDelete: boolean = false): AttributeMap {
return this.update(attributes.attribsFromString(str, this.pool!), emptyValueIsDelete);
}
}
module.exports = AttributeMap;
export default AttributeMap

View file

@ -44,6 +44,8 @@
* @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
* strings) along with their identifiers (non-negative integers).
@ -55,6 +57,14 @@
* in the pad.
*/
class AttributePool {
numToAttrib: {
[key: number]: [string, string]
}
private attribToNum: {
[key: number]: [string, string]
}
private nextNum: number
constructor() {
/**
* Maps an attribute identifier to the attribute's `[key, value]` string pair.
@ -96,7 +106,10 @@ class AttributePool {
*/
clone() {
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);
c.nextNum = this.nextNum;
return c;
@ -111,15 +124,17 @@ class AttributePool {
* membership in the pool without mutating 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);
if (str in this.attribToNum) {
// @ts-ignore
return this.attribToNum[str];
}
if (dontAddIfAbsent) {
return -1;
}
const num = this.nextNum++;
// @ts-ignore
this.attribToNum[str] = num;
this.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')];
return num;
@ -130,7 +145,7 @@ class AttributePool {
* @returns {Attribute} The attribute with the given identifier, or nullish if there is no such
* attribute.
*/
getAttrib(num) {
getAttrib(num: number): Attribute {
const pair = this.numToAttrib[num];
if (!pair) {
return pair;
@ -143,7 +158,7 @@ class AttributePool {
* @returns {string} Eqivalent to `getAttrib(num)[0]` if the attribute exists, otherwise the empty
* string.
*/
getAttribKey(num) {
getAttribKey(num: number): string {
const pair = this.numToAttrib[num];
if (!pair) return '';
return pair[0];
@ -154,7 +169,7 @@ class AttributePool {
* @returns {string} Eqivalent to `getAttrib(num)[1]` if the attribute exists, otherwise the empty
* string.
*/
getAttribValue(num) {
getAttribValue(num: number) {
const pair = this.numToAttrib[num];
if (!pair) return '';
return pair[1];
@ -166,8 +181,8 @@ class AttributePool {
* @param {Function} func - Callback to call with two arguments: key and value. Its return value
* is ignored.
*/
eachAttrib(func) {
for (const n of Object.keys(this.numToAttrib)) {
eachAttrib(func: (k: string, v: string)=>void) {
for (const n in this.numToAttrib) {
const pair = this.numToAttrib[n];
func(pair[0], pair[1]);
}
@ -196,11 +211,12 @@ class AttributePool {
* `new AttributePool().fromJsonable(pool.toJsonable())` to copy because the resulting shared
* state will lead to pool corruption.
*/
fromJsonable(obj) {
fromJsonable(obj: this) {
this.numToAttrib = obj.numToAttrib;
this.nextNum = obj.nextNum;
this.attribToNum = {};
for (const n of Object.keys(this.numToAttrib)) {
// @ts-ignore
this.attribToNum[String(this.numToAttrib[n])] = Number(n);
}
return this;
@ -213,6 +229,7 @@ class AttributePool {
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');
for (const prop of ['numToAttrib', 'attribToNum']) {
// @ts-ignore
const obj = this[prop];
if (obj == null) throw new Error(`${prop} property is null`);
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 (typeof v !== 'string') throw new TypeError(`attrib ${i} value is not a string`);
const attrStr = String(attr);
// @ts-ignore
if (this.attribToNum[attrStr] !== i) throw new Error(`attribToNum for ${attrStr} !== ${i}`);
}
}
}
module.exports = AttributePool;
export default AttributePool

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/*
@ -22,10 +23,10 @@
* https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
*/
const AttributeMap = require('./AttributeMap');
const AttributePool = require('./AttributePool');
import AttributeMap from './AttributeMap';
import AttributePool from './AttributePool';
const attributes = require('./attributes');
const {padutils} = require('./pad_utils');
import padutils from './pad_utils';
/**
* A `[key, value]` pair of strings describing a text attribute.

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,6 +1,6 @@
'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
@ -8,14 +8,25 @@ const {padutils: {warnDeprecated}} = require('./pad_utils');
*
* Supports serialization to JSON.
*/
class ChatMessage {
static fromObject(obj) {
export class ChatMessage {
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 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.
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;
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;
return Object.assign(new ChatMessage(), obj);
}
@ -25,7 +36,7 @@ class ChatMessage {
* @param {?string} [authorId] - Initial value of the `authorId` 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).
*
@ -62,11 +73,11 @@ class ChatMessage {
* @type {string}
*/
get userId() {
warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead');
padUtils.warnDeprecated('ChatMessage.userId property is deprecated; use .authorId instead');
return this.authorId;
}
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;
}
@ -77,11 +88,11 @@ class ChatMessage {
* @type {string}
*/
get userName() {
warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead');
padUtils.warnDeprecated('ChatMessage.userName property is deprecated; use .displayName instead');
return this.displayName;
}
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;
}
@ -89,10 +100,12 @@ class ChatMessage {
// doesn't support authorId and displayName.
toJSON() {
const {authorId, displayName, ...obj} = this;
// @ts-ignore
obj.userId = authorId;
// @ts-ignore
obj.userName = displayName;
return obj;
}
}
module.exports = ChatMessage;
export default ChatMessage

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.

View file

@ -6,6 +6,8 @@
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
import {MapArrayType} from "../../node/types/MapType";
/**
* Copyright 2009 Google Inc.
*
@ -22,11 +24,13 @@
* 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
// copied to new nodes that are spawned during editing; also,
// 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.
const binarySearch = (numItems, func) => {
export const binarySearch = (numItems: number, func: (num: number)=>boolean) => {
if (numItems < 1) return 0;
if (func(0)) return 0;
if (!func(numItems - 1)) return numItems;
@ -52,17 +56,10 @@ const binarySearch = (numItems, func) => {
return high;
};
const binarySearchInfinite = (expectedLength, func) => {
export const binarySearchInfinite = (expectedLength: number, func: (num: number)=>boolean) => {
let i = 0;
while (!func(i)) i += expectedLength;
return binarySearch(i, func);
};
const noop = () => {};
exports.isNodeText = isNodeText;
exports.getAssoc = getAssoc;
exports.setAssoc = setAssoc;
exports.binarySearch = binarySearch;
exports.binarySearchInfinite = binarySearchInfinite;
exports.noop = noop;
export const noop = () => {};

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -18,9 +19,9 @@
*/
let documentAttributeManager;
const AttributeMap = require('./AttributeMap');
import AttributeMap from './AttributeMap';
const browser = require('./vendors/browser');
const padutils = require('./pad_utils').padutils;
import padutils from './pad_utils'
const Ace2Common = require('./ace2_common');
const $ = require('./rjquery').$;
@ -29,19 +30,18 @@ const getAssoc = Ace2Common.getAssoc;
const setAssoc = Ace2Common.setAssoc;
const noop = Ace2Common.noop;
const hooks = require('./pluginfw/hooks');
import SkipList from "./skiplist";
import Scroll from './scroll'
import AttribPool from './AttributePool'
function Ace2Inner(editorInfo, cssManagers) {
const makeChangesetTracker = require('./changesettracker').makeChangesetTracker;
const colorutils = require('./colorutils').colorutils;
const makeContentCollector = require('./contentcollector').makeContentCollector;
const domline = require('./domline').domline;
const AttribPool = require('./AttributePool');
const Changeset = require('./Changeset');
const ChangesetUtils = require('./ChangesetUtils');
const linestylefilter = require('./linestylefilter').linestylefilter;
const SkipList = require('./skiplist');
const undoModule = require('./undomodule').undoModule;
const AttributeManager = require('./AttributeManager');
const DEBUG = false;

View file

@ -17,6 +17,9 @@
* @typedef {string} AttributeString
*/
import AttributePool from "./AttributePool";
import {Attribute} from "./types/Attribute";
/**
* Converts an attribute string into a sequence of attribute identifier numbers.
*
@ -28,7 +31,7 @@
* appear in `str`.
* @returns {Generator<number>}
*/
exports.decodeAttribString = function* (str) {
export const decodeAttribString = function* (str: string): Generator<number> {
const re = /\*([0-9a-z]+)|./gy;
let match;
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 (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}`);
@ -50,7 +53,7 @@ const checkAttribNum = (n) => {
* @param {Iterable<number>} attribNums - Sequence of attribute numbers.
* @returns {AttributeString}
*/
exports.encodeAttribString = (attribNums) => {
export const encodeAttribString = (attribNums: Iterable<number>): string => {
let str = '';
for (const n of attribNums) {
checkAttribNum(n);
@ -67,7 +70,7 @@ exports.encodeAttribString = (attribNums) => {
* @yields {Attribute} The identified attributes, in the same order as `attribNums`.
* @returns {Generator<Attribute>}
*/
exports.attribsFromNums = function* (attribNums, pool) {
export const attribsFromNums = function* (attribNums: Iterable<number>, pool: AttributePool): Generator<Attribute> {
for (const n of attribNums) {
checkAttribNum(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.
* @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);
};
@ -102,8 +105,8 @@ exports.attribsToNums = function* (attribs, pool) {
* @yields {Attribute} The attributes identified in `str`, in order.
* @returns {Generator<Attribute>}
*/
exports.attribsFromString = function* (str, pool) {
yield* exports.attribsFromNums(exports.decodeAttribString(str), pool);
export const attribsFromString = function* (str: string, pool: AttributePool): Generator<Attribute> {
yield* attribsFromNums(decodeAttribString(str), pool);
};
/**
@ -116,8 +119,8 @@ exports.attribsFromString = function* (str, pool) {
* @param {AttributePool} pool - Attribute pool.
* @returns {AttributeString}
*/
exports.attribsToString =
(attribs, pool) => exports.encodeAttribString(exports.attribsToNums(attribs, pool));
export const attribsToString =
(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
@ -126,5 +129,14 @@ exports.attribsToString =
* @param {Attribute[]} attribs - Attributes to sort in place.
* @returns {Attribute[]} `attribs` (for chaining).
*/
exports.sort =
(attribs) => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0));
export const sort = (attribs: Attribute[]): Attribute[] => attribs.sort(([keyA], [keyB]) => (keyA > keyB ? 1 : 0) - (keyA < keyB ? 1 : 0));
export default {
decodeAttribString,
encodeAttribString,
attribsFromNums,
attribsToNums,
attribsFromString,
attribsToString,
sort,
}

View file

@ -1,3 +1,4 @@
// @ts-nocheck
// @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt Apache-2.0
/* Copyright 2021 Richard Hansen <rhansen@rhansen.org> */

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -24,7 +25,7 @@
const makeCSSManager = require('./cssmanager').makeCSSManager;
const domline = require('./domline').domline;
const AttribPool = require('./AttributePool');
import AttribPool from './AttributePool';
const Changeset = require('./Changeset');
const attributes = require('./attributes');
const linestylefilter = require('./linestylefilter').linestylefilter;

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -22,8 +23,8 @@
* limitations under the License.
*/
const AttributeMap = require('./AttributeMap');
const AttributePool = require('./AttributePool');
import AttributeMap from './AttributeMap';
import AttributePool from './AttributePool';
const Changeset = require('./Changeset');
const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => {

5
src/static/js/chat.js → src/static/js/chat.ts Executable file → Normal file
View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
@ -15,8 +16,8 @@
* limitations under the License.
*/
const ChatMessage = require('./ChatMessage');
const padutils = require('./pad_utils').padutils;
import ChatMessage from './ChatMessage';
import padutils from './pad_utils'
const padcookie = require('./pad_cookie').padcookie;
const Tinycon = require('tinycon/tinycon');
const hooks = require('./pluginfw/hooks');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
* 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 AttributeMap = require('./AttributeMap');
import AttributeMap from './AttributeMap';
const UNorm = require('unorm');
const Changeset = require('./Changeset');
const hooks = require('./pluginfw/hooks');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -35,7 +36,7 @@ const attributes = require('./attributes');
const hooks = require('./pluginfw/hooks');
const linestylefilter = {};
const AttributeManager = require('./AttributeManager');
const padutils = require('./pad_utils').padutils;
import padutils from './pad_utils'
linestylefilter.ATTRIB_CLASSES = {
bold: 'tag:b',

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -33,7 +34,8 @@ require('./vendors/gritter');
import html10n from './vendors/html10n'
const Cookies = require('./pad_utils').Cookies;
import {Cookies} from "./pad_utils";
const chat = require('./chat').chat;
const getCollabClient = require('./collab_client').getCollabClient;
const padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus;
@ -44,9 +46,9 @@ const padimpexp = require('./pad_impexp').padimpexp;
const padmodals = require('./pad_modals').padmodals;
const padsavedrevs = require('./pad_savedrevs');
const paduserlist = require('./pad_userlist').paduserlist;
const padutils = require('./pad_utils').padutils;
import padutils from './pad_utils'
const colorutils = require('./colorutils').colorutils;
const randomString = require('./pad_utils').randomString;
import {randomString} from "./pad_utils";
const socketio = require('./socketio');
const hooks = require('./pluginfw/hooks');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
import html10n from './vendors/html10n';

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -16,7 +17,7 @@
* limitations under the License.
*/
const Cookies = require('./pad_utils').Cookies;
import {Cookies} from "./pad_utils";
exports.padcookie = new class {
constructor() {

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -24,7 +25,7 @@
const browser = require('./vendors/browser');
const hooks = require('./pluginfw/hooks');
const padutils = require('./pad_utils').padutils;
import padutils from "./pad_utils";
const padeditor = require('./pad_editor').padeditor;
const padsavedrevs = require('./pad_savedrevs');
const _ = require('underscore');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
@ -21,9 +22,8 @@
* limitations under the License.
*/
const Cookies = require('./pad_utils').Cookies;
import padutils,{Cookies} from "./pad_utils";
const padcookie = require('./pad_cookie').padcookie;
const padutils = require('./pad_utils').padutils;
const Ace2Editor = require('./ace').Ace2Editor;
import html10n from '../js/vendors/html10n'

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -16,7 +17,7 @@
* limitations under the License.
*/
const padutils = require('./pad_utils').padutils;
import padutils from './pad_utils'
const hooks = require('./pluginfw/hooks');
import html10n from './vendors/html10n';
let myUserInfo = {};

View file

@ -6,6 +6,8 @@
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
import {binarySearch} from "./ace2_common";
/**
* Copyright 2009 Google Inc.
*
@ -22,13 +24,14 @@
* 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,
* readonly, session Ids
*/
const randomString = (len) => {
export const randomString = (len?: number) => {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let randomstring = '';
len = len || 20;
@ -85,13 +88,41 @@ const urlRegex = (() => {
'tel',
].join('|')}):`;
return new RegExp(
`(?:${withAuth}|${withoutAuth}|www\\.)${urlChar}*(?!${postUrlPunct})${urlChar}`, 'g');
`(?:${withAuth}|${withoutAuth}|www\\.)${urlChar}*(?!${postUrlPunct})${urlChar}`, 'g');
})();
// https://stackoverflow.com/a/68957976
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
* is using the deprecated function).
@ -107,41 +138,41 @@ const padutils = {
* @param {...*} args - Passed to `padutils.warnDeprecated.logger.warn` (or `console.warn` if no
* logger is set), with a stack trace appended if available.
*/
warnDeprecated: (...args) => {
if (padutils.warnDeprecated.disabledForTestingOnly) return;
warnDeprecated = (...args: any[]) => {
if (this.warnDeprecatedFlags.disabledForTestingOnly) return;
const err = new Error();
if (Error.captureStackTrace) Error.captureStackTrace(err, padutils.warnDeprecated);
if (Error.captureStackTrace) Error.captureStackTrace(err, this.warnDeprecated);
err.name = '';
// Rate limit identical deprecation warnings (as determined by the stack) to avoid log spam.
if (typeof err.stack === 'string') {
if (padutils.warnDeprecated._rl == null) {
padutils.warnDeprecated._rl =
{prevs: new Map(), now: () => Date.now(), period: 10 * 60 * 1000};
if (this.warnDeprecatedFlags._rl == null) {
this.warnDeprecatedFlags._rl =
{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 prev = rl.prevs.get(err.stack);
if (prev != null && now - prev < rl.period) return;
rl.prevs.set(err.stack, now);
}
if (err.stack) args.push(err.stack);
(padutils.warnDeprecated.logger || console).warn(...args);
},
escapeHtml: (x) => Security.escapeHTML(String(x)),
uniqueId: () => {
(this.warnDeprecatedFlags.logger || console).warn(...args);
}
escapeHtml = (x: string) => Security.escapeHTML(String(x))
uniqueId = () => {
const pad = require('./pad').pad; // Sidestep circular dependency
// returns string that is exactly 'width' chars, padding with zeros and taking rightmost digits
const encodeNum =
(n, width) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
(n: number, width: number) => (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
return [
pad.getClientIp(),
encodeNum(+new Date(), 7),
encodeNum(Math.floor(Math.random() * 1e9), 4),
].join('.');
},
}
// e.g. "Thu Jun 18 2009 13:09"
simpleDateTime: (date) => {
simpleDateTime = (date: string) => {
const d = new Date(+date); // accept either number or date
const dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()];
const month = ([
@ -162,16 +193,14 @@ const padutils = {
const year = d.getFullYear();
const hourmin = `${d.getHours()}:${(`0${d.getMinutes()}`).slice(-2)}`;
return `${dayOfWeek} ${month} ${dayOfMonth} ${year} ${hourmin}`;
},
wordCharRegex,
urlRegex,
}
// 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)
// 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;
let urls = null;
let urls: [number, string][] | null = null;
let execResult;
// TODO: Switch to String.prototype.matchAll() after support for Node.js < 12.0.0 is dropped.
while ((execResult = urlRegex.exec(text))) {
@ -181,18 +210,19 @@ const padutils = {
urls.push([startIndex, url]);
}
return urls;
},
escapeHtmlWithClickableLinks: (text, target) => {
}
escapeHtmlWithClickableLinks = (text: string, target: string) => {
let idx = 0;
const pieces = [];
const urls = padutils.findURLs(text);
const urls = this.findURLs(text);
const advanceTo = (i) => {
if (i > idx) {
pieces.push(Security.escapeHTML(text.substring(idx, i)));
idx = i;
const advanceTo = (i: number) => {
if (i > idx) {
pieces.push(Security.escapeHTML(text.substring(idx, i)));
idx = i;
}
}
};
;
if (urls) {
for (let j = 0; j < urls.length; j++) {
const startIndex = urls[j][0];
@ -206,25 +236,25 @@ const padutils = {
// https://mathiasbynens.github.io/rel-noopener/
// https://github.com/ether/etherpad-lite/pull/3636
pieces.push(
'<a ',
(target ? `target="${Security.escapeHTMLAttribute(target)}" ` : ''),
'href="',
Security.escapeHTMLAttribute(href),
'" rel="noreferrer noopener">');
'<a ',
(target ? `target="${Security.escapeHTMLAttribute(target)}" ` : ''),
'href="',
Security.escapeHTMLAttribute(href),
'" rel="noreferrer noopener">');
advanceTo(startIndex + href.length);
pieces.push('</a>');
}
}
advanceTo(text.length);
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
// (Input Method Editor), But keypress is not. So, I changed to use keypress instead of keyup.
// It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox
// 3.6.10, Chrome 6.0.472, Safari 5.0).
if (onEnter) {
node.on('keypress', (evt) => {
node.on('keypress', (evt: { which: number; }) => {
if (evt.which === 13) {
onEnter(evt);
}
@ -238,13 +268,15 @@ const padutils = {
}
});
}
},
timediff: (d) => {
}
timediff = (d: number) => {
const pad = require('./pad').pad; // Sidestep circular dependency
const format = (n, word) => {
n = Math.round(n);
return (`${n} ${word}${n !== 1 ? 's' : ''} ago`);
};
const format = (n: number, word: string) => {
n = Math.round(n);
return (`${n} ${word}${n !== 1 ? 's' : ''} ago`);
}
;
d = Math.max(0, (+(new Date()) - (+d) - pad.clientTimeOffset) / 1000);
if (d < 60) {
return format(d, 'second');
@ -259,78 +291,89 @@ const padutils = {
}
d /= 24;
return format(d, 'day');
},
makeAnimationScheduler: (funcToAnimateOneStep, stepTime, stepsAtOnce) => {
if (stepsAtOnce === undefined) {
stepsAtOnce = 1;
}
makeAnimationScheduler =
(funcToAnimateOneStep: any, stepTime: number, stepsAtOnce?: number) => {
if (stepsAtOnce === undefined) {
stepsAtOnce = 1;
}
let animationTimer: any = null;
const scheduleAnimation = () => {
if (!animationTimer) {
animationTimer = window.setTimeout(() => {
animationTimer = null;
let n = stepsAtOnce;
let moreToDo = true;
while (moreToDo && n > 0) {
moreToDo = funcToAnimateOneStep();
n--;
}
if (moreToDo) {
// more to do
scheduleAnimation();
}
}, stepTime * stepsAtOnce);
}
};
return {scheduleAnimation};
}
let animationTimer = null;
makeFieldLabeledWhenEmpty
=
(field: JQueryNode, labelText: string) => {
field = $(field);
const scheduleAnimation = () => {
if (!animationTimer) {
animationTimer = window.setTimeout(() => {
animationTimer = null;
let n = stepsAtOnce;
let moreToDo = true;
while (moreToDo && n > 0) {
moreToDo = funcToAnimateOneStep();
n--;
}
if (moreToDo) {
// more to do
scheduleAnimation();
}
}, stepTime * stepsAtOnce);
}
};
return {scheduleAnimation};
},
makeFieldLabeledWhenEmpty: (field, labelText) => {
field = $(field);
const clear = () => {
field.addClass('editempty');
field.val(labelText);
};
field.focus(() => {
if (field.hasClass('editempty')) {
field.val('');
}
field.removeClass('editempty');
});
field.on('blur', () => {
if (!field.val()) {
clear();
}
});
return {
clear,
};
},
getCheckbox: (node) => $(node).is(':checked'),
setCheckbox: (node, value) => {
if (value) {
$(node).attr('checked', 'checked');
} else {
$(node).prop('checked', false);
const clear = () => {
field.addClass('editempty');
field.val(labelText);
}
;
field.focus(() => {
if (field.hasClass('editempty')) {
field.val('');
}
field.removeClass('editempty');
});
field.on('blur', () => {
if (!field.val()) {
clear();
}
});
return {
clear,
};
}
},
bindCheckboxChange: (node, func) => {
$(node).on('change', func);
},
encodeUserId: (userId) => userId.replace(/[^a-y0-9]/g, (c) => {
if (c === '.') return '-';
return `z${c.charCodeAt(0)}z`;
}),
decodeUserId: (encodedUserId) => encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, (cc) => {
if (cc === '-') { return '.'; } else if (cc.charAt(0) === 'z') {
return String.fromCharCode(Number(cc.slice(1, -1)));
} else {
return cc;
getCheckbox = (node: string) => $(node).is(':checked')
setCheckbox =
(node: JQueryNode, value: boolean) => {
if (value) {
$(node).attr('checked', 'checked');
} else {
$(node).prop('checked', false);
}
}
}),
bindCheckboxChange =
(node: JQueryNode, func: Function) => {
// @ts-ignore
$(node).on("change", func);
}
encodeUserId =
(userId: string) => userId.replace(/[^a-y0-9]/g, (c) => {
if (c === '.') return '-';
return `z${c.charCodeAt(0)}z`;
})
decodeUserId =
(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)));
} else {
return cc;
}
})
/**
* 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
@ -340,109 +383,109 @@ const padutils = {
* conditional transformation of a token to a database key in a way that does not allow a
* malicious user to impersonate another user).
*/
isValidAuthorToken: (t) => {
isValidAuthorToken = (t: string | object) => {
if (typeof t !== 'string' || !t.startsWith('t.')) return false;
const v = t.slice(2);
return v.length > 0 && base64url.test(v);
},
}
/**
* Returns a string that can be used in the `token` cookie as a secret that authenticates a
* particular author.
*/
generateAuthorToken: () => `t.${randomString()}`,
};
let globalExceptionHandler = null;
padutils.setupGlobalExceptionHandler = () => {
if (globalExceptionHandler == null) {
globalExceptionHandler = (e) => {
let type;
let err;
let msg, url, linenumber;
if (e instanceof ErrorEvent) {
type = 'Uncaught exception';
err = e.error || {};
({message: msg, filename: url, lineno: linenumber} = e);
} else if (e instanceof PromiseRejectionEvent) {
type = 'Unhandled Promise rejection';
err = e.reason || {};
({message: msg = 'unknown', fileName: url = 'unknown', lineNumber: linenumber = -1} = err);
} else {
throw new Error(`unknown event: ${e.toString()}`);
}
if (err.name != null && msg !== err.name && !msg.startsWith(`${err.name}: `)) {
msg = `${err.name}: ${msg}`;
}
const errorId = randomString(20);
let msgAlreadyVisible = false;
$('.gritter-item .error-msg').each(function () {
if ($(this).text() === msg) {
msgAlreadyVisible = true;
generateAuthorToken = () => `t.${randomString()}`
setupGlobalExceptionHandler = () => {
if (this.globalExceptionHandler == null) {
this.globalExceptionHandler = (e: any) => {
let type;
let err;
let msg, url, linenumber;
if (e instanceof ErrorEvent) {
type = 'Uncaught exception';
err = e.error || {};
({message: msg, filename: url, lineno: linenumber} = e);
} else if (e instanceof PromiseRejectionEvent) {
type = 'Unhandled Promise rejection';
err = e.reason || {};
({message: msg = 'unknown', fileName: url = 'unknown', lineNumber: linenumber = -1} = err);
} else {
throw new Error(`unknown event: ${e.toString()}`);
}
});
if (err.name != null && msg !== err.name && !msg.startsWith(`${err.name}: `)) {
msg = `${err.name}: ${msg}`;
}
const errorId = randomString(20);
if (!msgAlreadyVisible) {
const txt = document.createTextNode.bind(document); // Convenience shorthand.
const errorMsg = [
$('<p>')
let msgAlreadyVisible = false;
$('.gritter-item .error-msg').each(function () {
if ($(this).text() === msg) {
msgAlreadyVisible = true;
}
});
if (!msgAlreadyVisible) {
const txt = document.createTextNode.bind(document); // Convenience shorthand.
const errorMsg = [
$('<p>')
.append($('<b>').text('Please press and hold Ctrl and press F5 to reload this page')),
$('<p>')
$('<p>')
.text('If the problem persists, please send this error message to your webmaster:'),
$('<div>').css('text-align', 'left').css('font-size', '.8em').css('margin-top', '1em')
$('<div>').css('text-align', 'left').css('font-size', '.8em').css('margin-top', '1em')
.append($('<b>').addClass('error-msg').text(msg)).append($('<br>'))
.append(txt(`at ${url} at line ${linenumber}`)).append($('<br>'))
.append(txt(`ErrorId: ${errorId}`)).append($('<br>'))
.append(txt(type)).append($('<br>'))
.append(txt(`URL: ${window.location.href}`)).append($('<br>'))
.append(txt(`UserAgent: ${navigator.userAgent}`)).append($('<br>')),
];
];
$.gritter.add({
title: 'An error occurred',
text: errorMsg,
class_name: 'error',
position: 'bottom',
sticky: true,
// @ts-ignore
$.gritter.add({
title: 'An error occurred',
text: errorMsg,
class_name: 'error',
position: 'bottom',
sticky: true,
});
}
// send javascript errors to the server
$.post('../jserror', {
errorInfo: JSON.stringify({
errorId,
type,
msg,
url: window.location.href,
source: url,
linenumber,
userAgent: navigator.userAgent,
stack: err.stack,
}),
});
}
// send javascript errors to the server
$.post('../jserror', {
errorInfo: JSON.stringify({
errorId,
type,
msg,
url: window.location.href,
source: url,
linenumber,
userAgent: navigator.userAgent,
stack: err.stack,
}),
});
};
window.onerror = null; // Clear any pre-existing global error handler.
window.addEventListener('error', globalExceptionHandler);
window.addEventListener('unhandledrejection', globalExceptionHandler);
};
window.onerror = null; // Clear any pre-existing global error handler.
window.addEventListener('error', this.globalExceptionHandler);
window.addEventListener('unhandledrejection', this.globalExceptionHandler);
}
}
};
padutils.binarySearch = require('./ace2_common').binarySearch;
binarySearch = binarySearch
}
// https://stackoverflow.com/a/42660748
const inThirdPartyIframe = () => {
try {
return (!window.top.location.hostname);
return (!window.top!.location.hostname);
} catch (e) {
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
// window object.
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=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
@ -455,5 +498,5 @@ if (typeof window !== 'undefined') {
secure: window.location.protocol === 'https:',
});
}
exports.randomString = randomString;
exports.padutils = padutils;
export default new PadUtils()

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
const pluginUtils = require('./shared');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
const pluginDefs = require('./plugin_defs');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
const fs = require('fs').promises;

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
const defs = require('./plugin_defs');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
// Provides a require'able version of jQuery without leaking $ and jQuery;
window.$ = require('./vendors/jquery');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
// Specific hash to display the skin variants builder popup

View file

@ -22,10 +22,24 @@
* 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 {
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.entry = entry;
this.levels = levels;
@ -37,9 +51,9 @@ class Node {
propagateWidthChange() {
const oldWidth = this.downSkipWidths[0];
const newWidth = _entryWidth(this.entry);
const newWidth = _entryWidth(this.entry!);
const widthChange = newWidth - oldWidth;
let n = this;
let n: Node = this;
let lvl = 0;
while (lvl < n.levels) {
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
// invalidate this point.
class Point {
constructor(skipList, loc) {
this._skipList = skipList;
private 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;
const numLevels = this._skipList._start.levels;
const numLevels = this.skipList.start.levels;
let lvl = numLevels - 1;
let i = -1;
let ws = 0;
const nodes = new Array(numLevels);
const idxs = new Array(numLevels);
const widthSkips = new Array(numLevels);
nodes[lvl] = this._skipList._start;
const nodes: Node[] = new Array(numLevels);
const idxs: number[] = new Array(numLevels);
const widthSkips: number[] = new Array(numLevels);
nodes[lvl] = this.skipList.start;
idxs[lvl] = -1;
widthSkips[lvl] = 0;
while (lvl >= 0) {
@ -94,9 +114,9 @@ class Point {
return `Point(${this.loc})`;
}
insert(entry) {
insert(entry: Entry) {
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`);
}
@ -115,14 +135,14 @@ class Point {
if (lvl === pNodes.length) {
// assume we have just passed the end of this.nodes, and reached one level greater
// than the skiplist currently supports
pNodes[lvl] = this._skipList._start;
pNodes[lvl] = this.skipList.start;
pIdxs[lvl] = -1;
this._skipList._start.levels++;
this._skipList._end.levels++;
this._skipList._start.downPtrs[lvl] = this._skipList._end;
this._skipList._end.upPtrs[lvl] = this._skipList._start;
this._skipList._start.downSkips[lvl] = this._skipList._keyToNodeMap.size + 1;
this._skipList._start.downSkipWidths[lvl] = this._skipList._totalWidth;
this.skipList.start.levels++;
this.skipList.end.levels++;
this.skipList.start.downPtrs[lvl] = this.skipList.end;
this.skipList.end.upPtrs[lvl] = this.skipList.start;
this.skipList.start.downSkips[lvl] = this.skipList.keyToNodeMap.size + 1;
this.skipList.start.downSkipWidths[lvl] = this.skipList._totalWidth;
this.widthSkips[lvl] = 0;
}
const me = newNode;
@ -146,13 +166,13 @@ class Point {
up.downSkips[lvl]++;
up.downSkipWidths[lvl] += newWidth;
}
this._skipList._keyToNodeMap.set(newNode.key, newNode);
this._skipList._totalWidth += newWidth;
this.skipList.keyToNodeMap.set(newNode.key as string, newNode);
this.skipList._totalWidth += newWidth;
}
delete() {
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++) {
if (i < elem.levels) {
const up = elem.upPtrs[i];
@ -169,8 +189,8 @@ class Point {
up.downSkipWidths[i] -= elemWidth;
}
}
this._skipList._keyToNodeMap.delete(elem.key);
this._skipList._totalWidth -= elemWidth;
this.skipList.keyToNodeMap.delete(elem.key as string);
this.skipList._totalWidth -= elemWidth;
}
getNode() {
@ -183,20 +203,26 @@ class Point {
* property that is a string.
*/
class SkipList {
start: Node
end: Node
_totalWidth: number
keyToNodeMap: Map<string, Node>
constructor() {
// if there are N elements in the skiplist, "start" is element -1 and "end" is element N
this._start = new Node(null, 1);
this._end = new Node(null, 1, null, null);
this.start = new Node(null, 1);
this.end = new Node(null, 1, null, null);
this._totalWidth = 0;
this._keyToNodeMap = new Map();
this._start.downPtrs[0] = this._end;
this._end.upPtrs[0] = this._start;
this.keyToNodeMap = new Map();
this.start.downPtrs[0] = this.end;
this.end.upPtrs[0] = this.start;
}
_getNodeAtOffset(targetOffset) {
_getNodeAtOffset(targetOffset: number) {
let i = 0;
let n = this._start;
let lvl = this._start.levels - 1;
let n = this.start;
let lvl = this.start.levels - 1;
while (lvl >= 0 && n.downPtrs[lvl]) {
while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset)) {
i += n.downSkipWidths[lvl];
@ -204,17 +230,17 @@ class SkipList {
}
lvl--;
}
if (n === this._start) return (this._start.downPtrs[0] || null);
if (n === this._end) {
return targetOffset === this._totalWidth ? (this._end.upPtrs[0] || null) : null;
if (n === this.start) return (this.start.downPtrs[0] || null);
if (n === this.end) {
return targetOffset === this._totalWidth ? (this.end.upPtrs[0] || null) : null;
}
return n;
}
_getNodeIndex(node, byWidth) {
_getNodeIndex(node: Node, byWidth?: boolean) {
let dist = (byWidth ? 0 : -1);
let n = node;
while (n !== this._start) {
while (n !== this.start) {
const lvl = n.levels - 1;
n = n.upPtrs[lvl];
if (byWidth) dist += n.downSkipWidths[lvl];
@ -223,17 +249,19 @@ class SkipList {
return dist;
}
totalWidth() { return this._totalWidth; }
// Returns index of first entry such that entryFunc(entry) is truthy,
// or length() if no such entry. Assumes all falsy entries come before
// all truthy entries.
search(entryFunc) {
let low = this._start;
let lvl = this._start.levels - 1;
search(entryFunc: Function) {
let low = this.start;
let lvl = this.start.levels - 1;
let lowIndex = -1;
const f = (node) => {
if (node === this._start) return false;
else if (node === this._end) return true;
const f = (node: Node) => {
if (node === this.start) return false;
else if (node === this.end) return true;
else return entryFunc(node.entry);
};
@ -249,20 +277,20 @@ class SkipList {
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 >= 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;
}
// 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 + deleteCount > 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);
if (start + deleteCount > 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.trace();
}
@ -275,56 +303,55 @@ class SkipList {
}
}
next(entry) { return this._keyToNodeMap.get(entry.key).downPtrs[0].entry || null; }
prev(entry) { return this._keyToNodeMap.get(entry.key).upPtrs[0].entry || null; }
push(entry) { this.splice(this._keyToNodeMap.size, 0, [entry]); }
next(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.downPtrs[0].entry || null; }
prev(entry: Entry) { return this.keyToNodeMap.get(entry.key)!.upPtrs[0].entry || null; }
push(entry: Entry) { this.splice(this.keyToNodeMap.size, 0, [entry]); }
slice(start, end) {
slice(start: number, end: number) {
// act like Array.slice()
if (start === undefined) start = 0;
else if (start < 0) start += this._keyToNodeMap.size;
if (end === undefined) end = this._keyToNodeMap.size;
else if (end < 0) end += this._keyToNodeMap.size;
else if (start < 0) start += this.keyToNodeMap.size;
if (end === undefined) end = this.keyToNodeMap.size;
else if (end < 0) end += this.keyToNodeMap.size;
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 > this._keyToNodeMap.size) end = this._keyToNodeMap.size;
if (end > this.keyToNodeMap.size) end = this.keyToNodeMap.size;
if (end <= start) return [];
let n = this.atIndex(start);
const array = [n];
for (let i = 1; i < (end - start); i++) {
n = this.next(n);
n = this.next(n!);
array.push(n);
}
return array;
}
atKey(key) { return this._keyToNodeMap.get(key).entry; }
indexOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key)); }
indexOfEntry(entry) { return this.indexOfKey(entry.key); }
containsKey(key) { return this._keyToNodeMap.has(key); }
atKey(key: string) { return this.keyToNodeMap.get(key)!.entry; }
indexOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!); }
indexOfEntry(entry: Entry) { return this.indexOfKey(entry.key); }
containsKey(key: string) { return this.keyToNodeMap.has(key); }
// gets the last entry starting at or before the offset
atOffset(offset) { return this._getNodeAtOffset(offset).entry; }
keyAtOffset(offset) { return this.atOffset(offset).key; }
offsetOfKey(key) { return this._getNodeIndex(this._keyToNodeMap.get(key), true); }
offsetOfEntry(entry) { return this.offsetOfKey(entry.key); }
setEntryWidth(entry, width) {
atOffset(offset: number) { return this._getNodeAtOffset(offset)!.entry; }
keyAtOffset(offset: number) { return this.atOffset(offset)!.key; }
offsetOfKey(key: string) { return this._getNodeIndex(this.keyToNodeMap.get(key)!, true); }
offsetOfEntry(entry: Entry) { return this.offsetOfKey(entry.key); }
setEntryWidth(entry: Entry, width: number) {
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) {
offsetOfIndex(i: number) {
if (i < 0) return 0;
if (i >= this._keyToNodeMap.size) return this._totalWidth;
return this.offsetOfEntry(this.atIndex(i));
if (i >= this.keyToNodeMap.size) return this._totalWidth;
return this.offsetOfEntry(this.atIndex(i)!);
}
indexOfOffset(offset) {
indexOfOffset(offset: number) {
if (offset <= 0) return 0;
if (offset >= this._totalWidth) return this._keyToNodeMap.size;
return this.indexOfEntry(this.atOffset(offset));
if (offset >= this._totalWidth) return this.keyToNodeMap.size;
return this.indexOfEntry(this.atOffset(offset)!);
}
}
module.exports = SkipList;
export default SkipList

View file

@ -1,3 +1,4 @@
// @ts-nocheck
import io from 'socket.io-client';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**
@ -26,10 +27,9 @@
// assigns to the global `$` and augments it with plugins.
require('./vendors/jquery');
const Cookies = require('./pad_utils').Cookies;
const randomString = require('./pad_utils').randomString;
import {randomString, Cookies} from "./pad_utils";
const hooks = require('./pluginfw/hooks');
const padutils = require('./pad_utils').padutils;
import padutils from './pad_utils'
const socketio = require('./socketio');
import html10n from '../js/vendors/html10n'
let token, padId, exportLinks, socket, changesetLoader, BroadcastSlider;

View file

@ -0,0 +1,4 @@
export type AText = {
text: string,
attribs: string,
}

View file

@ -0,0 +1 @@
export type Attribute = [string, string]

View file

@ -0,0 +1,7 @@
export type PadRevision = {
revNum: number;
savedById: string;
label: string;
timestamp: number;
id: string;
}

View 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
}

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
module.exports = require('underscore');

View file

@ -1,3 +1,4 @@
// @ts-nocheck
'use strict';
/**

View file

@ -1,3 +1,4 @@
// @ts-nocheck
// WARNING: This file may have been modified from original.
// TODO: Check requirement of this file, this afaik was to cover weird edge cases
// that have probably been fixed in browsers.

View file

@ -1,3 +1,4 @@
// @ts-nocheck
// WARNING: This file has been modified from original.
// TODO: Replace with https://github.com/Simonwep/pickr

View file

@ -1,3 +1,4 @@
// @ts-nocheck
// WARNING: This file has been modified from the Original
/*

View file

@ -1,3 +1,4 @@
// @ts-nocheck
/*!
* jQuery JavaScript Library v3.7.1
* https://jquery.com/

View file

@ -1,3 +1,4 @@
// @ts-nocheck
// WARNING: This file has been modified from the Original
// TODO: Nice Select seems relatively abandoned, we should consider other options.