mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-23 00:46:16 -04:00
use reporter
This commit is contained in:
commit
697bfd979b
60 changed files with 1520 additions and 1395 deletions
644
package-lock.json
generated
644
package-lock.json
generated
|
@ -183,6 +183,15 @@
|
|||
"event-target-shim": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||
"requires": {
|
||||
"mime-types": "~2.1.24",
|
||||
"negotiator": "0.6.2"
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
|
@ -223,6 +232,11 @@
|
|||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
|
||||
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg=="
|
||||
},
|
||||
"after": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
|
||||
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
||||
},
|
||||
"agentkeepalive": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
|
||||
|
@ -288,6 +302,11 @@
|
|||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"arraybuffer.slice": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
|
@ -327,11 +346,26 @@
|
|||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
|
||||
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
|
||||
},
|
||||
"backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"base64-arraybuffer": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
|
||||
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
|
||||
},
|
||||
"base64id": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
|
@ -354,6 +388,11 @@
|
|||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"blob": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
|
||||
|
@ -518,6 +557,21 @@
|
|||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"component-bind": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
||||
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
|
||||
},
|
||||
"component-emitter": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
|
||||
},
|
||||
"component-inherit": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
|
||||
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
@ -529,6 +583,11 @@
|
|||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"optional": true
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
|
@ -689,6 +748,74 @@
|
|||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
||||
"dev": true
|
||||
},
|
||||
"engine.io": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz",
|
||||
"integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==",
|
||||
"requires": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.4.1",
|
||||
"debug": "~4.1.0",
|
||||
"engine.io-parser": "~2.2.0",
|
||||
"ws": "~7.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz",
|
||||
"integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==",
|
||||
"requires": {
|
||||
"component-emitter": "~1.3.0",
|
||||
"component-inherit": "0.0.3",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-parser": "~2.2.0",
|
||||
"has-cors": "1.1.0",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"ws": "~7.4.2",
|
||||
"xmlhttprequest-ssl": "~1.5.4",
|
||||
"yeast": "0.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
|
||||
"integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
|
||||
"requires": {
|
||||
"after": "0.8.2",
|
||||
"arraybuffer.slice": "~0.0.7",
|
||||
"base64-arraybuffer": "0.1.4",
|
||||
"blob": "0.0.5",
|
||||
"has-binary2": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"enquirer": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
|
||||
|
@ -736,7 +863,7 @@
|
|||
"security": "1.0.0",
|
||||
"semver": "5.6.0",
|
||||
"slide": "1.1.6",
|
||||
"socket.io": "^2.3.0",
|
||||
"socket.io": "^2.4.1",
|
||||
"terser": "^4.7.0",
|
||||
"threads": "^1.4.0",
|
||||
"tiny-worker": "^2.3.0",
|
||||
|
@ -1220,11 +1347,6 @@
|
|||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
|
||||
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng=="
|
||||
},
|
||||
"after": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
|
||||
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
|
||||
},
|
||||
"aggregate-error": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
|
||||
|
@ -1371,11 +1493,6 @@
|
|||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"arraybuffer.slice": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
|
@ -1399,11 +1516,6 @@
|
|||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
|
||||
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"async-stacktrace": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/async-stacktrace/-/async-stacktrace-0.0.2.tgz",
|
||||
|
@ -1424,31 +1536,16 @@
|
|||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||
},
|
||||
"backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"bail": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
|
||||
"integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ=="
|
||||
},
|
||||
"base64-arraybuffer": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
|
||||
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"base64id": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
|
||||
},
|
||||
"bath-es5": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bath-es5/-/bath-es5-3.0.3.tgz",
|
||||
|
@ -1462,14 +1559,6 @@
|
|||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"better-assert": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
|
||||
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
|
||||
"requires": {
|
||||
"callsite": "1.0.0"
|
||||
}
|
||||
},
|
||||
"binary-search": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
|
||||
|
@ -1485,11 +1574,6 @@
|
|||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"blob": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||
|
@ -1571,11 +1655,6 @@
|
|||
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
|
||||
"integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms="
|
||||
},
|
||||
"callsite": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
|
||||
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
||||
},
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
|
@ -1665,21 +1744,6 @@
|
|||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
|
||||
},
|
||||
"component-bind": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
||||
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
|
||||
},
|
||||
"component-emitter": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
|
||||
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
|
||||
},
|
||||
"component-inherit": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
|
||||
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
|
||||
},
|
||||
"compress-commons": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
|
||||
|
@ -1759,11 +1823,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||
},
|
||||
"cookie-parser": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
|
||||
|
@ -1963,92 +2022,6 @@
|
|||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"engine.io": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz",
|
||||
"integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==",
|
||||
"requires": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "0.3.1",
|
||||
"debug": "~4.1.0",
|
||||
"engine.io-parser": "~2.2.0",
|
||||
"ws": "^7.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz",
|
||||
"integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==",
|
||||
"requires": {
|
||||
"component-emitter": "~1.3.0",
|
||||
"component-inherit": "0.0.3",
|
||||
"debug": "~4.1.0",
|
||||
"engine.io-parser": "~2.2.0",
|
||||
"has-cors": "1.1.0",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.5",
|
||||
"parseuri": "0.0.5",
|
||||
"ws": "~6.1.0",
|
||||
"xmlhttprequest-ssl": "~1.5.4",
|
||||
"yeast": "0.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"component-emitter": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
|
||||
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
|
||||
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
|
||||
"requires": {
|
||||
"after": "0.8.2",
|
||||
"arraybuffer.slice": "~0.0.7",
|
||||
"base64-arraybuffer": "0.1.5",
|
||||
"blob": "0.0.5",
|
||||
"has-binary2": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"enquirer": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
|
||||
|
@ -2658,26 +2631,6 @@
|
|||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-binary2": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
|
||||
"requires": {
|
||||
"isarray": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||
},
|
||||
"hasha": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz",
|
||||
|
@ -2853,11 +2806,6 @@
|
|||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
|
||||
},
|
||||
"indexof": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
|
@ -7186,11 +7134,6 @@
|
|||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||
},
|
||||
"object-component": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
|
||||
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
|
||||
},
|
||||
"observable-fns": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.5.1.tgz",
|
||||
|
@ -7297,22 +7240,6 @@
|
|||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
|
||||
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="
|
||||
},
|
||||
"parseqs": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
|
||||
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
|
||||
"requires": {
|
||||
"better-assert": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"parseuri": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
|
||||
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
|
||||
"requires": {
|
||||
"better-assert": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
@ -7769,135 +7696,6 @@
|
|||
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
|
||||
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
|
||||
},
|
||||
"socket.io": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
|
||||
"integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
|
||||
"requires": {
|
||||
"debug": "~4.1.0",
|
||||
"engine.io": "~3.4.0",
|
||||
"has-binary2": "~1.0.2",
|
||||
"socket.io-adapter": "~1.1.0",
|
||||
"socket.io-client": "2.3.0",
|
||||
"socket.io-parser": "~3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-adapter": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
|
||||
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
|
||||
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
|
||||
"requires": {
|
||||
"backo2": "1.0.2",
|
||||
"base64-arraybuffer": "0.1.5",
|
||||
"component-bind": "1.0.0",
|
||||
"component-emitter": "1.2.1",
|
||||
"debug": "~4.1.0",
|
||||
"engine.io-client": "~3.4.0",
|
||||
"has-binary2": "~1.0.2",
|
||||
"has-cors": "1.1.0",
|
||||
"indexof": "0.0.1",
|
||||
"object-component": "0.0.3",
|
||||
"parseqs": "0.0.5",
|
||||
"parseuri": "0.0.5",
|
||||
"socket.io-parser": "~3.3.0",
|
||||
"to-array": "0.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
|
||||
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
|
||||
"requires": {
|
||||
"component-emitter": "1.2.1",
|
||||
"debug": "~3.1.0",
|
||||
"isarray": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
|
||||
"integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
|
||||
"requires": {
|
||||
"component-emitter": "1.2.1",
|
||||
"debug": "~4.1.0",
|
||||
"isarray": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
|
@ -8284,11 +8082,6 @@
|
|||
"resolved": "https://registry.npmjs.org/tinycon/-/tinycon-0.0.1.tgz",
|
||||
"integrity": "sha1-beEM1SGaHxIdmgokssEbP7JN/+0="
|
||||
},
|
||||
"to-array": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
|
@ -8578,26 +8371,11 @@
|
|||
"typedarray-to-buffer": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz",
|
||||
"integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w=="
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
|
||||
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
},
|
||||
"yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
},
|
||||
"z-schema": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.3.tgz",
|
||||
|
@ -9126,6 +8904,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"has-binary2": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
|
||||
"requires": {
|
||||
"isarray": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
|
||||
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
|
@ -9194,6 +8992,11 @@
|
|||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
|
||||
"dev": true
|
||||
},
|
||||
"indexof": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
|
@ -9545,6 +9348,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz",
|
||||
|
@ -9790,6 +9598,16 @@
|
|||
"callsites": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"parseqs": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
|
||||
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
|
||||
},
|
||||
"parseuri": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
|
||||
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
|
@ -10184,6 +10002,112 @@
|
|||
"is-fullwidth-code-point": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"socket.io": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz",
|
||||
"integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==",
|
||||
"requires": {
|
||||
"debug": "~4.1.0",
|
||||
"engine.io": "~3.5.0",
|
||||
"has-binary2": "~1.0.2",
|
||||
"socket.io-adapter": "~1.1.0",
|
||||
"socket.io-client": "2.4.0",
|
||||
"socket.io-parser": "~3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-adapter": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
|
||||
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
|
||||
"integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
|
||||
"requires": {
|
||||
"backo2": "1.0.2",
|
||||
"component-bind": "1.0.0",
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-client": "~3.5.0",
|
||||
"has-binary2": "~1.0.2",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.6",
|
||||
"parseuri": "0.0.6",
|
||||
"socket.io-parser": "~3.3.0",
|
||||
"to-array": "0.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
|
||||
"integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
|
||||
"requires": {
|
||||
"component-emitter": "~1.3.0",
|
||||
"debug": "~3.1.0",
|
||||
"isarray": "2.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
|
||||
"integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
|
||||
"requires": {
|
||||
"component-emitter": "1.2.1",
|
||||
"debug": "~4.1.0",
|
||||
"isarray": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"component-emitter": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
|
||||
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
|
||||
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"sparse-bitfield": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||
|
@ -10395,6 +10319,11 @@
|
|||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||
"dev": true
|
||||
},
|
||||
"to-array": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
|
||||
|
@ -10555,6 +10484,11 @@
|
|||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
|
||||
"integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA=="
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.23",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
|
||||
|
@ -10574,6 +10508,11 @@
|
|||
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz",
|
||||
"integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA=="
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
|
||||
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
|
||||
},
|
||||
"xpath.js": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz",
|
||||
|
@ -10589,6 +10528,11 @@
|
|||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yeast": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,21 +18,39 @@
|
|||
"Арсен Асхат"
|
||||
]
|
||||
},
|
||||
"admin.page-title": "Панель администратора — Etherpad",
|
||||
"admin_plugins": "Менеджер плагинов",
|
||||
"admin_plugins.available": "Доступные плагины",
|
||||
"admin_plugins.available_not-found": "Плагины не найдены.",
|
||||
"admin_plugins.available_fetching": "Получение…",
|
||||
"admin_plugins.available_install.value": "Установить",
|
||||
"admin_plugins.available_search.placeholder": "Искать плагины для установки",
|
||||
"admin_plugins.description": "Описание",
|
||||
"admin_plugins.installed": "Установленные плагины",
|
||||
"admin_plugins.installed_fetching": "Получение установленных плагинов…",
|
||||
"admin_plugins.installed_nothing": "Вы еще не установили ни одного плагина.",
|
||||
"admin_plugins.installed_uninstall.value": "Удалить",
|
||||
"admin_plugins.last-update": "Последнее обновление",
|
||||
"admin_plugins.name": "Название",
|
||||
"admin_plugins.page-title": "Менеджер плагинов — Etherpad",
|
||||
"admin_plugins.version": "Версия",
|
||||
"admin_plugins_info": "Информация об устранении неполадок",
|
||||
"admin_plugins_info.hooks": "Установленные крючки",
|
||||
"admin_plugins_info.hooks_client": "Клиентские хуки",
|
||||
"admin_plugins_info.hooks_server": "Серверные хуки",
|
||||
"admin_plugins_info.parts": "Установленные части",
|
||||
"admin_plugins_info.plugins": "Установленные плагины",
|
||||
"admin_plugins_info.page-title": "Информация о плагине — Etherpad",
|
||||
"admin_plugins_info.version": "Версия Etherpad",
|
||||
"admin_plugins_info.version_latest": "Последняя доступная версия",
|
||||
"admin_plugins_info.version_number": "Номер версии",
|
||||
"admin_settings": "Настройки",
|
||||
"admin_settings.current": "Текущая конфигурация",
|
||||
"admin_settings.current_example-devel": "Пример шаблона настроек для среда разработки",
|
||||
"admin_settings.current_example-prod": "Пример шаблона настроек для боевой среды",
|
||||
"admin_settings.current_restart.value": "Перезагрузить Etherpad",
|
||||
"admin_settings.current_save.value": "Сохранить настройки",
|
||||
"admin_settings.page-title": "Настройки — Etherpad",
|
||||
"index.newPad": "Создать",
|
||||
"index.createOpenPad": "или создать/открыть документ с именем:",
|
||||
"index.openPad": "откройте существующий документ с именем:",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* This module provides all API functions
|
||||
*/
|
||||
|
@ -18,8 +19,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const customError = require('../utils/customError');
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
const CustomError = require('../utils/customError');
|
||||
const padManager = require('./PadManager');
|
||||
const padMessageHandler = require('../handler/PadMessageHandler');
|
||||
const readOnlyManager = require('./ReadOnlyManager');
|
||||
|
@ -101,7 +102,7 @@ Example returns:
|
|||
}
|
||||
|
||||
*/
|
||||
exports.getAttributePool = async function (padID) {
|
||||
exports.getAttributePool = async (padID) => {
|
||||
const pad = await getPadSafe(padID, true);
|
||||
return {pool: pad.pool};
|
||||
};
|
||||
|
@ -119,7 +120,7 @@ Example returns:
|
|||
}
|
||||
|
||||
*/
|
||||
exports.getRevisionChangeset = async function (padID, rev) {
|
||||
exports.getRevisionChangeset = async (padID, rev) => {
|
||||
// try to parse the revision number
|
||||
if (rev !== undefined) {
|
||||
rev = checkValidRev(rev);
|
||||
|
@ -133,7 +134,7 @@ exports.getRevisionChangeset = async function (padID, rev) {
|
|||
if (rev !== undefined) {
|
||||
// check if this is a valid revision
|
||||
if (rev > head) {
|
||||
throw new customError('rev is higher than the head revision of the pad', 'apierror');
|
||||
throw new CustomError('rev is higher than the head revision of the pad', 'apierror');
|
||||
}
|
||||
|
||||
// get the changeset for this revision
|
||||
|
@ -152,7 +153,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {text:"Welcome Text"}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getText = async function (padID, rev) {
|
||||
exports.getText = async (padID, rev) => {
|
||||
// try to parse the revision number
|
||||
if (rev !== undefined) {
|
||||
rev = checkValidRev(rev);
|
||||
|
@ -166,7 +167,7 @@ exports.getText = async function (padID, rev) {
|
|||
if (rev !== undefined) {
|
||||
// check if this is a valid revision
|
||||
if (rev > head) {
|
||||
throw new customError('rev is higher than the head revision of the pad', 'apierror');
|
||||
throw new CustomError('rev is higher than the head revision of the pad', 'apierror');
|
||||
}
|
||||
|
||||
// get the text of this revision
|
||||
|
@ -188,10 +189,10 @@ Example returns:
|
|||
{code: 1, message:"padID does not exist", data: null}
|
||||
{code: 1, message:"text too long", data: null}
|
||||
*/
|
||||
exports.setText = async function (padID, text) {
|
||||
exports.setText = async (padID, text) => {
|
||||
// text is required
|
||||
if (typeof text !== 'string') {
|
||||
throw new customError('text is not a string', 'apierror');
|
||||
throw new CustomError('text is not a string', 'apierror');
|
||||
}
|
||||
|
||||
// get the pad
|
||||
|
@ -212,10 +213,10 @@ Example returns:
|
|||
{code: 1, message:"padID does not exist", data: null}
|
||||
{code: 1, message:"text too long", data: null}
|
||||
*/
|
||||
exports.appendText = async function (padID, text) {
|
||||
exports.appendText = async (padID, text) => {
|
||||
// text is required
|
||||
if (typeof text !== 'string') {
|
||||
throw new customError('text is not a string', 'apierror');
|
||||
throw new CustomError('text is not a string', 'apierror');
|
||||
}
|
||||
|
||||
const pad = await getPadSafe(padID, true);
|
||||
|
@ -233,7 +234,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {text:"Welcome <strong>Text</strong>"}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getHTML = async function (padID, rev) {
|
||||
exports.getHTML = async (padID, rev) => {
|
||||
if (rev !== undefined) {
|
||||
rev = checkValidRev(rev);
|
||||
}
|
||||
|
@ -245,7 +246,7 @@ exports.getHTML = async function (padID, rev) {
|
|||
// check if this is a valid revision
|
||||
const head = pad.getHeadRevisionNumber();
|
||||
if (rev > head) {
|
||||
throw new customError('rev is higher than the head revision of the pad', 'apierror');
|
||||
throw new CustomError('rev is higher than the head revision of the pad', 'apierror');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,10 +266,10 @@ Example returns:
|
|||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.setHTML = async function (padID, html) {
|
||||
exports.setHTML = async (padID, html) => {
|
||||
// html string is required
|
||||
if (typeof html !== 'string') {
|
||||
throw new customError('html is not a string', 'apierror');
|
||||
throw new CustomError('html is not a string', 'apierror');
|
||||
}
|
||||
|
||||
// get the pad
|
||||
|
@ -278,7 +279,7 @@ exports.setHTML = async function (padID, html) {
|
|||
try {
|
||||
await importHtml.setPadHTML(pad, cleanText(html));
|
||||
} catch (e) {
|
||||
throw new customError('HTML is malformed', 'apierror');
|
||||
throw new CustomError('HTML is malformed', 'apierror');
|
||||
}
|
||||
|
||||
// update the clients on the pad
|
||||
|
@ -294,23 +295,25 @@ getChatHistory(padId, start, end), returns a part of or the whole chat-history o
|
|||
|
||||
Example returns:
|
||||
|
||||
{"code":0,"message":"ok","data":{"messages":[{"text":"foo","authorID":"a.foo","time":1359199533759,"userName":"test"},
|
||||
{"text":"bar","authorID":"a.foo","time":1359199534622,"userName":"test"}]}}
|
||||
{"code":0,"message":"ok","data":{"messages":[
|
||||
{"text":"foo","authorID":"a.foo","time":1359199533759,"userName":"test"},
|
||||
{"text":"bar","authorID":"a.foo","time":1359199534622,"userName":"test"}
|
||||
]}}
|
||||
|
||||
{code: 1, message:"start is higher or equal to the current chatHead", data: null}
|
||||
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getChatHistory = async function (padID, start, end) {
|
||||
exports.getChatHistory = async (padID, start, end) => {
|
||||
if (start && end) {
|
||||
if (start < 0) {
|
||||
throw new customError('start is below zero', 'apierror');
|
||||
throw new CustomError('start is below zero', 'apierror');
|
||||
}
|
||||
if (end < 0) {
|
||||
throw new customError('end is below zero', 'apierror');
|
||||
throw new CustomError('end is below zero', 'apierror');
|
||||
}
|
||||
if (start > end) {
|
||||
throw new customError('start is higher than end', 'apierror');
|
||||
throw new CustomError('start is higher than end', 'apierror');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,16 +323,16 @@ exports.getChatHistory = async function (padID, start, end) {
|
|||
const chatHead = pad.chatHead;
|
||||
|
||||
// fall back to getting the whole chat-history if a parameter is missing
|
||||
if (!start || !end) {
|
||||
if (!start || !end) {
|
||||
start = 0;
|
||||
end = pad.chatHead;
|
||||
}
|
||||
|
||||
if (start > chatHead) {
|
||||
throw new customError('start is higher than the current chatHead', 'apierror');
|
||||
throw new CustomError('start is higher than the current chatHead', 'apierror');
|
||||
}
|
||||
if (end > chatHead) {
|
||||
throw new customError('end is higher than the current chatHead', 'apierror');
|
||||
throw new CustomError('end is higher than the current chatHead', 'apierror');
|
||||
}
|
||||
|
||||
// the the whole message-log and return it to the client
|
||||
|
@ -339,21 +342,22 @@ exports.getChatHistory = async function (padID, start, end) {
|
|||
};
|
||||
|
||||
/**
|
||||
appendChatMessage(padID, text, authorID, time), creates a chat message for the pad id, time is a timestamp
|
||||
appendChatMessage(padID, text, authorID, time), creates a chat message for the pad id,
|
||||
time is a timestamp
|
||||
|
||||
Example returns:
|
||||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.appendChatMessage = async function (padID, text, authorID, time) {
|
||||
exports.appendChatMessage = async (padID, text, authorID, time) => {
|
||||
// text is required
|
||||
if (typeof text !== 'string') {
|
||||
throw new customError('text is not a string', 'apierror');
|
||||
throw new CustomError('text is not a string', 'apierror');
|
||||
}
|
||||
|
||||
// if time is not an integer value set time to current timestamp
|
||||
if (time === undefined || !is_int(time)) {
|
||||
if (time === undefined || !isInt(time)) {
|
||||
time = Date.now();
|
||||
}
|
||||
|
||||
|
@ -375,7 +379,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {revisions: 56}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getRevisionsCount = async function (padID) {
|
||||
exports.getRevisionsCount = async (padID) => {
|
||||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
return {revisions: pad.getHeadRevisionNumber()};
|
||||
|
@ -389,7 +393,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {savedRevisions: 42}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getSavedRevisionsCount = async function (padID) {
|
||||
exports.getSavedRevisionsCount = async (padID) => {
|
||||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
return {savedRevisions: pad.getSavedRevisionsNumber()};
|
||||
|
@ -403,7 +407,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.listSavedRevisions = async function (padID) {
|
||||
exports.listSavedRevisions = async (padID) => {
|
||||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
return {savedRevisions: pad.getSavedRevisionsList()};
|
||||
|
@ -417,7 +421,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.saveRevision = async function (padID, rev) {
|
||||
exports.saveRevision = async (padID, rev) => {
|
||||
// check if rev is a number
|
||||
if (rev !== undefined) {
|
||||
rev = checkValidRev(rev);
|
||||
|
@ -430,7 +434,7 @@ exports.saveRevision = async function (padID, rev) {
|
|||
// the client asked for a special revision
|
||||
if (rev !== undefined) {
|
||||
if (rev > head) {
|
||||
throw new customError('rev is higher than the head revision of the pad', 'apierror');
|
||||
throw new CustomError('rev is higher than the head revision of the pad', 'apierror');
|
||||
}
|
||||
} else {
|
||||
rev = pad.getHeadRevisionNumber();
|
||||
|
@ -448,7 +452,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {lastEdited: 1340815946602}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getLastEdited = async function (padID) {
|
||||
exports.getLastEdited = async (padID) => {
|
||||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
const lastEdited = await pad.getLastEdit();
|
||||
|
@ -463,16 +467,16 @@ Example returns:
|
|||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"pad does already exist", data: null}
|
||||
*/
|
||||
exports.createPad = async function (padID, text) {
|
||||
exports.createPad = async (padID, text) => {
|
||||
if (padID) {
|
||||
// ensure there is no $ in the padID
|
||||
if (padID.indexOf('$') !== -1) {
|
||||
throw new customError("createPad can't create group pads", 'apierror');
|
||||
throw new CustomError("createPad can't create group pads", 'apierror');
|
||||
}
|
||||
|
||||
// check for url special characters
|
||||
if (padID.match(/(\/|\?|&|#)/)) {
|
||||
throw new customError('malformed padID: Remove special characters', 'apierror');
|
||||
throw new CustomError('malformed padID: Remove special characters', 'apierror');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -488,7 +492,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.deletePad = async function (padID) {
|
||||
exports.deletePad = async (padID) => {
|
||||
const pad = await getPadSafe(padID, true);
|
||||
await pad.remove();
|
||||
};
|
||||
|
@ -501,10 +505,10 @@ exports.deletePad = async function (padID) {
|
|||
{code:0, message:"ok", data:null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.restoreRevision = async function (padID, rev) {
|
||||
exports.restoreRevision = async (padID, rev) => {
|
||||
// check if rev is a number
|
||||
if (rev === undefined) {
|
||||
throw new customError('rev is not defined', 'apierror');
|
||||
throw new CustomError('rev is not defined', 'apierror');
|
||||
}
|
||||
rev = checkValidRev(rev);
|
||||
|
||||
|
@ -513,7 +517,7 @@ exports.restoreRevision = async function (padID, rev) {
|
|||
|
||||
// check if this is a valid revision
|
||||
if (rev > pad.getHeadRevisionNumber()) {
|
||||
throw new customError('rev is higher than the head revision of the pad', 'apierror');
|
||||
throw new CustomError('rev is higher than the head revision of the pad', 'apierror');
|
||||
}
|
||||
|
||||
const atext = await pad.getInternalRevisionAText(rev);
|
||||
|
@ -521,7 +525,7 @@ exports.restoreRevision = async function (padID, rev) {
|
|||
const oldText = pad.text();
|
||||
atext.text += '\n';
|
||||
|
||||
function eachAttribRun(attribs, func) {
|
||||
const eachAttribRun = (attribs, func) => {
|
||||
const attribsIter = Changeset.opIterator(attribs);
|
||||
let textIndex = 0;
|
||||
const newTextStart = 0;
|
||||
|
@ -534,7 +538,7 @@ exports.restoreRevision = async function (padID, rev) {
|
|||
}
|
||||
textIndex = nextIndex;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// create a new changeset with a helper builder object
|
||||
const builder = Changeset.builder(oldText.length);
|
||||
|
@ -569,7 +573,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {padID: destinationID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.copyPad = async function (sourceID, destinationID, force) {
|
||||
exports.copyPad = async (sourceID, destinationID, force) => {
|
||||
const pad = await getPadSafe(sourceID, true);
|
||||
await pad.copy(destinationID, force);
|
||||
};
|
||||
|
@ -583,7 +587,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {padID: destinationID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.copyPadWithoutHistory = async function (sourceID, destinationID, force) {
|
||||
exports.copyPadWithoutHistory = async (sourceID, destinationID, force) => {
|
||||
const pad = await getPadSafe(sourceID, true);
|
||||
await pad.copyPadWithoutHistory(destinationID, force);
|
||||
};
|
||||
|
@ -597,7 +601,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {padID: destinationID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.movePad = async function (sourceID, destinationID, force) {
|
||||
exports.movePad = async (sourceID, destinationID, force) => {
|
||||
const pad = await getPadSafe(sourceID, true);
|
||||
await pad.copy(destinationID, force);
|
||||
await pad.remove();
|
||||
|
@ -611,7 +615,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getReadOnlyID = async function (padID) {
|
||||
exports.getReadOnlyID = async (padID) => {
|
||||
// we don't need the pad object, but this function does all the security stuff for us
|
||||
await getPadSafe(padID, true);
|
||||
|
||||
|
@ -629,11 +633,11 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {padID: padID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getPadID = async function (roID) {
|
||||
exports.getPadID = async (roID) => {
|
||||
// get the PadId
|
||||
const padID = await readOnlyManager.getPadId(roID);
|
||||
if (padID === null) {
|
||||
throw new customError('padID does not exist', 'apierror');
|
||||
if (padID == null) {
|
||||
throw new CustomError('padID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
return {padID};
|
||||
|
@ -647,7 +651,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.setPublicStatus = async function (padID, publicStatus) {
|
||||
exports.setPublicStatus = async (padID, publicStatus) => {
|
||||
// ensure this is a group pad
|
||||
checkGroupPad(padID, 'publicStatus');
|
||||
|
||||
|
@ -670,7 +674,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {publicStatus: true}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getPublicStatus = async function (padID) {
|
||||
exports.getPublicStatus = async (padID) => {
|
||||
// ensure this is a group pad
|
||||
checkGroupPad(padID, 'publicStatus');
|
||||
|
||||
|
@ -687,7 +691,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {authorIDs : ["a.s8oes9dhwrvt0zif", "a.akf8finncvomlqva"]}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.listAuthorsOfPad = async function (padID) {
|
||||
exports.listAuthorsOfPad = async (padID) => {
|
||||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
const authorIDs = pad.getAllAuthors();
|
||||
|
@ -717,7 +721,7 @@ Example returns:
|
|||
{code: 1, message:"padID does not exist"}
|
||||
*/
|
||||
|
||||
exports.sendClientsMessage = async function (padID, msg) {
|
||||
exports.sendClientsMessage = async (padID, msg) => {
|
||||
const pad = await getPadSafe(padID, true);
|
||||
padMessageHandler.handleCustomMessage(padID, msg);
|
||||
};
|
||||
|
@ -730,7 +734,7 @@ Example returns:
|
|||
{"code":0,"message":"ok","data":null}
|
||||
{"code":4,"message":"no or wrong API Key","data":null}
|
||||
*/
|
||||
exports.checkToken = async function () {
|
||||
exports.checkToken = async () => {
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -741,7 +745,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: {chatHead: 42}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
*/
|
||||
exports.getChatHead = async function (padID) {
|
||||
exports.getChatHead = async (padID) => {
|
||||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
return {chatHead: pad.chatHead};
|
||||
|
@ -751,11 +755,21 @@ exports.getChatHead = async function (padID) {
|
|||
createDiffHTML(padID, startRev, endRev) returns an object of diffs from 2 points in a pad
|
||||
|
||||
Example returns:
|
||||
|
||||
{"code":0,"message":"ok","data":{"html":"<style>\n.authora_HKIv23mEbachFYfH {background-color: #a979d9}\n.authora_n4gEeMLsv1GivNeh {background-color: #a9b5d9}\n.removed {text-decoration: line-through; -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)'; filter: alpha(opacity=80); opacity: 0.8; }\n</style>Welcome to Etherpad!<br><br>This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!<br><br>Get involved with Etherpad at <a href=\"http://etherpad.org\">http://etherpad.org</a><br><span class=\"authora_HKIv23mEbachFYfH\">aw</span><br><br>","authors":["a.HKIv23mEbachFYfH",""]}}
|
||||
{
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"html": "...",
|
||||
"authors": [
|
||||
"a.HKIv23mEbachFYfH",
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
{"code":4,"message":"no or wrong API Key","data":null}
|
||||
|
||||
*/
|
||||
exports.createDiffHTML = async function (padID, startRev, endRev) {
|
||||
exports.createDiffHTML = async (padID, startRev, endRev) => {
|
||||
// check if startRev is a number
|
||||
if (startRev !== undefined) {
|
||||
startRev = checkValidRev(startRev);
|
||||
|
@ -768,8 +782,9 @@ exports.createDiffHTML = async function (padID, startRev, endRev) {
|
|||
|
||||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
let padDiff;
|
||||
try {
|
||||
var padDiff = new PadDiff(pad, startRev, endRev);
|
||||
padDiff = new PadDiff(pad, startRev, endRev);
|
||||
} catch (e) {
|
||||
throw {stop: e.message};
|
||||
}
|
||||
|
@ -793,7 +808,7 @@ exports.createDiffHTML = async function (padID, startRev, endRev) {
|
|||
{"code":4,"message":"no or wrong API Key","data":null}
|
||||
*/
|
||||
|
||||
exports.getStats = async function () {
|
||||
exports.getStats = async () => {
|
||||
const sessionInfos = padMessageHandler.sessioninfos;
|
||||
|
||||
const sessionKeys = Object.keys(sessionInfos);
|
||||
|
@ -813,20 +828,18 @@ exports.getStats = async function () {
|
|||
**************************** */
|
||||
|
||||
// checks if a number is an int
|
||||
function is_int(value) {
|
||||
return (parseFloat(value) == parseInt(value, 10)) && !isNaN(value);
|
||||
}
|
||||
const isInt = (value) => (parseFloat(value) === parseInt(value, 10)) && !isNaN(value);
|
||||
|
||||
// gets a pad safe
|
||||
async function getPadSafe(padID, shouldExist, text) {
|
||||
// check if padID is a string
|
||||
if (typeof padID !== 'string') {
|
||||
throw new customError('padID is not a string', 'apierror');
|
||||
throw new CustomError('padID is not a string', 'apierror');
|
||||
}
|
||||
|
||||
// check if the padID maches the requirements
|
||||
if (!padManager.isValidPadId(padID)) {
|
||||
throw new customError('padID did not match requirements', 'apierror');
|
||||
throw new CustomError('padID did not match requirements', 'apierror');
|
||||
}
|
||||
|
||||
// check if the pad exists
|
||||
|
@ -834,12 +847,12 @@ async function getPadSafe(padID, shouldExist, text) {
|
|||
|
||||
if (!exists && shouldExist) {
|
||||
// does not exist, but should
|
||||
throw new customError('padID does not exist', 'apierror');
|
||||
throw new CustomError('padID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
if (exists && !shouldExist) {
|
||||
// does exist, but shouldn't
|
||||
throw new customError('padID does already exist', 'apierror');
|
||||
throw new CustomError('padID does already exist', 'apierror');
|
||||
}
|
||||
|
||||
// pad exists, let's get it
|
||||
|
@ -848,33 +861,34 @@ async function getPadSafe(padID, shouldExist, text) {
|
|||
|
||||
// checks if a rev is a legal number
|
||||
// pre-condition is that `rev` is not undefined
|
||||
function checkValidRev(rev) {
|
||||
const checkValidRev = (rev) => {
|
||||
if (typeof rev !== 'number') {
|
||||
rev = parseInt(rev, 10);
|
||||
}
|
||||
|
||||
// check if rev is a number
|
||||
if (isNaN(rev)) {
|
||||
throw new customError('rev is not a number', 'apierror');
|
||||
throw new CustomError('rev is not a number', 'apierror');
|
||||
}
|
||||
|
||||
// ensure this is not a negative number
|
||||
if (rev < 0) {
|
||||
throw new customError('rev is not a negative number', 'apierror');
|
||||
throw new CustomError('rev is not a negative number', 'apierror');
|
||||
}
|
||||
|
||||
// ensure this is not a float value
|
||||
if (!is_int(rev)) {
|
||||
throw new customError('rev is a float value', 'apierror');
|
||||
if (!isInt(rev)) {
|
||||
throw new CustomError('rev is a float value', 'apierror');
|
||||
}
|
||||
|
||||
return rev;
|
||||
}
|
||||
};
|
||||
|
||||
// checks if a padID is part of a group
|
||||
function checkGroupPad(padID, field) {
|
||||
const checkGroupPad = (padID, field) => {
|
||||
// ensure this is a group pad
|
||||
if (padID && padID.indexOf('$') === -1) {
|
||||
throw new customError(`You can only get/set the ${field} of pads that belong to a group`, 'apierror');
|
||||
}
|
||||
throw new CustomError(
|
||||
`You can only get/set the ${field} of pads that belong to a group`, 'apierror');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The AuthorManager controlls all information about the Pad authors
|
||||
*/
|
||||
|
@ -19,11 +20,10 @@
|
|||
*/
|
||||
|
||||
const db = require('./DB');
|
||||
const customError = require('../utils/customError');
|
||||
const randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||
const CustomError = require('../utils/customError');
|
||||
const randomString = require('../../static/js/pad_utils').randomString;
|
||||
|
||||
exports.getColorPalette = function () {
|
||||
return [
|
||||
exports.getColorPalette = () => [
|
||||
'#ffc7c7',
|
||||
'#fff1c7',
|
||||
'#e3ffc7',
|
||||
|
@ -89,15 +89,14 @@ exports.getColorPalette = function () {
|
|||
'#f8d2a0',
|
||||
'#b3b3e6',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the author exists
|
||||
*/
|
||||
exports.doesAuthorExist = async function (authorID) {
|
||||
exports.doesAuthorExist = async (authorID) => {
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
||||
return author !== null;
|
||||
return author != null;
|
||||
};
|
||||
|
||||
/* exported for backwards compatibility */
|
||||
|
@ -107,7 +106,7 @@ exports.doesAuthorExists = exports.doesAuthorExist;
|
|||
* Returns the AuthorID for a token.
|
||||
* @param {String} token The token
|
||||
*/
|
||||
exports.getAuthor4Token = async function (token) {
|
||||
exports.getAuthor4Token = async (token) => {
|
||||
const author = await mapAuthorWithDBKey('token2author', token);
|
||||
|
||||
// return only the sub value authorID
|
||||
|
@ -119,7 +118,7 @@ exports.getAuthor4Token = async function (token) {
|
|||
* @param {String} token The mapper
|
||||
* @param {String} name The name of the author (optional)
|
||||
*/
|
||||
exports.createAuthorIfNotExistsFor = async function (authorMapper, name) {
|
||||
exports.createAuthorIfNotExistsFor = async (authorMapper, name) => {
|
||||
const author = await mapAuthorWithDBKey('mapper2author', authorMapper);
|
||||
|
||||
if (name) {
|
||||
|
@ -140,7 +139,7 @@ async function mapAuthorWithDBKey(mapperkey, mapper) {
|
|||
// try to map to an author
|
||||
const author = await db.get(`${mapperkey}:${mapper}`);
|
||||
|
||||
if (author === null) {
|
||||
if (author == null) {
|
||||
// there is no author with this mapper, so create one
|
||||
const author = await exports.createAuthor(null);
|
||||
|
||||
|
@ -163,7 +162,7 @@ async function mapAuthorWithDBKey(mapperkey, mapper) {
|
|||
* Internal function that creates the database entry for an author
|
||||
* @param {String} name The name of the author
|
||||
*/
|
||||
exports.createAuthor = function (name) {
|
||||
exports.createAuthor = (name) => {
|
||||
// create the new author name
|
||||
const author = `a.${randomString(16)}`;
|
||||
|
||||
|
@ -185,50 +184,40 @@ exports.createAuthor = function (name) {
|
|||
* Returns the Author Obj of the author
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.getAuthor = function (author) {
|
||||
// NB: result is already a Promise
|
||||
return db.get(`globalAuthor:${author}`);
|
||||
};
|
||||
exports.getAuthor = (author) => db.get(`globalAuthor:${author}`);
|
||||
|
||||
/**
|
||||
* Returns the color Id of the author
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.getAuthorColorId = function (author) {
|
||||
return db.getSub(`globalAuthor:${author}`, ['colorId']);
|
||||
};
|
||||
exports.getAuthorColorId = (author) => db.getSub(`globalAuthor:${author}`, ['colorId']);
|
||||
|
||||
/**
|
||||
* Sets the color Id of the author
|
||||
* @param {String} author The id of the author
|
||||
* @param {String} colorId The color id of the author
|
||||
*/
|
||||
exports.setAuthorColorId = function (author, colorId) {
|
||||
return db.setSub(`globalAuthor:${author}`, ['colorId'], colorId);
|
||||
};
|
||||
exports.setAuthorColorId = (author, colorId) => db.setSub(
|
||||
`globalAuthor:${author}`, ['colorId'], colorId);
|
||||
|
||||
/**
|
||||
* Returns the name of the author
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.getAuthorName = function (author) {
|
||||
return db.getSub(`globalAuthor:${author}`, ['name']);
|
||||
};
|
||||
exports.getAuthorName = (author) => db.getSub(`globalAuthor:${author}`, ['name']);
|
||||
|
||||
/**
|
||||
* Sets the name of the author
|
||||
* @param {String} author The id of the author
|
||||
* @param {String} name The name of the author
|
||||
*/
|
||||
exports.setAuthorName = function (author, name) {
|
||||
return db.setSub(`globalAuthor:${author}`, ['name'], name);
|
||||
};
|
||||
exports.setAuthorName = (author, name) => db.setSub(`globalAuthor:${author}`, ['name'], name);
|
||||
|
||||
/**
|
||||
* Returns an array of all pads this author contributed to
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.listPadsOfAuthor = async function (authorID) {
|
||||
exports.listPadsOfAuthor = async (authorID) => {
|
||||
/* There are two other places where this array is manipulated:
|
||||
* (1) When the author is added to a pad, the author object is also updated
|
||||
* (2) When a pad is deleted, each author of that pad is also updated
|
||||
|
@ -237,9 +226,9 @@ exports.listPadsOfAuthor = async function (authorID) {
|
|||
// get the globalAuthor
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
||||
if (author === null) {
|
||||
if (author == null) {
|
||||
// author does not exist
|
||||
throw new customError('authorID does not exist', 'apierror');
|
||||
throw new CustomError('authorID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// everything is fine, return the pad IDs
|
||||
|
@ -253,11 +242,11 @@ exports.listPadsOfAuthor = async function (authorID) {
|
|||
* @param {String} author The id of the author
|
||||
* @param {String} padID The id of the pad the author contributes to
|
||||
*/
|
||||
exports.addPad = async function (authorID, padID) {
|
||||
exports.addPad = async (authorID, padID) => {
|
||||
// get the entry
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
||||
if (author === null) return;
|
||||
if (author == null) return;
|
||||
|
||||
/*
|
||||
* ACHTUNG: padIDs can also be undefined, not just null, so it is not possible
|
||||
|
@ -280,12 +269,12 @@ exports.addPad = async function (authorID, padID) {
|
|||
* @param {String} author The id of the author
|
||||
* @param {String} padID The id of the pad the author contributes to
|
||||
*/
|
||||
exports.removePad = async function (authorID, padID) {
|
||||
exports.removePad = async (authorID, padID) => {
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
||||
if (author === null) return;
|
||||
if (author == null) return;
|
||||
|
||||
if (author.padIDs !== null) {
|
||||
if (author.padIDs != null) {
|
||||
// remove pad from author
|
||||
delete author.padIDs[padID];
|
||||
await db.set(`globalAuthor:${authorID}`, author);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The Group Manager provides functions to manage groups in the database
|
||||
*/
|
||||
|
@ -18,13 +19,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const customError = require('../utils/customError');
|
||||
const randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
|
||||
const CustomError = require('../utils/customError');
|
||||
const randomString = require('../../static/js/pad_utils').randomString;
|
||||
const db = require('./DB');
|
||||
const padManager = require('./PadManager');
|
||||
const sessionManager = require('./SessionManager');
|
||||
|
||||
exports.listAllGroups = async function () {
|
||||
exports.listAllGroups = async () => {
|
||||
let groups = await db.get('groups');
|
||||
groups = groups || {};
|
||||
|
||||
|
@ -32,17 +33,20 @@ exports.listAllGroups = async function () {
|
|||
return {groupIDs};
|
||||
};
|
||||
|
||||
exports.deleteGroup = async function (groupID) {
|
||||
exports.deleteGroup = async (groupID) => {
|
||||
const group = await db.get(`group:${groupID}`);
|
||||
|
||||
// ensure group exists
|
||||
if (group == null) {
|
||||
// group does not exist
|
||||
throw new customError('groupID does not exist', 'apierror');
|
||||
throw new CustomError('groupID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// iterate through all pads of this group and delete them (in parallel)
|
||||
await Promise.all(Object.keys(group.pads).map((padID) => padManager.getPad(padID).then((pad) => pad.remove())));
|
||||
await Promise.all(Object.keys(group.pads)
|
||||
.map((padID) => padManager.getPad(padID)
|
||||
.then((pad) => pad.remove())
|
||||
));
|
||||
|
||||
// iterate through group2sessions and delete all sessions
|
||||
const group2sessions = await db.get(`group2sessions:${groupID}`);
|
||||
|
@ -76,14 +80,14 @@ exports.deleteGroup = async function (groupID) {
|
|||
await db.set('groups', newGroups);
|
||||
};
|
||||
|
||||
exports.doesGroupExist = async function (groupID) {
|
||||
exports.doesGroupExist = async (groupID) => {
|
||||
// try to get the group entry
|
||||
const group = await db.get(`group:${groupID}`);
|
||||
|
||||
return (group != null);
|
||||
};
|
||||
|
||||
exports.createGroup = async function () {
|
||||
exports.createGroup = async () => {
|
||||
// search for non existing groupID
|
||||
const groupID = `g.${randomString(16)}`;
|
||||
|
||||
|
@ -103,10 +107,10 @@ exports.createGroup = async function () {
|
|||
return {groupID};
|
||||
};
|
||||
|
||||
exports.createGroupIfNotExistsFor = async function (groupMapper) {
|
||||
exports.createGroupIfNotExistsFor = async (groupMapper) => {
|
||||
// ensure mapper is optional
|
||||
if (typeof groupMapper !== 'string') {
|
||||
throw new customError('groupMapper is not a string', 'apierror');
|
||||
throw new CustomError('groupMapper is not a string', 'apierror');
|
||||
}
|
||||
|
||||
// try to get a group for this mapper
|
||||
|
@ -128,7 +132,7 @@ exports.createGroupIfNotExistsFor = async function (groupMapper) {
|
|||
return result;
|
||||
};
|
||||
|
||||
exports.createGroupPad = async function (groupID, padName, text) {
|
||||
exports.createGroupPad = async (groupID, padName, text) => {
|
||||
// create the padID
|
||||
const padID = `${groupID}$${padName}`;
|
||||
|
||||
|
@ -136,7 +140,7 @@ exports.createGroupPad = async function (groupID, padName, text) {
|
|||
const groupExists = await exports.doesGroupExist(groupID);
|
||||
|
||||
if (!groupExists) {
|
||||
throw new customError('groupID does not exist', 'apierror');
|
||||
throw new CustomError('groupID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// ensure pad doesn't exist already
|
||||
|
@ -144,7 +148,7 @@ exports.createGroupPad = async function (groupID, padName, text) {
|
|||
|
||||
if (padExists) {
|
||||
// pad exists already
|
||||
throw new customError('padName does already exist', 'apierror');
|
||||
throw new CustomError('padName does already exist', 'apierror');
|
||||
}
|
||||
|
||||
// create the pad
|
||||
|
@ -156,12 +160,12 @@ exports.createGroupPad = async function (groupID, padName, text) {
|
|||
return {padID};
|
||||
};
|
||||
|
||||
exports.listPads = async function (groupID) {
|
||||
exports.listPads = async (groupID) => {
|
||||
const exists = await exports.doesGroupExist(groupID);
|
||||
|
||||
// ensure the group exists
|
||||
if (!exists) {
|
||||
throw new customError('groupID does not exist', 'apierror');
|
||||
throw new CustomError('groupID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// group exists, let's get the pads
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The pad object, defined with joose
|
||||
*/
|
||||
|
||||
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const AttributePool = require('ep_etherpad-lite/static/js/AttributePool');
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
const AttributePool = require('../../static/js/AttributePool');
|
||||
const db = require('./DB');
|
||||
const settings = require('../utils/Settings');
|
||||
const authorManager = require('./AuthorManager');
|
||||
const padManager = require('./PadManager');
|
||||
const padMessageHandler = require('../handler/PadMessageHandler');
|
||||
const groupManager = require('./GroupManager');
|
||||
const customError = require('../utils/customError');
|
||||
const CustomError = require('../utils/customError');
|
||||
const readOnlyManager = require('./ReadOnlyManager');
|
||||
const crypto = require('crypto');
|
||||
const randomString = require('../utils/randomstring');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
const promises = require('../utils/promises');
|
||||
|
||||
// serialization/deserialization attributes
|
||||
|
@ -23,13 +23,14 @@ const attributeBlackList = ['id'];
|
|||
const jsonableList = ['pool'];
|
||||
|
||||
/**
|
||||
* Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
|
||||
* Copied from the Etherpad source code. It converts Windows line breaks to Unix
|
||||
* line breaks and convert Tabs to spaces
|
||||
* @param txt
|
||||
*/
|
||||
exports.cleanText = function (txt) {
|
||||
return txt.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\t/g, ' ').replace(/\xa0/g, ' ');
|
||||
};
|
||||
|
||||
exports.cleanText = (txt) => txt.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '\n')
|
||||
.replace(/\t/g, ' ')
|
||||
.replace(/\xa0/g, ' ');
|
||||
|
||||
const Pad = function Pad(id) {
|
||||
this.atext = Changeset.makeAText('\n');
|
||||
|
@ -56,7 +57,7 @@ Pad.prototype.getSavedRevisionsNumber = function getSavedRevisionsNumber() {
|
|||
};
|
||||
|
||||
Pad.prototype.getSavedRevisionsList = function getSavedRevisionsList() {
|
||||
const savedRev = new Array();
|
||||
const savedRev = [];
|
||||
for (const rev in this.savedRevisions) {
|
||||
savedRev.push(this.savedRevisions[rev].revNum);
|
||||
}
|
||||
|
@ -85,11 +86,11 @@ Pad.prototype.appendRevision = async function appendRevision(aChangeset, author)
|
|||
newRevData.meta.timestamp = Date.now();
|
||||
|
||||
// ex. getNumForAuthor
|
||||
if (author != '') {
|
||||
if (author !== '') {
|
||||
this.pool.putAttrib(['author', author || '']);
|
||||
}
|
||||
|
||||
if (newRev % 100 == 0) {
|
||||
if (newRev % 100 === 0) {
|
||||
newRevData.meta.pool = this.pool;
|
||||
newRevData.meta.atext = this.atext;
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ Pad.prototype.appendRevision = async function appendRevision(aChangeset, author)
|
|||
p.push(authorManager.addPad(author, this.id));
|
||||
}
|
||||
|
||||
if (this.head == 0) {
|
||||
if (this.head === 0) {
|
||||
hooks.callAll('padCreate', {pad: this, author});
|
||||
} else {
|
||||
hooks.callAll('padUpdate', {pad: this, author, revs: newRev, changeset: aChangeset});
|
||||
|
@ -153,7 +154,7 @@ Pad.prototype.getAllAuthors = function getAllAuthors() {
|
|||
const authors = [];
|
||||
|
||||
for (const key in this.pool.numToAttrib) {
|
||||
if (this.pool.numToAttrib[key][0] == 'author' && this.pool.numToAttrib[key][1] != '') {
|
||||
if (this.pool.numToAttrib[key][0] === 'author' && this.pool.numToAttrib[key][1] !== '') {
|
||||
authors.push(this.pool.numToAttrib[key][1]);
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +178,8 @@ Pad.prototype.getInternalRevisionAText = async function getInternalRevisionAText
|
|||
|
||||
// get all needed changesets
|
||||
const changesets = [];
|
||||
await Promise.all(neededChangesets.map((item) => this.getRevisionChangeset(item).then((changeset) => {
|
||||
await Promise.all(
|
||||
neededChangesets.map((item) => this.getRevisionChangeset(item).then((changeset) => {
|
||||
changesets[item] = changeset;
|
||||
})));
|
||||
|
||||
|
@ -204,7 +206,8 @@ Pad.prototype.getAllAuthorColors = async function getAllAuthorColors() {
|
|||
const returnTable = {};
|
||||
const colorPalette = authorManager.getColorPalette();
|
||||
|
||||
await Promise.all(authors.map((author) => authorManager.getAuthorColorId(author).then((colorId) => {
|
||||
await Promise.all(
|
||||
authors.map((author) => authorManager.getAuthorColorId(author).then((colorId) => {
|
||||
// colorId might be a hex color or an number out of the palette
|
||||
returnTable[author] = colorPalette[colorId] || colorId;
|
||||
})));
|
||||
|
@ -227,7 +230,7 @@ Pad.prototype.getValidRevisionRange = function getValidRevisionRange(startRev, e
|
|||
endRev = head;
|
||||
}
|
||||
|
||||
if (startRev !== null && endRev !== null) {
|
||||
if (startRev != null && endRev != null) {
|
||||
return {startRev, endRev};
|
||||
}
|
||||
return null;
|
||||
|
@ -251,7 +254,7 @@ Pad.prototype.setText = async function setText(newText) {
|
|||
// We want to ensure the pad still ends with a \n, but otherwise keep
|
||||
// getText() and setText() consistent.
|
||||
let changeset;
|
||||
if (newText[newText.length - 1] == '\n') {
|
||||
if (newText[newText.length - 1] === '\n') {
|
||||
changeset = Changeset.makeSplice(oldText, 0, oldText.length, newText);
|
||||
} else {
|
||||
changeset = Changeset.makeSplice(oldText, 0, oldText.length - 1, newText);
|
||||
|
@ -304,7 +307,8 @@ Pad.prototype.getChatMessages = async function getChatMessages(start, end) {
|
|||
|
||||
// get all entries out of the database
|
||||
const entries = [];
|
||||
await Promise.all(neededEntries.map((entryObject) => this.getChatMessage(entryObject.entryNum).then((entry) => {
|
||||
await Promise.all(
|
||||
neededEntries.map((entryObject) => this.getChatMessage(entryObject.entryNum).then((entry) => {
|
||||
entries[entryObject.order] = entry;
|
||||
})));
|
||||
|
||||
|
@ -384,14 +388,16 @@ Pad.prototype.copy = async function copy(destinationID, force) {
|
|||
// copy all chat messages
|
||||
const chatHead = this.chatHead;
|
||||
for (let i = 0; i <= chatHead; ++i) {
|
||||
const p = db.get(`pad:${sourceID}:chat:${i}`).then((chat) => db.set(`pad:${destinationID}:chat:${i}`, chat));
|
||||
const p = db.get(`pad:${sourceID}:chat:${i}`)
|
||||
.then((chat) => db.set(`pad:${destinationID}:chat:${i}`, chat));
|
||||
promises.push(p);
|
||||
}
|
||||
|
||||
// copy all revisions
|
||||
const revHead = this.head;
|
||||
for (let i = 0; i <= revHead; ++i) {
|
||||
const p = db.get(`pad:${sourceID}:revs:${i}`).then((rev) => db.set(`pad:${destinationID}:revs:${i}`, rev));
|
||||
const p = db.get(`pad:${sourceID}:revs:${i}`)
|
||||
.then((rev) => db.set(`pad:${destinationID}:revs:${i}`, rev));
|
||||
promises.push(p);
|
||||
}
|
||||
|
||||
|
@ -426,7 +432,7 @@ Pad.prototype.checkIfGroupExistAndReturnIt = async function checkIfGroupExistAnd
|
|||
|
||||
// group does not exist
|
||||
if (!groupExists) {
|
||||
throw new customError('groupID does not exist for destinationID', 'apierror');
|
||||
throw new CustomError('groupID does not exist for destinationID', 'apierror');
|
||||
}
|
||||
}
|
||||
return destGroupID;
|
||||
|
@ -446,7 +452,7 @@ Pad.prototype.removePadIfForceIsTrueAndAlreadyExist = async function removePadIf
|
|||
if (exists) {
|
||||
if (!force) {
|
||||
console.error('erroring out without force');
|
||||
throw new customError('destinationID already exists', 'apierror');
|
||||
throw new CustomError('destinationID already exists', 'apierror');
|
||||
}
|
||||
|
||||
// exists and forcing
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The Pad Manager is a Factory for pad Objects
|
||||
*/
|
||||
|
@ -18,7 +19,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const customError = require('../utils/customError');
|
||||
const CustomError = require('../utils/customError');
|
||||
const Pad = require('../db/Pad').Pad;
|
||||
const db = require('./DB');
|
||||
|
||||
|
@ -109,22 +110,22 @@ const padList = {
|
|||
* @param id A String with the id of the pad
|
||||
* @param {Function} callback
|
||||
*/
|
||||
exports.getPad = async function (id, text) {
|
||||
exports.getPad = async (id, text) => {
|
||||
// check if this is a valid padId
|
||||
if (!exports.isValidPadId(id)) {
|
||||
throw new customError(`${id} is not a valid padId`, 'apierror');
|
||||
throw new CustomError(`${id} is not a valid padId`, 'apierror');
|
||||
}
|
||||
|
||||
// check if this is a valid text
|
||||
if (text != null) {
|
||||
// check if text is a string
|
||||
if (typeof text !== 'string') {
|
||||
throw new customError('text is not a string', 'apierror');
|
||||
throw new CustomError('text is not a string', 'apierror');
|
||||
}
|
||||
|
||||
// check if text is less than 100k chars
|
||||
if (text.length > 100000) {
|
||||
throw new customError('text must be less than 100k chars', 'apierror');
|
||||
throw new CustomError('text must be less than 100k chars', 'apierror');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,14 +147,14 @@ exports.getPad = async function (id, text) {
|
|||
return pad;
|
||||
};
|
||||
|
||||
exports.listAllPads = async function () {
|
||||
exports.listAllPads = async () => {
|
||||
const padIDs = await padList.getPads();
|
||||
|
||||
return {padIDs};
|
||||
};
|
||||
|
||||
// checks if a pad exists
|
||||
exports.doesPadExist = async function (padId) {
|
||||
exports.doesPadExist = async (padId) => {
|
||||
const value = await db.get(`pad:${padId}`);
|
||||
|
||||
return (value != null && value.atext);
|
||||
|
@ -189,9 +190,7 @@ exports.sanitizePadId = async function sanitizePadId(padId) {
|
|||
return padId;
|
||||
};
|
||||
|
||||
exports.isValidPadId = function (padId) {
|
||||
return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
|
||||
};
|
||||
exports.isValidPadId = (padId) => /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
|
||||
|
||||
/**
|
||||
* Removes the pad from database and unloads it.
|
||||
|
@ -204,6 +203,6 @@ exports.removePad = async (padId) => {
|
|||
};
|
||||
|
||||
// removes a pad from the cache
|
||||
exports.unloadPad = function (padId) {
|
||||
exports.unloadPad = (padId) => {
|
||||
globalPads.remove(padId);
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The ReadOnlyManager manages the database and rendering releated to read only pads
|
||||
*/
|
||||
|
@ -27,15 +28,13 @@ const randomString = require('../utils/randomstring');
|
|||
* checks if the id pattern matches a read-only pad id
|
||||
* @param {String} the pad's id
|
||||
*/
|
||||
exports.isReadOnlyId = function (id) {
|
||||
return id.indexOf('r.') === 0;
|
||||
};
|
||||
exports.isReadOnlyId = (id) => id.indexOf('r.') === 0;
|
||||
|
||||
/**
|
||||
* returns a read only id for a pad
|
||||
* @param {String} padId the id of the pad
|
||||
*/
|
||||
exports.getReadOnlyId = async function (padId) {
|
||||
exports.getReadOnlyId = async (padId) => {
|
||||
// check if there is a pad2readonly entry
|
||||
let readOnlyId = await db.get(`pad2readonly:${padId}`);
|
||||
|
||||
|
@ -53,15 +52,13 @@ exports.getReadOnlyId = async function (padId) {
|
|||
* returns the padId for a read only id
|
||||
* @param {String} readOnlyId read only id
|
||||
*/
|
||||
exports.getPadId = function (readOnlyId) {
|
||||
return db.get(`readonly2pad:${readOnlyId}`);
|
||||
};
|
||||
exports.getPadId = (readOnlyId) => db.get(`readonly2pad:${readOnlyId}`);
|
||||
|
||||
/**
|
||||
* returns the padId and readonlyPadId in an object for any id
|
||||
* @param {String} padIdOrReadonlyPadId read only id or real pad id
|
||||
*/
|
||||
exports.getIds = async function (id) {
|
||||
exports.getIds = async (id) => {
|
||||
const readonly = (id.indexOf('r.') === 0);
|
||||
|
||||
// Might be null, if this is an unknown read-only id
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Controls the security of pad access
|
||||
*/
|
||||
|
@ -19,7 +20,7 @@
|
|||
*/
|
||||
|
||||
const authorManager = require('./AuthorManager');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks.js');
|
||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
||||
const padManager = require('./PadManager');
|
||||
const sessionManager = require('./SessionManager');
|
||||
const settings = require('../utils/Settings');
|
||||
|
@ -47,7 +48,7 @@ const DENY = Object.freeze({accessStatus: 'deny'});
|
|||
* WARNING: Tokens and session IDs MUST be kept secret, otherwise users will be able to impersonate
|
||||
* each other (which might allow them to gain privileges).
|
||||
*/
|
||||
exports.checkAccess = async function (padID, sessionCookie, token, userSettings) {
|
||||
exports.checkAccess = async (padID, sessionCookie, token, userSettings) => {
|
||||
if (!padID) {
|
||||
authLogger.debug('access denied: missing padID');
|
||||
return DENY;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The Session Manager provides functions to manage session in the database, it only provides session management for sessions created by the API
|
||||
* The Session Manager provides functions to manage session in the database,
|
||||
* it only provides session management for sessions created by the API
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -18,7 +20,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const customError = require('../utils/customError');
|
||||
const CustomError = require('../utils/customError');
|
||||
const promises = require('../utils/promises');
|
||||
const randomString = require('../utils/randomstring');
|
||||
const db = require('./DB');
|
||||
|
@ -40,7 +42,8 @@ exports.findAuthorID = async (groupID, sessionCookie) => {
|
|||
* Sometimes, RFC 6265-compliant web servers may send back a cookie whose
|
||||
* value is enclosed in double quotes, such as:
|
||||
*
|
||||
* Set-Cookie: sessionCookie="s.37cf5299fbf981e14121fba3a588c02b,s.2b21517bf50729d8130ab85736a11346"; Version=1; Path=/; Domain=localhost; Discard
|
||||
* Set-Cookie: sessionCookie="s.37cf5299fbf981e14121fba3a588c02b,
|
||||
* s.2b21517bf50729d8130ab85736a11346"; Version=1; Path=/; Domain=localhost; Discard
|
||||
*
|
||||
* Where the double quotes at the start and the end of the header value are
|
||||
* just delimiters. This is perfectly legal: Etherpad parsing logic should
|
||||
|
@ -78,26 +81,26 @@ exports.findAuthorID = async (groupID, sessionCookie) => {
|
|||
return sessionInfo.authorID;
|
||||
};
|
||||
|
||||
exports.doesSessionExist = async function (sessionID) {
|
||||
exports.doesSessionExist = async (sessionID) => {
|
||||
// check if the database entry of this session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
return (session !== null);
|
||||
return (session != null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new session between an author and a group
|
||||
*/
|
||||
exports.createSession = async function (groupID, authorID, validUntil) {
|
||||
exports.createSession = async (groupID, authorID, validUntil) => {
|
||||
// check if the group exists
|
||||
const groupExists = await groupManager.doesGroupExist(groupID);
|
||||
if (!groupExists) {
|
||||
throw new customError('groupID does not exist', 'apierror');
|
||||
throw new CustomError('groupID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// check if the author exists
|
||||
const authorExists = await authorManager.doesAuthorExist(authorID);
|
||||
if (!authorExists) {
|
||||
throw new customError('authorID does not exist', 'apierror');
|
||||
throw new CustomError('authorID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// try to parse validUntil if it's not a number
|
||||
|
@ -107,22 +110,22 @@ exports.createSession = async function (groupID, authorID, validUntil) {
|
|||
|
||||
// check it's a valid number
|
||||
if (isNaN(validUntil)) {
|
||||
throw new customError('validUntil is not a number', 'apierror');
|
||||
throw new CustomError('validUntil is not a number', 'apierror');
|
||||
}
|
||||
|
||||
// ensure this is not a negative number
|
||||
if (validUntil < 0) {
|
||||
throw new customError('validUntil is a negative number', 'apierror');
|
||||
throw new CustomError('validUntil is a negative number', 'apierror');
|
||||
}
|
||||
|
||||
// ensure this is not a float value
|
||||
if (!is_int(validUntil)) {
|
||||
throw new customError('validUntil is a float value', 'apierror');
|
||||
if (!isInt(validUntil)) {
|
||||
throw new CustomError('validUntil is a float value', 'apierror');
|
||||
}
|
||||
|
||||
// check if validUntil is in the future
|
||||
if (validUntil < Math.floor(Date.now() / 1000)) {
|
||||
throw new customError('validUntil is in the past', 'apierror');
|
||||
throw new CustomError('validUntil is in the past', 'apierror');
|
||||
}
|
||||
|
||||
// generate sessionID
|
||||
|
@ -170,13 +173,13 @@ exports.createSession = async function (groupID, authorID, validUntil) {
|
|||
return {sessionID};
|
||||
};
|
||||
|
||||
exports.getSessionInfo = async function (sessionID) {
|
||||
exports.getSessionInfo = async (sessionID) => {
|
||||
// check if the database entry of this session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
|
||||
if (session == null) {
|
||||
// session does not exist
|
||||
throw new customError('sessionID does not exist', 'apierror');
|
||||
throw new CustomError('sessionID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// everything is fine, return the sessioninfos
|
||||
|
@ -186,11 +189,11 @@ exports.getSessionInfo = async function (sessionID) {
|
|||
/**
|
||||
* Deletes a session
|
||||
*/
|
||||
exports.deleteSession = async function (sessionID) {
|
||||
exports.deleteSession = async (sessionID) => {
|
||||
// ensure that the session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
if (session == null) {
|
||||
throw new customError('sessionID does not exist', 'apierror');
|
||||
throw new CustomError('sessionID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
// everything is fine, use the sessioninfos
|
||||
|
@ -217,22 +220,22 @@ exports.deleteSession = async function (sessionID) {
|
|||
}
|
||||
};
|
||||
|
||||
exports.listSessionsOfGroup = async function (groupID) {
|
||||
exports.listSessionsOfGroup = async (groupID) => {
|
||||
// check that the group exists
|
||||
const exists = await groupManager.doesGroupExist(groupID);
|
||||
if (!exists) {
|
||||
throw new customError('groupID does not exist', 'apierror');
|
||||
throw new CustomError('groupID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
const sessions = await listSessionsWithDBKey(`group2sessions:${groupID}`);
|
||||
return sessions;
|
||||
};
|
||||
|
||||
exports.listSessionsOfAuthor = async function (authorID) {
|
||||
exports.listSessionsOfAuthor = async (authorID) => {
|
||||
// check that the author exists
|
||||
const exists = await authorManager.doesAuthorExist(authorID);
|
||||
if (!exists) {
|
||||
throw new customError('authorID does not exist', 'apierror');
|
||||
throw new CustomError('authorID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
const sessions = await listSessionsWithDBKey(`author2sessions:${authorID}`);
|
||||
|
@ -241,7 +244,7 @@ exports.listSessionsOfAuthor = async function (authorID) {
|
|||
|
||||
// this function is basically the code listSessionsOfAuthor and listSessionsOfGroup has in common
|
||||
// required to return null rather than an empty object if there are none
|
||||
async function listSessionsWithDBKey(dbkey) {
|
||||
const listSessionsWithDBKey = async (dbkey) => {
|
||||
// get the group2sessions entry
|
||||
const sessionObject = await db.get(dbkey);
|
||||
const sessions = sessionObject ? sessionObject.sessionIDs : null;
|
||||
|
@ -252,7 +255,7 @@ async function listSessionsWithDBKey(dbkey) {
|
|||
const sessionInfo = await exports.getSessionInfo(sessionID);
|
||||
sessions[sessionID] = sessionInfo;
|
||||
} catch (err) {
|
||||
if (err == 'apierror: sessionID does not exist') {
|
||||
if (err === 'apierror: sessionID does not exist') {
|
||||
console.warn(`Found bad session ${sessionID} in ${dbkey}`);
|
||||
sessions[sessionID] = null;
|
||||
} else {
|
||||
|
@ -262,9 +265,7 @@ async function listSessionsWithDBKey(dbkey) {
|
|||
}
|
||||
|
||||
return sessions;
|
||||
}
|
||||
};
|
||||
|
||||
// checks if a number is an int
|
||||
function is_int(value) {
|
||||
return (parseFloat(value) == parseInt(value)) && !isNaN(value);
|
||||
}
|
||||
const isInt = (value) => (parseFloat(value) === parseInt(value)) && !isNaN(value);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/*
|
||||
* Stores session data in the database
|
||||
* Source; https://github.com/edy-b/SciFlowWriter/blob/develop/available_plugins/ep_sciflowwriter/db/DirtyStore.js
|
||||
|
@ -7,9 +8,9 @@
|
|||
* express-session, which can't actually use promises anyway.
|
||||
*/
|
||||
|
||||
const DB = require('ep_etherpad-lite/node/db/DB');
|
||||
const Store = require('ep_etherpad-lite/node_modules/express-session').Store;
|
||||
const log4js = require('ep_etherpad-lite/node_modules/log4js');
|
||||
const DB = require('./DB');
|
||||
const Store = require('express-session').Store;
|
||||
const log4js = require('log4js');
|
||||
|
||||
const logger = log4js.getLogger('SessionStore');
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
'use strict';
|
||||
/**
|
||||
* I found this tests in the old Etherpad and used it to test if the Changeset library can be run on node.js.
|
||||
* It has no use for ep-lite, but I thought I keep it cause it may help someone to understand the Changeset library
|
||||
* I found this tests in the old Etherpad and used it to test if the Changeset library can be run on
|
||||
* node.js. It has no use for ep-lite, but I thought I keep it cause it may help someone to
|
||||
* understand the Changeset library
|
||||
* https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2_tests.js
|
||||
*/
|
||||
|
||||
|
@ -21,52 +23,47 @@
|
|||
*/
|
||||
|
||||
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const AttributePool = require('ep_etherpad-lite/static/js/AttributePool');
|
||||
const Changeset = require('../static/js/Changeset');
|
||||
const AttributePool = require('../static/js/AttributePool');
|
||||
|
||||
function random() {
|
||||
this.nextInt = function (maxValue) {
|
||||
return Math.floor(Math.random() * maxValue);
|
||||
};
|
||||
|
||||
this.nextDouble = function (maxValue) {
|
||||
return Math.random();
|
||||
};
|
||||
this.nextInt = (maxValue) => Math.floor(Math.random() * maxValue);
|
||||
this.nextDouble = (maxValue) => Math.random();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
function print(str) {
|
||||
const runTests = () => {
|
||||
const print = (str) => {
|
||||
console.log(str);
|
||||
}
|
||||
};
|
||||
|
||||
function assert(code, optMsg) {
|
||||
if (!eval(code)) throw new Error(`FALSE: ${optMsg || code}`);
|
||||
}
|
||||
const assert = (code, optMsg) => {
|
||||
if (!eval(code)) throw new Error(`FALSE: ${optMsg || code}`); /* eslint-disable-line no-eval */
|
||||
};
|
||||
|
||||
function literal(v) {
|
||||
const literal = (v) => {
|
||||
if ((typeof v) === 'string') {
|
||||
return `"${v.replace(/[\\\"]/g, '\\$1').replace(/\n/g, '\\n')}"`;
|
||||
} else { return JSON.stringify(v); }
|
||||
}
|
||||
};
|
||||
|
||||
function assertEqualArrays(a, b) {
|
||||
const assertEqualArrays = (a, b) => {
|
||||
assert(`JSON.stringify(${literal(a)}) == JSON.stringify(${literal(b)})`);
|
||||
}
|
||||
};
|
||||
|
||||
function assertEqualStrings(a, b) {
|
||||
const assertEqualStrings = (a, b) => {
|
||||
assert(`${literal(a)} == ${literal(b)}`);
|
||||
}
|
||||
};
|
||||
|
||||
function throughIterator(opsStr) {
|
||||
const throughIterator = (opsStr) => {
|
||||
const iter = Changeset.opIterator(opsStr);
|
||||
const assem = Changeset.opAssembler();
|
||||
while (iter.hasNext()) {
|
||||
assem.append(iter.next());
|
||||
}
|
||||
return assem.toString();
|
||||
}
|
||||
};
|
||||
|
||||
function throughSmartAssembler(opsStr) {
|
||||
const throughSmartAssembler = (opsStr) => {
|
||||
const iter = Changeset.opIterator(opsStr);
|
||||
const assem = Changeset.smartOpAssembler();
|
||||
while (iter.hasNext()) {
|
||||
|
@ -74,50 +71,50 @@ function runTests() {
|
|||
}
|
||||
assem.endDocument();
|
||||
return assem.toString();
|
||||
}
|
||||
};
|
||||
|
||||
(function () {
|
||||
(() => {
|
||||
print('> throughIterator');
|
||||
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
|
||||
assert(`throughIterator(${literal(x)}) == ${literal(x)}`);
|
||||
})();
|
||||
|
||||
(function () {
|
||||
(() => {
|
||||
print('> throughSmartAssembler');
|
||||
const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1';
|
||||
assert(`throughSmartAssembler(${literal(x)}) == ${literal(x)}`);
|
||||
})();
|
||||
|
||||
function applyMutations(mu, arrayOfArrays) {
|
||||
const applyMutations = (mu, arrayOfArrays) => {
|
||||
arrayOfArrays.forEach((a) => {
|
||||
const result = mu[a[0]].apply(mu, a.slice(1));
|
||||
if (a[0] == 'remove' && a[3]) {
|
||||
if (a[0] === 'remove' && a[3]) {
|
||||
assertEqualStrings(a[3], result);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function mutationsToChangeset(oldLen, arrayOfArrays) {
|
||||
const mutationsToChangeset = (oldLen, arrayOfArrays) => {
|
||||
const assem = Changeset.smartOpAssembler();
|
||||
const op = Changeset.newOp();
|
||||
const bank = Changeset.stringAssembler();
|
||||
let oldPos = 0;
|
||||
let newLen = 0;
|
||||
arrayOfArrays.forEach((a) => {
|
||||
if (a[0] == 'skip') {
|
||||
if (a[0] === 'skip') {
|
||||
op.opcode = '=';
|
||||
op.chars = a[1];
|
||||
op.lines = (a[2] || 0);
|
||||
assem.append(op);
|
||||
oldPos += op.chars;
|
||||
newLen += op.chars;
|
||||
} else if (a[0] == 'remove') {
|
||||
} else if (a[0] === 'remove') {
|
||||
op.opcode = '-';
|
||||
op.chars = a[1];
|
||||
op.lines = (a[2] || 0);
|
||||
assem.append(op);
|
||||
oldPos += op.chars;
|
||||
} else if (a[0] == 'insert') {
|
||||
} else if (a[0] === 'insert') {
|
||||
op.opcode = '+';
|
||||
bank.append(a[1]);
|
||||
op.chars = a[1].length;
|
||||
|
@ -129,9 +126,9 @@ function runTests() {
|
|||
newLen += oldLen - oldPos;
|
||||
assem.endDocument();
|
||||
return Changeset.pack(oldLen, newLen, assem.toString(), bank.toString());
|
||||
}
|
||||
};
|
||||
|
||||
function runMutationTest(testId, origLines, muts, correct) {
|
||||
const runMutationTest = (testId, origLines, muts, correct) => {
|
||||
print(`> runMutationTest#${testId}`);
|
||||
let lines = origLines.slice();
|
||||
const mu = Changeset.textLinesMutator(lines);
|
||||
|
@ -149,7 +146,7 @@ function runTests() {
|
|||
// print(literal(cs));
|
||||
const outText = Changeset.applyToText(cs, inText);
|
||||
assertEqualStrings(correctText, outText);
|
||||
}
|
||||
};
|
||||
|
||||
runMutationTest(1, ['apple\n', 'banana\n', 'cabbage\n', 'duffle\n', 'eggplant\n'], [
|
||||
['remove', 1, 0, 'a'],
|
||||
|
@ -220,7 +217,7 @@ function runTests() {
|
|||
['skip', 1, 1, true],
|
||||
], ['banana\n', 'cabbage\n', 'duffle\n']);
|
||||
|
||||
function poolOrArray(attribs) {
|
||||
const poolOrArray = (attribs) => {
|
||||
if (attribs.getAttrib) {
|
||||
return attribs; // it's already an attrib pool
|
||||
} else {
|
||||
|
@ -231,23 +228,25 @@ function runTests() {
|
|||
});
|
||||
return p;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function runApplyToAttributionTest(testId, attribs, cs, inAttr, outCorrect) {
|
||||
const runApplyToAttributionTest = (testId, attribs, cs, inAttr, outCorrect) => {
|
||||
print(`> applyToAttribution#${testId}`);
|
||||
const p = poolOrArray(attribs);
|
||||
const result = Changeset.applyToAttribution(
|
||||
Changeset.checkRep(cs), inAttr, p);
|
||||
assertEqualStrings(outCorrect, result);
|
||||
}
|
||||
};
|
||||
|
||||
// turn c<b>a</b>ctus\n into a<b>c</b>tusabcd\n
|
||||
runApplyToAttributionTest(1, ['bold,', 'bold,true'], 'Z:7>3-1*0=1*1=1=3+4$abcd', '+1*1+1|1+5', '+1*1+1|1+8');
|
||||
runApplyToAttributionTest(1,
|
||||
['bold,', 'bold,true'], 'Z:7>3-1*0=1*1=1=3+4$abcd', '+1*1+1|1+5', '+1*1+1|1+8');
|
||||
|
||||
// turn "david\ngreenspan\n" into "<b>david\ngreen</b>\n"
|
||||
runApplyToAttributionTest(2, ['bold,', 'bold,true'], 'Z:g<4*1|1=6*1=5-4$', '|2+g', '*1|1+6*1+5|1+1');
|
||||
runApplyToAttributionTest(2,
|
||||
['bold,', 'bold,true'], 'Z:g<4*1|1=6*1=5-4$', '|2+g', '*1|1+6*1+5|1+1');
|
||||
|
||||
(function () {
|
||||
(() => {
|
||||
print('> mutatorHasMore');
|
||||
const lines = ['1\n', '2\n', '3\n', '4\n'];
|
||||
let mu;
|
||||
|
@ -288,7 +287,7 @@ function runTests() {
|
|||
assert(`${mu.hasMore()} == false`);
|
||||
})();
|
||||
|
||||
function runMutateAttributionTest(testId, attribs, cs, alines, outCorrect) {
|
||||
const runMutateAttributionTest = (testId, attribs, cs, alines, outCorrect) => {
|
||||
print(`> runMutateAttributionTest#${testId}`);
|
||||
const p = poolOrArray(attribs);
|
||||
const alines2 = Array.prototype.slice.call(alines);
|
||||
|
@ -298,30 +297,35 @@ function runTests() {
|
|||
|
||||
print(`> runMutateAttributionTest#${testId}.applyToAttribution`);
|
||||
|
||||
function removeQuestionMarks(a) {
|
||||
return a.replace(/\?/g, '');
|
||||
}
|
||||
const removeQuestionMarks = (a) => a.replace(/\?/g, '');
|
||||
const inMerged = Changeset.joinAttributionLines(alines.map(removeQuestionMarks));
|
||||
const correctMerged = Changeset.joinAttributionLines(outCorrect.map(removeQuestionMarks));
|
||||
const mergedResult = Changeset.applyToAttribution(cs, inMerged, p);
|
||||
assertEqualStrings(correctMerged, mergedResult);
|
||||
}
|
||||
};
|
||||
|
||||
// turn 123\n 456\n 789\n into 123\n 4<b>5</b>6\n 789\n
|
||||
runMutateAttributionTest(1, ['bold,true'], 'Z:c>0|1=4=1*0=1$', ['|1+4', '|1+4', '|1+4'], ['|1+4', '+1*0+1|1+2', '|1+4']);
|
||||
runMutateAttributionTest(1,
|
||||
['bold,true'], 'Z:c>0|1=4=1*0=1$', ['|1+4', '|1+4', '|1+4'], ['|1+4', '+1*0+1|1+2', '|1+4']);
|
||||
|
||||
// make a document bold
|
||||
runMutateAttributionTest(2, ['bold,true'], 'Z:c>0*0|3=c$', ['|1+4', '|1+4', '|1+4'], ['*0|1+4', '*0|1+4', '*0|1+4']);
|
||||
runMutateAttributionTest(2,
|
||||
['bold,true'], 'Z:c>0*0|3=c$', ['|1+4', '|1+4', '|1+4'], ['*0|1+4', '*0|1+4', '*0|1+4']);
|
||||
|
||||
// clear bold on document
|
||||
runMutateAttributionTest(3, ['bold,', 'bold,true'], 'Z:c>0*0|3=c$', ['*1+1+1*1+1|1+1', '+1*1+1|1+2', '*1+1+1*1+1|1+1'], ['|1+4', '|1+4', '|1+4']);
|
||||
runMutateAttributionTest(3,
|
||||
['bold,', 'bold,true'], 'Z:c>0*0|3=c$',
|
||||
['*1+1+1*1+1|1+1', '+1*1+1|1+2', '*1+1+1*1+1|1+1'], ['|1+4', '|1+4', '|1+4']);
|
||||
|
||||
// add a character on line 3 of a document with 5 blank lines, and make sure
|
||||
// the optimization that skips purely-kept lines is working; if any attribution string
|
||||
// with a '?' is parsed it will cause an error.
|
||||
runMutateAttributionTest(4, ['foo,bar', 'line,1', 'line,2', 'line,3', 'line,4', 'line,5'], 'Z:5>1|2=2+1$x', ['?*1|1+1', '?*2|1+1', '*3|1+1', '?*4|1+1', '?*5|1+1'], ['?*1|1+1', '?*2|1+1', '+1*3|1+1', '?*4|1+1', '?*5|1+1']);
|
||||
runMutateAttributionTest(4,
|
||||
['foo,bar', 'line,1', 'line,2', 'line,3', 'line,4', 'line,5'],
|
||||
'Z:5>1|2=2+1$x', ['?*1|1+1', '?*2|1+1', '*3|1+1', '?*4|1+1', '?*5|1+1'],
|
||||
['?*1|1+1', '?*2|1+1', '+1*3|1+1', '?*4|1+1', '?*5|1+1']);
|
||||
|
||||
const testPoolWithChars = (function () {
|
||||
const testPoolWithChars = (() => {
|
||||
const p = new AttributePool();
|
||||
p.putAttrib(['char', 'newline']);
|
||||
for (let i = 1; i < 36; i++) {
|
||||
|
@ -332,39 +336,66 @@ function runTests() {
|
|||
})();
|
||||
|
||||
// based on runMutationTest#1
|
||||
runMutateAttributionTest(5, testPoolWithChars, 'Z:11>7-2*t+1*u+1|2=b|2+a=2*b+1*o+1*t+1*0|1+1*b+1*u+1=3|1-3-6$' + 'tucream\npie\nbot\nbu', ['*a+1*p+2*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '*c+1*a+1*b+2*a+1*g+1*e+1*0|1+1', '*d+1*u+1*f+2*l+1*e+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1'], ['*t+1*u+1*p+1*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '|1+6', '|1+4', '*c+1*a+1*b+1*o+1*t+1*0|1+1', '*b+1*u+1*b+2*a+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1']);
|
||||
runMutateAttributionTest(5, testPoolWithChars,
|
||||
'Z:11>7-2*t+1*u+1|2=b|2+a=2*b+1*o+1*t+1*0|1+1*b+1*u+1=3|1-3-6$' + 'tucream\npie\nbot\nbu', ['*a+1*p+2*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '*c+1*a+1*b+2*a+1*g+1*e+1*0|1+1', '*d+1*u+1*f+2*l+1*e+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1'], ['*t+1*u+1*p+1*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '|1+6', '|1+4', '*c+1*a+1*b+1*o+1*t+1*0|1+1', '*b+1*u+1*b+2*a+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1']);
|
||||
|
||||
// based on runMutationTest#3
|
||||
runMutateAttributionTest(6, testPoolWithChars, 'Z:11<f|1-6|2=f=6|1-1-8$', ['*a|1+6', '*b|1+7', '*c|1+8', '*d|1+7', '*e|1+9'], ['*b|1+7', '*c|1+8', '*d+6*e|1+1']);
|
||||
runMutateAttributionTest(6, testPoolWithChars,
|
||||
'Z:11<f|1-6|2=f=6|1-1-8$', ['*a|1+6', '*b|1+7', '*c|1+8', '*d|1+7', '*e|1+9'],
|
||||
['*b|1+7', '*c|1+8', '*d+6*e|1+1']);
|
||||
|
||||
// based on runMutationTest#4
|
||||
runMutateAttributionTest(7, testPoolWithChars, 'Z:3>7=1|4+7$\n2\n3\n4\n', ['*1+1*5|1+2'], ['*1+1|1+1', '|1+2', '|1+2', '|1+2', '*5|1+2']);
|
||||
runMutateAttributionTest(7, testPoolWithChars, 'Z:3>7=1|4+7$\n2\n3\n4\n',
|
||||
['*1+1*5|1+2'], ['*1+1|1+1', '|1+2', '|1+2', '|1+2', '*5|1+2']);
|
||||
|
||||
// based on runMutationTest#5
|
||||
runMutateAttributionTest(8, testPoolWithChars, 'Z:a<7=1|4-7$', ['*1|1+2', '*2|1+2', '*3|1+2', '*4|1+2', '*5|1+2'], ['*1+1*5|1+2']);
|
||||
runMutateAttributionTest(8, testPoolWithChars, 'Z:a<7=1|4-7$',
|
||||
['*1|1+2', '*2|1+2', '*3|1+2', '*4|1+2', '*5|1+2'], ['*1+1*5|1+2']);
|
||||
|
||||
// based on runMutationTest#6
|
||||
runMutateAttributionTest(9, testPoolWithChars, 'Z:k<7*0+1*10|2=8|2-8$0', ['*1+1*2+1*3+1|1+1', '*a+1*b+1*c+1|1+1', '*d+1*e+1*f+1|1+1', '*g+1*h+1*i+1|1+1', '?*x+1*y+1*z+1|1+1'], ['*0+1|1+4', '|1+4', '?*x+1*y+1*z+1|1+1']);
|
||||
runMutateAttributionTest(9, testPoolWithChars, 'Z:k<7*0+1*10|2=8|2-8$0',
|
||||
[
|
||||
'*1+1*2+1*3+1|1+1',
|
||||
'*a+1*b+1*c+1|1+1',
|
||||
'*d+1*e+1*f+1|1+1',
|
||||
'*g+1*h+1*i+1|1+1',
|
||||
'?*x+1*y+1*z+1|1+1',
|
||||
],
|
||||
['*0+1|1+4', '|1+4', '?*x+1*y+1*z+1|1+1']);
|
||||
|
||||
runMutateAttributionTest(10, testPoolWithChars, 'Z:6>4=1+1=1+1|1=1+1=1*0+1$abcd', ['|1+3', '|1+3'], ['|1+5', '+2*0+1|1+2']);
|
||||
runMutateAttributionTest(10, testPoolWithChars, 'Z:6>4=1+1=1+1|1=1+1=1*0+1$abcd',
|
||||
['|1+3', '|1+3'], ['|1+5', '+2*0+1|1+2']);
|
||||
|
||||
|
||||
runMutateAttributionTest(11, testPoolWithChars, 'Z:s>1|1=4=6|1+1$\n', ['*0|1+4', '*0|1+8', '*0+5|1+1', '*0|1+1', '*0|1+5', '*0|1+1', '*0|1+1', '*0|1+1', '|1+1'], ['*0|1+4', '*0+6|1+1', '*0|1+2', '*0+5|1+1', '*0|1+1', '*0|1+5', '*0|1+1', '*0|1+1', '*0|1+1', '|1+1']);
|
||||
runMutateAttributionTest(11, testPoolWithChars, 'Z:s>1|1=4=6|1+1$\n',
|
||||
['*0|1+4', '*0|1+8', '*0+5|1+1', '*0|1+1', '*0|1+5', '*0|1+1', '*0|1+1', '*0|1+1', '|1+1'],
|
||||
[
|
||||
'*0|1+4',
|
||||
'*0+6|1+1',
|
||||
'*0|1+2',
|
||||
'*0+5|1+1',
|
||||
'*0|1+1',
|
||||
'*0|1+5',
|
||||
'*0|1+1',
|
||||
'*0|1+1',
|
||||
'*0|1+1',
|
||||
'|1+1',
|
||||
]);
|
||||
|
||||
function randomInlineString(len, rand) {
|
||||
const randomInlineString = (len, rand) => {
|
||||
const assem = Changeset.stringAssembler();
|
||||
for (let i = 0; i < len; i++) {
|
||||
assem.append(String.fromCharCode(rand.nextInt(26) + 97));
|
||||
}
|
||||
return assem.toString();
|
||||
}
|
||||
};
|
||||
|
||||
function randomMultiline(approxMaxLines, approxMaxCols, rand) {
|
||||
const randomMultiline = (approxMaxLines, approxMaxCols, rand) => {
|
||||
const numParts = rand.nextInt(approxMaxLines * 2) + 1;
|
||||
const txt = Changeset.stringAssembler();
|
||||
txt.append(rand.nextInt(2) ? '\n' : '');
|
||||
for (let i = 0; i < numParts; i++) {
|
||||
if ((i % 2) == 0) {
|
||||
if ((i % 2) === 0) {
|
||||
if (rand.nextInt(10)) {
|
||||
txt.append(randomInlineString(rand.nextInt(approxMaxCols) + 1, rand));
|
||||
} else {
|
||||
|
@ -375,9 +406,9 @@ function runTests() {
|
|||
}
|
||||
}
|
||||
return txt.toString();
|
||||
}
|
||||
};
|
||||
|
||||
function randomStringOperation(numCharsLeft, rand) {
|
||||
const randomStringOperation = (numCharsLeft, rand) => {
|
||||
let result;
|
||||
switch (rand.nextInt(9)) {
|
||||
case 0:
|
||||
|
@ -476,26 +507,26 @@ function runTests() {
|
|||
result.skip = Math.min(result.skip, maxOrig);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
function randomTwoPropAttribs(opcode, rand) {
|
||||
const randomTwoPropAttribs = (opcode, rand) => {
|
||||
// assumes attrib pool like ['apple,','apple,true','banana,','banana,true']
|
||||
if (opcode == '-' || rand.nextInt(3)) {
|
||||
if (opcode === '-' || rand.nextInt(3)) {
|
||||
return '';
|
||||
} else if (rand.nextInt(3)) {
|
||||
if (opcode == '+' || rand.nextInt(2)) {
|
||||
if (opcode === '+' || rand.nextInt(2)) {
|
||||
return `*${Changeset.numToString(rand.nextInt(2) * 2 + 1)}`;
|
||||
} else {
|
||||
return `*${Changeset.numToString(rand.nextInt(2) * 2)}`;
|
||||
}
|
||||
} else if (opcode == '+' || rand.nextInt(4) == 0) {
|
||||
} else if (opcode === '+' || rand.nextInt(4) === 0) {
|
||||
return '*1*3';
|
||||
} else {
|
||||
return ['*0*2', '*0*3', '*1*2'][rand.nextInt(3)];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function randomTestChangeset(origText, rand, withAttribs) {
|
||||
const randomTestChangeset = (origText, rand, withAttribs) => {
|
||||
const charBank = Changeset.stringAssembler();
|
||||
let textLeft = origText; // always keep final newline
|
||||
const outTextAssem = Changeset.stringAssembler();
|
||||
|
@ -504,13 +535,13 @@ function runTests() {
|
|||
|
||||
const nextOp = Changeset.newOp();
|
||||
|
||||
function appendMultilineOp(opcode, txt) {
|
||||
const appendMultilineOp = (opcode, txt) => {
|
||||
nextOp.opcode = opcode;
|
||||
if (withAttribs) {
|
||||
nextOp.attribs = randomTwoPropAttribs(opcode, rand);
|
||||
}
|
||||
txt.replace(/\n|[^\n]+/g, (t) => {
|
||||
if (t == '\n') {
|
||||
if (t === '\n') {
|
||||
nextOp.chars = 1;
|
||||
nextOp.lines = 1;
|
||||
opAssem.append(nextOp);
|
||||
|
@ -521,26 +552,26 @@ function runTests() {
|
|||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function doOp() {
|
||||
const doOp = () => {
|
||||
const o = randomStringOperation(textLeft.length, rand);
|
||||
if (o.insert) {
|
||||
var txt = o.insert;
|
||||
const txt = o.insert;
|
||||
charBank.append(txt);
|
||||
outTextAssem.append(txt);
|
||||
appendMultilineOp('+', txt);
|
||||
} else if (o.skip) {
|
||||
var txt = textLeft.substring(0, o.skip);
|
||||
const txt = textLeft.substring(0, o.skip);
|
||||
textLeft = textLeft.substring(o.skip);
|
||||
outTextAssem.append(txt);
|
||||
appendMultilineOp('=', txt);
|
||||
} else if (o.remove) {
|
||||
var txt = textLeft.substring(0, o.remove);
|
||||
const txt = textLeft.substring(0, o.remove);
|
||||
textLeft = textLeft.substring(o.remove);
|
||||
appendMultilineOp('-', txt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
while (textLeft.length > 1) doOp();
|
||||
for (let i = 0; i < 5; i++) doOp(); // do some more (only insertions will happen)
|
||||
|
@ -549,9 +580,9 @@ function runTests() {
|
|||
const cs = Changeset.pack(oldLen, outText.length, opAssem.toString(), charBank.toString());
|
||||
Changeset.checkRep(cs);
|
||||
return [cs, outText];
|
||||
}
|
||||
};
|
||||
|
||||
function testCompose(randomSeed) {
|
||||
const testCompose = (randomSeed) => {
|
||||
const rand = new random();
|
||||
print(`> testCompose#${randomSeed}`);
|
||||
|
||||
|
@ -583,9 +614,9 @@ function runTests() {
|
|||
assertEqualStrings(text2, Changeset.applyToText(change12, startText));
|
||||
assertEqualStrings(text3, Changeset.applyToText(change23, text1));
|
||||
assertEqualStrings(text3, Changeset.applyToText(change123, startText));
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < 30; i++) testCompose(i);
|
||||
for (let i = 0; i < 30; i++) testCompose(i);
|
||||
|
||||
(function simpleComposeAttributesTest() {
|
||||
print('> simpleComposeAttributesTest');
|
||||
|
@ -607,12 +638,12 @@ function runTests() {
|
|||
p.putAttrib(['y', 'abc']);
|
||||
p.putAttrib(['y', 'def']);
|
||||
|
||||
function testFollow(a, b, afb, bfa, merge) {
|
||||
const testFollow = (a, b, afb, bfa, merge) => {
|
||||
assertEqualStrings(afb, Changeset.followAttributes(a, b, p));
|
||||
assertEqualStrings(bfa, Changeset.followAttributes(b, a, p));
|
||||
assertEqualStrings(merge, Changeset.composeAttributes(a, afb, true, p));
|
||||
assertEqualStrings(merge, Changeset.composeAttributes(b, bfa, true, p));
|
||||
}
|
||||
};
|
||||
|
||||
testFollow('', '', '', '', '');
|
||||
testFollow('*0', '', '', '*0', '*0');
|
||||
|
@ -624,7 +655,7 @@ function runTests() {
|
|||
testFollow('*0*4', '*2', '', '*0*4', '*0*4');
|
||||
})();
|
||||
|
||||
function testFollow(randomSeed) {
|
||||
const testFollow = (randomSeed) => {
|
||||
const rand = new random();
|
||||
print(`> testFollow#${randomSeed}`);
|
||||
|
||||
|
@ -642,37 +673,37 @@ function runTests() {
|
|||
const merge2 = Changeset.checkRep(Changeset.compose(cs2, bfa));
|
||||
|
||||
assertEqualStrings(merge1, merge2);
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < 30; i++) testFollow(i);
|
||||
for (let i = 0; i < 30; i++) testFollow(i);
|
||||
|
||||
function testSplitJoinAttributionLines(randomSeed) {
|
||||
const testSplitJoinAttributionLines = (randomSeed) => {
|
||||
const rand = new random();
|
||||
print(`> testSplitJoinAttributionLines#${randomSeed}`);
|
||||
|
||||
const doc = `${randomMultiline(10, 20, rand)}\n`;
|
||||
|
||||
function stringToOps(str) {
|
||||
const stringToOps = (str) => {
|
||||
const assem = Changeset.mergingOpAssembler();
|
||||
const o = Changeset.newOp('+');
|
||||
o.chars = 1;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const c = str.charAt(i);
|
||||
o.lines = (c == '\n' ? 1 : 0);
|
||||
o.attribs = (c == 'a' || c == 'b' ? `*${c}` : '');
|
||||
o.lines = (c === '\n' ? 1 : 0);
|
||||
o.attribs = (c === 'a' || c === 'b' ? `*${c}` : '');
|
||||
assem.append(o);
|
||||
}
|
||||
return assem.toString();
|
||||
}
|
||||
};
|
||||
|
||||
const theJoined = stringToOps(doc);
|
||||
const theSplit = doc.match(/[^\n]*\n/g).map(stringToOps);
|
||||
|
||||
assertEqualArrays(theSplit, Changeset.splitAttributionLines(theJoined, doc));
|
||||
assertEqualStrings(theJoined, Changeset.joinAttributionLines(theSplit));
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < 10; i++) testSplitJoinAttributionLines(i);
|
||||
for (let i = 0; i < 10; i++) testSplitJoinAttributionLines(i);
|
||||
|
||||
(function testMoveOpsToNewPool() {
|
||||
print('> testMoveOpsToNewPool');
|
||||
|
@ -685,8 +716,10 @@ function runTests() {
|
|||
|
||||
pool2.putAttrib(['foo', 'bar']);
|
||||
|
||||
assertEqualStrings(Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2), 'Z:1>2*0+1*1+1$ab');
|
||||
assertEqualStrings(Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2), '*0+1*1+1');
|
||||
assertEqualStrings(
|
||||
Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2), 'Z:1>2*0+1*1+1$ab');
|
||||
assertEqualStrings(
|
||||
Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2), '*0+1*1+1');
|
||||
})();
|
||||
|
||||
|
||||
|
@ -709,14 +742,15 @@ function runTests() {
|
|||
assertEqualArrays(correctSplices, Changeset.toSplices(cs));
|
||||
})();
|
||||
|
||||
function testCharacterRangeFollow(testId, cs, oldRange, insertionsAfter, correctNewRange) {
|
||||
const testCharacterRangeFollow = (testId, cs, oldRange, insertionsAfter, correctNewRange) => {
|
||||
print(`> testCharacterRangeFollow#${testId}`);
|
||||
cs = Changeset.checkRep(cs);
|
||||
assertEqualArrays(correctNewRange, Changeset.characterRangeFollow(
|
||||
cs, oldRange[0], oldRange[1], insertionsAfter));
|
||||
};
|
||||
|
||||
var cs = Changeset.checkRep(cs);
|
||||
assertEqualArrays(correctNewRange, Changeset.characterRangeFollow(cs, oldRange[0], oldRange[1], insertionsAfter));
|
||||
}
|
||||
|
||||
testCharacterRangeFollow(1, 'Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk', [7, 10], false, [14, 15]);
|
||||
testCharacterRangeFollow(1, 'Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk',
|
||||
[7, 10], false, [14, 15]);
|
||||
testCharacterRangeFollow(2, 'Z:bc<6|x=b4|2-6$', [400, 407], false, [400, 401]);
|
||||
testCharacterRangeFollow(3, 'Z:4>0-3+3$abc', [0, 3], false, [3, 3]);
|
||||
testCharacterRangeFollow(4, 'Z:4>0-3+3$abc', [0, 3], true, [0, 0]);
|
||||
|
@ -735,23 +769,31 @@ function runTests() {
|
|||
p.putAttrib(['name', 'david']);
|
||||
p.putAttrib(['color', 'green']);
|
||||
|
||||
assertEqualStrings('david', Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'name', p));
|
||||
assertEqualStrings('david', Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'name', p));
|
||||
assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'name', p));
|
||||
assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('+1'), 'name', p));
|
||||
assertEqualStrings('green', Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'color', p));
|
||||
assertEqualStrings('green', Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'color', p));
|
||||
assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'color', p));
|
||||
assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('+1'), 'color', p));
|
||||
assertEqualStrings('david',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'name', p));
|
||||
assertEqualStrings('david',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'name', p));
|
||||
assertEqualStrings('',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'name', p));
|
||||
assertEqualStrings('',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('+1'), 'name', p));
|
||||
assertEqualStrings('green',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'color', p));
|
||||
assertEqualStrings('green',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'color', p));
|
||||
assertEqualStrings('',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'color', p));
|
||||
assertEqualStrings('',
|
||||
Changeset.opAttributeValue(Changeset.stringOp('+1'), 'color', p));
|
||||
})();
|
||||
|
||||
function testAppendATextToAssembler(testId, atext, correctOps) {
|
||||
const testAppendATextToAssembler = (testId, atext, correctOps) => {
|
||||
print(`> testAppendATextToAssembler#${testId}`);
|
||||
|
||||
const assem = Changeset.smartOpAssembler();
|
||||
Changeset.appendATextToAssembler(atext, assem);
|
||||
assertEqualStrings(correctOps, assem.toString());
|
||||
}
|
||||
};
|
||||
|
||||
testAppendATextToAssembler(1, {
|
||||
text: '\n',
|
||||
|
@ -786,13 +828,13 @@ function runTests() {
|
|||
attribs: '|2+2*x|2+5',
|
||||
}, '|2+2*x|1+1*x+3');
|
||||
|
||||
function testMakeAttribsString(testId, pool, opcode, attribs, correctString) {
|
||||
const testMakeAttribsString = (testId, pool, opcode, attribs, correctString) => {
|
||||
print(`> testMakeAttribsString#${testId}`);
|
||||
|
||||
const p = poolOrArray(pool);
|
||||
const str = Changeset.makeAttribsString(opcode, attribs, p);
|
||||
assertEqualStrings(correctString, str);
|
||||
}
|
||||
};
|
||||
|
||||
testMakeAttribsString(1, ['bold,'], '+', [
|
||||
['bold', ''],
|
||||
|
@ -809,12 +851,12 @@ function runTests() {
|
|||
['abc', 'def'],
|
||||
], '*0*1');
|
||||
|
||||
function testSubattribution(testId, astr, start, end, correctOutput) {
|
||||
const testSubattribution = (testId, astr, start, end, correctOutput) => {
|
||||
print(`> testSubattribution#${testId}`);
|
||||
|
||||
const str = Changeset.subattribution(astr, start, end);
|
||||
assertEqualStrings(correctOutput, str);
|
||||
}
|
||||
};
|
||||
|
||||
testSubattribution(1, '+1', 0, 0, '');
|
||||
testSubattribution(2, '+1', 0, 1, '+1');
|
||||
|
@ -859,39 +901,42 @@ function runTests() {
|
|||
testSubattribution(41, '*0+2+1*1|1+3', 2, 6, '+1*1|1+3');
|
||||
testSubattribution(42, '*0+2+1*1+3', 2, 6, '+1*1+3');
|
||||
|
||||
function testFilterAttribNumbers(testId, cs, filter, correctOutput) {
|
||||
const testFilterAttribNumbers = (testId, cs, filter, correctOutput) => {
|
||||
print(`> testFilterAttribNumbers#${testId}`);
|
||||
|
||||
const str = Changeset.filterAttribNumbers(cs, filter);
|
||||
assertEqualStrings(correctOutput, str);
|
||||
}
|
||||
};
|
||||
|
||||
testFilterAttribNumbers(1, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', (n) => (n % 2) == 0, '*0+1+2+3+4*2+5*0*2*c+6');
|
||||
testFilterAttribNumbers(2, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', (n) => (n % 2) == 1, '*1+1+2+3*1+4+5*1*b+6');
|
||||
testFilterAttribNumbers(1, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6',
|
||||
(n) => (n % 2) === 0, '*0+1+2+3+4*2+5*0*2*c+6');
|
||||
testFilterAttribNumbers(2, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6',
|
||||
(n) => (n % 2) === 1, '*1+1+2+3*1+4+5*1*b+6');
|
||||
|
||||
function testInverse(testId, cs, lines, alines, pool, correctOutput) {
|
||||
const testInverse = (testId, cs, lines, alines, pool, correctOutput) => {
|
||||
print(`> testInverse#${testId}`);
|
||||
|
||||
pool = poolOrArray(pool);
|
||||
const str = Changeset.inverse(Changeset.checkRep(cs), lines, alines, pool);
|
||||
assertEqualStrings(correctOutput, str);
|
||||
}
|
||||
};
|
||||
|
||||
// take "FFFFTTTTT" and apply "-FT--FFTT", the inverse of which is "--F--TT--"
|
||||
testInverse(1, 'Z:9>0=1*0=1*1=1=2*0=2*1|1=2$', null, ['+4*1+5'], ['bold,', 'bold,true'], 'Z:9>0=2*0=1=2*1=2$');
|
||||
testInverse(1, 'Z:9>0=1*0=1*1=1=2*0=2*1|1=2$', null,
|
||||
['+4*1+5'], ['bold,', 'bold,true'], 'Z:9>0=2*0=1=2*1=2$');
|
||||
|
||||
function testMutateTextLines(testId, cs, lines, correctLines) {
|
||||
const testMutateTextLines = (testId, cs, lines, correctLines) => {
|
||||
print(`> testMutateTextLines#${testId}`);
|
||||
|
||||
const a = lines.slice();
|
||||
Changeset.mutateTextLines(cs, a);
|
||||
assertEqualArrays(correctLines, a);
|
||||
}
|
||||
};
|
||||
|
||||
testMutateTextLines(1, 'Z:4<1|1-2-1|1+1+1$\nc', ['a\n', 'b\n'], ['\n', 'c\n']);
|
||||
testMutateTextLines(2, 'Z:4>0|1-2-1|2+3$\nc\n', ['a\n', 'b\n'], ['\n', 'c\n', '\n']);
|
||||
|
||||
function testInverseRandom(randomSeed) {
|
||||
const testInverseRandom = (randomSeed) => {
|
||||
const rand = new random();
|
||||
print(`> testInverseRandom#${randomSeed}`);
|
||||
|
||||
|
@ -928,9 +973,9 @@ function runTests() {
|
|||
// print(lines.map(function(s) { return '3: '+s.slice(0,-1); }).join('\n'));
|
||||
assertEqualArrays(origLines, lines);
|
||||
assertEqualArrays(origALines, alines);
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < 30; i++) testInverseRandom(i);
|
||||
}
|
||||
for (let i = 0; i < 30; i++) testInverseRandom(i);
|
||||
};
|
||||
|
||||
runTests();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The API Handler handles all API http requests
|
||||
*/
|
||||
|
@ -37,7 +38,8 @@ try {
|
|||
apikey = fs.readFileSync(apikeyFilename, 'utf8');
|
||||
apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`);
|
||||
} catch (e) {
|
||||
apiHandlerLogger.info(`Api key file "${apikeyFilename}" not found. Creating with random contents.`);
|
||||
apiHandlerLogger.info(
|
||||
`Api key file "${apikeyFilename}" not found. Creating with random contents.`);
|
||||
apikey = randomString(32);
|
||||
fs.writeFileSync(apikeyFilename, apikey, 'utf8');
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Handles the export requests
|
||||
*/
|
||||
|
@ -25,7 +26,7 @@ const exportEtherpad = require('../utils/ExportEtherpad');
|
|||
const fs = require('fs');
|
||||
const settings = require('../utils/Settings');
|
||||
const os = require('os');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
const TidyHtml = require('../utils/TidyHtml');
|
||||
const util = require('util');
|
||||
|
||||
|
@ -49,7 +50,7 @@ const tempDirectory = os.tmpdir();
|
|||
/**
|
||||
* do a requested export
|
||||
*/
|
||||
async function doExport(req, res, padId, readOnlyId, type) {
|
||||
const doExport = async (req, res, padId, readOnlyId, type) => {
|
||||
// avoid naming the read-only file as the original pad's id
|
||||
let fileName = readOnlyId ? readOnlyId : padId;
|
||||
|
||||
|
@ -104,7 +105,6 @@ async function doExport(req, res, padId, readOnlyId, type) {
|
|||
const result = await hooks.aCallAll('exportConvert', {srcFile, destFile, req, res});
|
||||
if (result.length > 0) {
|
||||
// console.log("export handled by plugin", destFile);
|
||||
handledByPlugin = true;
|
||||
} else {
|
||||
// @TODO no Promise interface for convertors (yet)
|
||||
await new Promise((resolve, reject) => {
|
||||
|
@ -115,7 +115,6 @@ async function doExport(req, res, padId, readOnlyId, type) {
|
|||
}
|
||||
|
||||
// send the file
|
||||
const sendFile = util.promisify(res.sendFile);
|
||||
await res.sendFile(destFile, null);
|
||||
|
||||
// clean up temporary files
|
||||
|
@ -128,9 +127,9 @@ async function doExport(req, res, padId, readOnlyId, type) {
|
|||
|
||||
await fsp_unlink(destFile);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.doExport = function (req, res, padId, readOnlyId, type) {
|
||||
exports.doExport = (req, res, padId, readOnlyId, type) => {
|
||||
doExport(req, res, padId, readOnlyId, type).catch((err) => {
|
||||
if (err !== 'stop') {
|
||||
throw err;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Handles the import requests
|
||||
*/
|
||||
|
@ -30,7 +31,7 @@ const os = require('os');
|
|||
const importHtml = require('../utils/ImportHtml');
|
||||
const importEtherpad = require('../utils/ImportEtherpad');
|
||||
const log4js = require('log4js');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks.js');
|
||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
||||
const util = require('util');
|
||||
|
||||
const fsp_exists = util.promisify(fs.exists);
|
||||
|
@ -42,7 +43,7 @@ let convertor = null;
|
|||
let exportExtension = 'htm';
|
||||
|
||||
// load abiword only if it is enabled and if soffice is disabled
|
||||
if (settings.abiword != null && settings.soffice === null) {
|
||||
if (settings.abiword != null && settings.soffice == null) {
|
||||
convertor = require('../utils/Abiword');
|
||||
}
|
||||
|
||||
|
@ -57,7 +58,7 @@ const tmpDirectory = os.tmpdir();
|
|||
/**
|
||||
* do a requested import
|
||||
*/
|
||||
async function doImport(req, res, padId) {
|
||||
const doImport = async (req, res, padId) => {
|
||||
const apiLogger = log4js.getLogger('ImportHandler');
|
||||
|
||||
// pipe to a file
|
||||
|
@ -112,7 +113,8 @@ async function doImport(req, res, padId) {
|
|||
// ensure this is a file ending we know, else we change the file ending to .txt
|
||||
// this allows us to accept source code files like .c or .java
|
||||
const fileEnding = path.extname(srcFile).toLowerCase();
|
||||
const knownFileEndings = ['.txt', '.doc', '.docx', '.pdf', '.odt', '.html', '.htm', '.etherpad', '.rtf'];
|
||||
const knownFileEndings =
|
||||
['.txt', '.doc', '.docx', '.pdf', '.odt', '.html', '.htm', '.etherpad', '.rtf'];
|
||||
const fileEndingUnknown = (knownFileEndings.indexOf(fileEnding) < 0);
|
||||
|
||||
if (fileEndingUnknown) {
|
||||
|
@ -146,7 +148,7 @@ async function doImport(req, res, padId) {
|
|||
const headCount = _pad.head;
|
||||
|
||||
if (headCount >= 10) {
|
||||
apiLogger.warn("Direct database Import attempt of a pad that already has content, we won't be doing this");
|
||||
apiLogger.warn('Aborting direct database import attempt of a pad that already has content');
|
||||
throw 'padHasData';
|
||||
}
|
||||
|
||||
|
@ -251,9 +253,9 @@ async function doImport(req, res, padId) {
|
|||
if (await fsp_exists(destFile)) {
|
||||
fsp_unlink(destFile);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.doImport = function (req, res, padId) {
|
||||
exports.doImport = (req, res, padId) => {
|
||||
/**
|
||||
* NB: abuse the 'req' object by storing an additional
|
||||
* 'directDatabaseAccess' property on it so that it can
|
||||
|
@ -266,7 +268,10 @@ exports.doImport = function (req, res, padId) {
|
|||
let status = 'ok';
|
||||
doImport(req, res, padId).catch((err) => {
|
||||
// check for known errors and replace the status
|
||||
if (err == 'uploadFailed' || err == 'convertFailed' || err == 'padHasData' || err == 'maxFileSize') {
|
||||
if (err === 'uploadFailed' ||
|
||||
err === 'convertFailed' ||
|
||||
err === 'padHasData' ||
|
||||
err === 'maxFileSize') {
|
||||
status = err;
|
||||
} else {
|
||||
throw err;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
||||
*/
|
||||
|
@ -18,22 +19,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* global exports, process, require */
|
||||
|
||||
const padManager = require('../db/PadManager');
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const AttributePool = require('ep_etherpad-lite/static/js/AttributePool');
|
||||
const AttributeManager = require('ep_etherpad-lite/static/js/AttributeManager');
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
const AttributePool = require('../../static/js/AttributePool');
|
||||
const AttributeManager = require('../../static/js/AttributeManager');
|
||||
const authorManager = require('../db/AuthorManager');
|
||||
const readOnlyManager = require('../db/ReadOnlyManager');
|
||||
const settings = require('../utils/Settings');
|
||||
const securityManager = require('../db/SecurityManager');
|
||||
const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs.js');
|
||||
const plugins = require('../../static/js/pluginfw/plugin_defs.js');
|
||||
const log4js = require('log4js');
|
||||
const messageLogger = log4js.getLogger('message');
|
||||
const accessLogger = log4js.getLogger('access');
|
||||
const _ = require('underscore');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks.js');
|
||||
const hooks = require('../../static/js/pluginfw/hooks.js');
|
||||
const channels = require('channels');
|
||||
const stats = require('../stats');
|
||||
const assert = require('assert').strict;
|
||||
|
@ -65,7 +64,9 @@ stats.gauge('totalUsers', () => Object.keys(socketio.sockets.sockets).length);
|
|||
/**
|
||||
* A changeset queue per pad that is processed by handleUserChanges()
|
||||
*/
|
||||
const padChannels = new channels.channels(({socket, message}, callback) => nodeify(handleUserChanges(socket, message), callback));
|
||||
const padChannels = new channels.channels(
|
||||
({socket, message}, callback) => nodeify(handleUserChanges(socket, message), callback)
|
||||
);
|
||||
|
||||
/**
|
||||
* Saves the Socket class we need to send and receive data from the client
|
||||
|
@ -76,7 +77,7 @@ let socketio;
|
|||
* This Method is called by server.js to tell the message handler on which socket it should send
|
||||
* @param socket_io The Socket
|
||||
*/
|
||||
exports.setSocketIO = function (socket_io) {
|
||||
exports.setSocketIO = (socket_io) => {
|
||||
socketio = socket_io;
|
||||
};
|
||||
|
||||
|
@ -94,7 +95,7 @@ exports.handleConnect = (socket) => {
|
|||
/**
|
||||
* Kicks all sessions from a pad
|
||||
*/
|
||||
exports.kickSessionsFromPad = function (padID) {
|
||||
exports.kickSessionsFromPad = (padID) => {
|
||||
if (typeof socketio.sockets.clients !== 'function') return;
|
||||
|
||||
// skip if there is nobody on this pad
|
||||
|
@ -114,7 +115,8 @@ exports.handleDisconnect = async (socket) => {
|
|||
// save the padname of this session
|
||||
const session = sessioninfos[socket.id];
|
||||
|
||||
// if this connection was already etablished with a handshake, send a disconnect message to the others
|
||||
// if this connection was already etablished with a handshake,
|
||||
// send a disconnect message to the others
|
||||
if (session && session.author) {
|
||||
const {session: {user} = {}} = socket.client.request;
|
||||
accessLogger.info(`${'[LEAVE]' +
|
||||
|
@ -192,7 +194,8 @@ exports.handleMessage = async (socket, message) => {
|
|||
|
||||
const auth = thisSession.auth;
|
||||
if (!auth) {
|
||||
console.error('Auth was never applied to a session. If you are using the stress-test tool then restart Etherpad and the Stress test tool.');
|
||||
console.error('Auth was never applied to a session. If you are using the ' +
|
||||
'stress-test tool then restart Etherpad and the Stress test tool.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -234,7 +237,7 @@ exports.handleMessage = async (socket, message) => {
|
|||
}
|
||||
|
||||
// Call handleMessage hook. If a plugin returns null, the message will be dropped.
|
||||
if ((await hooks.aCallAll('handleMessage', context)).some((m) => m === null)) {
|
||||
if ((await hooks.aCallAll('handleMessage', context)).some((m) => m == null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -283,11 +286,11 @@ exports.handleMessage = async (socket, message) => {
|
|||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
async function handleSaveRevisionMessage(socket, message) {
|
||||
const handleSaveRevisionMessage = async (socket, message) => {
|
||||
const {padId, author: authorId} = sessioninfos[socket.id];
|
||||
const pad = await padManager.getPad(padId);
|
||||
await pad.addSavedRevision(pad.head, authorId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a custom message, different to the function below as it handles
|
||||
|
@ -296,7 +299,7 @@ async function handleSaveRevisionMessage(socket, message) {
|
|||
* @param msg {Object} the message we're sending
|
||||
* @param sessionID {string} the socketIO session to which we're sending this message
|
||||
*/
|
||||
exports.handleCustomObjectMessage = function (msg, sessionID) {
|
||||
exports.handleCustomObjectMessage = (msg, sessionID) => {
|
||||
if (msg.data.type === 'CUSTOM') {
|
||||
if (sessionID) {
|
||||
// a sessionID is targeted: directly to this sessionID
|
||||
|
@ -314,7 +317,7 @@ exports.handleCustomObjectMessage = function (msg, sessionID) {
|
|||
* @param padID {Pad} the pad to which we're sending this message
|
||||
* @param msgString {String} the message we're sending
|
||||
*/
|
||||
exports.handleCustomMessage = function (padID, msgString) {
|
||||
exports.handleCustomMessage = (padID, msgString) => {
|
||||
const time = Date.now();
|
||||
const msg = {
|
||||
type: 'COLLABROOM',
|
||||
|
@ -331,12 +334,12 @@ exports.handleCustomMessage = function (padID, msgString) {
|
|||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
async function handleChatMessage(socket, message) {
|
||||
const handleChatMessage = async (socket, message) => {
|
||||
const time = Date.now();
|
||||
const text = message.data.text;
|
||||
const {padId, author: authorId} = sessioninfos[socket.id];
|
||||
await exports.sendChatMessageToPadClients(time, authorId, text, padId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a chat message to all clients of this pad
|
||||
|
@ -345,7 +348,7 @@ async function handleChatMessage(socket, message) {
|
|||
* @param text the text of the chat message
|
||||
* @param padId the padId to send the chat message to
|
||||
*/
|
||||
exports.sendChatMessageToPadClients = async function (time, userId, text, padId) {
|
||||
exports.sendChatMessageToPadClients = async (time, userId, text, padId) => {
|
||||
// get the pad
|
||||
const pad = await padManager.getPad(padId);
|
||||
|
||||
|
@ -371,7 +374,7 @@ exports.sendChatMessageToPadClients = async function (time, userId, text, padId)
|
|||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
async function handleGetChatMessages(socket, message) {
|
||||
const handleGetChatMessages = async (socket, message) => {
|
||||
if (message.data.start == null) {
|
||||
messageLogger.warn('Dropped message, GetChatMessages Message has no start!');
|
||||
return;
|
||||
|
@ -387,7 +390,8 @@ async function handleGetChatMessages(socket, message) {
|
|||
const count = end - start;
|
||||
|
||||
if (count < 0 || count > 100) {
|
||||
messageLogger.warn('Dropped message, GetChatMessages Message, client requested invalid amount of messages!');
|
||||
messageLogger.warn(
|
||||
'Dropped message, GetChatMessages Message, client requested invalid amount of messages!');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -405,14 +409,14 @@ async function handleGetChatMessages(socket, message) {
|
|||
|
||||
// send the messages back to the client
|
||||
socket.json.send(infoMsg);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a handleSuggestUserName, that means a user have suggest a userName for a other user
|
||||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
function handleSuggestUserName(socket, message) {
|
||||
const handleSuggestUserName = (socket, message) => {
|
||||
// check if all ok
|
||||
if (message.data.payload.newName == null) {
|
||||
messageLogger.warn('Dropped message, suggestUserName Message has no newName!');
|
||||
|
@ -433,14 +437,15 @@ function handleSuggestUserName(socket, message) {
|
|||
socket.json.send(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a USERINFO_UPDATE, that means that a user have changed his color or name. Anyway, we get both informations
|
||||
* Handles a USERINFO_UPDATE, that means that a user have changed his color or name.
|
||||
* Anyway, we get both informations
|
||||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
async function handleUserInfoUpdate(socket, message) {
|
||||
const handleUserInfoUpdate = async (socket, message) => {
|
||||
// check if all ok
|
||||
if (message.data.userInfo == null) {
|
||||
messageLogger.warn('Dropped message, USERINFO_UPDATE Message has no userInfo!');
|
||||
|
@ -463,7 +468,8 @@ async function handleUserInfoUpdate(socket, message) {
|
|||
const author = session.author;
|
||||
|
||||
// Check colorId is a Hex color
|
||||
const isColor = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(message.data.userInfo.colorId); // for #f00 (Thanks Smamatti)
|
||||
// for #f00 (Thanks Smamatti)
|
||||
const isColor = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(message.data.userInfo.colorId);
|
||||
if (!isColor) {
|
||||
messageLogger.warn(`Dropped message, USERINFO_UPDATE Color is malformed.${message.data}`);
|
||||
return;
|
||||
|
@ -496,7 +502,7 @@ async function handleUserInfoUpdate(socket, message) {
|
|||
|
||||
// Block until the authorManager has stored the new attributes.
|
||||
await p;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a USER_CHANGES message, where the client submits its local
|
||||
|
@ -512,7 +518,7 @@ async function handleUserInfoUpdate(socket, message) {
|
|||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
async function handleUserChanges(socket, message) {
|
||||
const handleUserChanges = async (socket, message) => {
|
||||
// This one's no longer pending, as we're gonna process it now
|
||||
stats.counter('pendingEdits').dec();
|
||||
|
||||
|
@ -578,7 +584,8 @@ async function handleUserChanges(socket, message) {
|
|||
|
||||
// + can add text with attribs
|
||||
// = can change or add attribs
|
||||
// - can have attribs, but they are discarded and don't show up in the attribs - but do show up in the pool
|
||||
// - can have attribs, but they are discarded and don't show up in the attribs -
|
||||
// but do show up in the pool
|
||||
|
||||
op.attribs.split('*').forEach((attr) => {
|
||||
if (!attr) return;
|
||||
|
@ -586,9 +593,11 @@ async function handleUserChanges(socket, message) {
|
|||
attr = wireApool.getAttrib(attr);
|
||||
if (!attr) return;
|
||||
|
||||
// the empty author is used in the clearAuthorship functionality so this should be the only exception
|
||||
// the empty author is used in the clearAuthorship functionality so this
|
||||
// should be the only exception
|
||||
if ('author' === attr[0] && (attr[1] !== thisSession.author && attr[1] !== '')) {
|
||||
throw new Error(`Author ${thisSession.author} tried to submit changes as author ${attr[1]} in changeset ${changeset}`);
|
||||
throw new Error(`Author ${thisSession.author} tried to submit changes as author ` +
|
||||
`${attr[1]} in changeset ${changeset}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -628,7 +637,7 @@ async function handleUserChanges(socket, message) {
|
|||
if (baseRev + 1 === r && c === changeset) {
|
||||
socket.json.send({disconnect: 'badChangeset'});
|
||||
stats.meter('failedChangesets').mark();
|
||||
throw new Error("Won't apply USER_CHANGES, because it contains an already accepted changeset");
|
||||
throw new Error("Won't apply USER_CHANGES, as it contains an already accepted changeset");
|
||||
}
|
||||
|
||||
changeset = Changeset.follow(c, changeset, false, apool);
|
||||
|
@ -672,9 +681,9 @@ async function handleUserChanges(socket, message) {
|
|||
}
|
||||
|
||||
stopWatch.end();
|
||||
}
|
||||
};
|
||||
|
||||
exports.updatePadClients = async function (pad) {
|
||||
exports.updatePadClients = async (pad) => {
|
||||
// skip this if no-one is on this pad
|
||||
const roomSockets = _getRoomSockets(pad.id);
|
||||
if (roomSockets.length === 0) return;
|
||||
|
@ -682,9 +691,12 @@ exports.updatePadClients = async function (pad) {
|
|||
// since all clients usually get the same set of changesets, store them in local cache
|
||||
// to remove unnecessary roundtrip to the datalayer
|
||||
// NB: note below possibly now accommodated via the change to promises/async
|
||||
// TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired
|
||||
// BEFORE first result will be landed to our cache object. The solution is to replace parallel processing
|
||||
// via async.forEach with sequential for() loop. There is no real benefits of running this in parallel,
|
||||
// TODO: in REAL world, if we're working without datalayer cache,
|
||||
// all requests to revisions will be fired
|
||||
// BEFORE first result will be landed to our cache object.
|
||||
// The solution is to replace parallel processing
|
||||
// via async.forEach with sequential for() loop. There is no real
|
||||
// benefits of running this in parallel,
|
||||
// but benefit of reusing cached revision object is HUGE
|
||||
const revCache = {};
|
||||
|
||||
|
@ -737,7 +749,7 @@ exports.updatePadClients = async function (pad) {
|
|||
/**
|
||||
* Copied from the Etherpad Source Code. Don't know what this method does excatly...
|
||||
*/
|
||||
function _correctMarkersInPad(atext, apool) {
|
||||
const _correctMarkersInPad = (atext, apool) => {
|
||||
const text = atext.text;
|
||||
|
||||
// collect char positions of line markers (e.g. bullets) in new atext
|
||||
|
@ -746,9 +758,11 @@ function _correctMarkersInPad(atext, apool) {
|
|||
const iter = Changeset.opIterator(atext.attribs);
|
||||
let offset = 0;
|
||||
while (iter.hasNext()) {
|
||||
var op = iter.next();
|
||||
const op = iter.next();
|
||||
|
||||
const hasMarker = _.find(AttributeManager.lineAttributes, (attribute) => Changeset.opAttributeValue(op, attribute, apool)) !== undefined;
|
||||
const hasMarker = _.find(
|
||||
AttributeManager.lineAttributes,
|
||||
(attribute) => Changeset.opAttributeValue(op, attribute, apool)) !== undefined;
|
||||
|
||||
if (hasMarker) {
|
||||
for (let i = 0; i < op.chars; i++) {
|
||||
|
@ -778,9 +792,9 @@ function _correctMarkersInPad(atext, apool) {
|
|||
});
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
};
|
||||
|
||||
async function handleSwitchToPad(socket, message, _authorID) {
|
||||
const handleSwitchToPad = async (socket, message, _authorID) => {
|
||||
const currentSessionInfo = sessioninfos[socket.id];
|
||||
const padId = currentSessionInfo.padId;
|
||||
|
||||
|
@ -816,10 +830,10 @@ async function handleSwitchToPad(socket, message, _authorID) {
|
|||
const newSessionInfo = sessioninfos[socket.id];
|
||||
createSessionInfoAuth(newSessionInfo, message);
|
||||
await handleClientReady(socket, message, authorID);
|
||||
}
|
||||
};
|
||||
|
||||
// Creates/replaces the auth object in the given session info.
|
||||
function createSessionInfoAuth(sessionInfo, message) {
|
||||
const createSessionInfoAuth = (sessionInfo, message) => {
|
||||
// Remember this information since we won't
|
||||
// have the cookie in further socket.io messages.
|
||||
// This information will be used to check if
|
||||
|
@ -830,15 +844,16 @@ function createSessionInfoAuth(sessionInfo, message) {
|
|||
padID: message.padId,
|
||||
token: message.token,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token
|
||||
* Handles a CLIENT_READY. A CLIENT_READY is the first message from the client
|
||||
* to the server. The Client sends his token
|
||||
* and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad
|
||||
* @param socket the socket.io Socket object for the client
|
||||
* @param message the message from the client
|
||||
*/
|
||||
async function handleClientReady(socket, message, authorID) {
|
||||
const handleClientReady = async (socket, message, authorID) => {
|
||||
// check if all ok
|
||||
if (!message.token) {
|
||||
messageLogger.warn('Dropped message, CLIENT_READY Message has no token!');
|
||||
|
@ -884,9 +899,11 @@ async function handleClientReady(socket, message, authorID) {
|
|||
const historicalAuthorData = {};
|
||||
await Promise.all(authors.map((authorId) => authorManager.getAuthor(authorId).then((author) => {
|
||||
if (!author) {
|
||||
messageLogger.error('There is no author for authorId: ', authorId, '. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802');
|
||||
messageLogger.error(`There is no author for authorId: ${authorId}. ` +
|
||||
'This is possibly related to https://github.com/ether/etherpad-lite/issues/2802');
|
||||
} else {
|
||||
historicalAuthorData[authorId] = {name: author.name, colorId: author.colorId}; // Filter author attribs (e.g. don't send author's pads to all clients)
|
||||
// Filter author attribs (e.g. don't send author's pads to all clients)
|
||||
historicalAuthorData[authorId] = {name: author.name, colorId: author.colorId};
|
||||
}
|
||||
})));
|
||||
|
||||
|
@ -931,7 +948,8 @@ async function handleClientReady(socket, message, authorID) {
|
|||
// Save the revision in sessioninfos, we take the revision from the info the client send to us
|
||||
sessionInfo.rev = message.client_rev;
|
||||
|
||||
// During the client reconnect, client might miss some revisions from other clients. By using client revision,
|
||||
// During the client reconnect, client might miss some revisions from other clients.
|
||||
// By using client revision,
|
||||
// this below code sends all the revisions missed during the client reconnect
|
||||
const revisionsNeeded = [];
|
||||
const changesets = {};
|
||||
|
@ -987,12 +1005,13 @@ async function handleClientReady(socket, message, authorID) {
|
|||
}
|
||||
} else {
|
||||
// This is a normal first connect
|
||||
|
||||
let atext;
|
||||
let apool;
|
||||
// prepare all values for the wire, there's a chance that this throws, if the pad is corrupted
|
||||
try {
|
||||
var atext = Changeset.cloneAText(pad.atext);
|
||||
atext = Changeset.cloneAText(pad.atext);
|
||||
const attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool);
|
||||
var apool = attribsForWire.pool.toJsonable();
|
||||
apool = attribsForWire.pool.toJsonable();
|
||||
atext.attribs = attribsForWire.translated;
|
||||
} catch (e) {
|
||||
console.error(e.stack || e);
|
||||
|
@ -1147,12 +1166,12 @@ async function handleClientReady(socket, message, authorID) {
|
|||
socket.json.send(msg);
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a request for a rough changeset, the timeslider client needs it
|
||||
*/
|
||||
async function handleChangesetRequest(socket, message) {
|
||||
const handleChangesetRequest = async (socket, message) => {
|
||||
// check if all ok
|
||||
if (message.data == null) {
|
||||
messageLogger.warn('Dropped message, changeset request has no data!');
|
||||
|
@ -1197,15 +1216,16 @@ async function handleChangesetRequest(socket, message) {
|
|||
data.requestID = message.data.requestID;
|
||||
socket.json.send({type: 'CHANGESET_REQ', data});
|
||||
} catch (err) {
|
||||
console.error(`Error while handling a changeset request for ${padIds.padId}`, err.toString(), message.data);
|
||||
}
|
||||
console.error(`Error while handling a changeset request for ${padIds.padId}`,
|
||||
err.toString(), message.data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to rebuild the getChangestInfo function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L144
|
||||
*/
|
||||
async function getChangesetInfo(padId, startNum, endNum, granularity) {
|
||||
const getChangesetInfo = async (padId, startNum, endNum, granularity) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
const head_revision = pad.getHeadRevisionNumber();
|
||||
|
||||
|
@ -1237,15 +1257,25 @@ async function getChangesetInfo(padId, startNum, endNum, granularity) {
|
|||
|
||||
// get all needed composite Changesets
|
||||
const composedChangesets = {};
|
||||
const p1 = Promise.all(compositesChangesetNeeded.map((item) => composePadChangesets(padId, item.start, item.end).then((changeset) => {
|
||||
const p1 = Promise.all(
|
||||
compositesChangesetNeeded.map(
|
||||
(item) => composePadChangesets(
|
||||
padId, item.start, item.end
|
||||
).then(
|
||||
(changeset) => {
|
||||
composedChangesets[`${item.start}/${item.end}`] = changeset;
|
||||
})));
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// get all needed revision Dates
|
||||
const revisionDate = [];
|
||||
const p2 = Promise.all(revTimesNeeded.map((revNum) => pad.getRevisionDate(revNum).then((revDate) => {
|
||||
const p2 = Promise.all(revTimesNeeded.map((revNum) => pad.getRevisionDate(revNum)
|
||||
.then((revDate) => {
|
||||
revisionDate[revNum] = Math.floor(revDate / 1000);
|
||||
})));
|
||||
})
|
||||
));
|
||||
|
||||
// get the lines
|
||||
let lines;
|
||||
|
@ -1288,13 +1318,13 @@ async function getChangesetInfo(padId, startNum, endNum, granularity) {
|
|||
return {forwardsChangesets, backwardsChangesets,
|
||||
apool: apool.toJsonable(), actualEndNum: endNum,
|
||||
timeDeltas, start: startNum, granularity};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to rebuild the getPadLines function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
||||
*/
|
||||
async function getPadLines(padId, revNum) {
|
||||
const getPadLines = async (padId, revNum) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
|
||||
// get the atext
|
||||
|
@ -1310,13 +1340,13 @@ async function getPadLines(padId, revNum) {
|
|||
textlines: Changeset.splitTextLines(atext.text),
|
||||
alines: Changeset.splitAttributionLines(atext.attribs, atext.text),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to rebuild the composePadChangeset function of the original Etherpad
|
||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L241
|
||||
*/
|
||||
async function composePadChangesets(padId, startNum, endNum) {
|
||||
const composePadChangesets = async (padId, startNum, endNum) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
|
||||
// fetch all changesets we need
|
||||
|
@ -1333,7 +1363,9 @@ async function composePadChangesets(padId, startNum, endNum) {
|
|||
|
||||
// get all changesets
|
||||
const changesets = {};
|
||||
await Promise.all(changesetsNeeded.map((revNum) => pad.getRevisionChangeset(revNum).then((changeset) => changesets[revNum] = changeset)));
|
||||
await Promise.all(changesetsNeeded.map(
|
||||
(revNum) => pad.getRevisionChangeset(revNum).then((changeset) => changesets[revNum] = changeset)
|
||||
));
|
||||
|
||||
// compose Changesets
|
||||
let r;
|
||||
|
@ -1351,9 +1383,9 @@ async function composePadChangesets(padId, startNum, endNum) {
|
|||
console.warn('failed to compose cs in pad:', padId, ' startrev:', startNum, ' current rev:', r);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function _getRoomSockets(padID) {
|
||||
const _getRoomSockets = (padID) => {
|
||||
const roomSockets = [];
|
||||
const room = socketio.sockets.adapter.rooms[padID];
|
||||
|
||||
|
@ -1364,21 +1396,19 @@ function _getRoomSockets(padID) {
|
|||
}
|
||||
|
||||
return roomSockets;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of users in a pad
|
||||
*/
|
||||
exports.padUsersCount = function (padID) {
|
||||
return {
|
||||
exports.padUsersCount = (padID) => ({
|
||||
padUsersCount: _getRoomSockets(padID).length,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the list of users in a pad
|
||||
*/
|
||||
exports.padUsers = async function (padID) {
|
||||
exports.padUsers = async (padID) => {
|
||||
const padUsers = [];
|
||||
|
||||
// iterate over all clients (in parallel)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* This is the Socket.IO Router. It routes the Messages between the
|
||||
* components of the Server. The components are at the moment: pad and timeslider
|
||||
|
@ -21,9 +22,6 @@
|
|||
|
||||
const log4js = require('log4js');
|
||||
const messageLogger = log4js.getLogger('message');
|
||||
const securityManager = require('../db/SecurityManager');
|
||||
const readOnlyManager = require('../db/ReadOnlyManager');
|
||||
const settings = require('../utils/Settings');
|
||||
|
||||
/**
|
||||
* Saves all components
|
||||
|
@ -37,7 +35,7 @@ let socket;
|
|||
/**
|
||||
* adds a component
|
||||
*/
|
||||
exports.addComponent = function (moduleName, module) {
|
||||
exports.addComponent = (moduleName, module) => {
|
||||
// save the component
|
||||
components[moduleName] = module;
|
||||
|
||||
|
@ -48,14 +46,14 @@ exports.addComponent = function (moduleName, module) {
|
|||
/**
|
||||
* sets the socket.io and adds event functions for routing
|
||||
*/
|
||||
exports.setSocketIO = function (_socket) {
|
||||
exports.setSocketIO = (_socket) => {
|
||||
// save this socket internaly
|
||||
socket = _socket;
|
||||
|
||||
socket.sockets.on('connection', (client) => {
|
||||
// wrap the original send function to log the messages
|
||||
client._send = client.send;
|
||||
client.send = function (message) {
|
||||
client.send = (message) => {
|
||||
messageLogger.debug(`to ${client.id}: ${JSON.stringify(message)}`);
|
||||
client._send(message);
|
||||
};
|
||||
|
@ -66,7 +64,7 @@ exports.setSocketIO = function (_socket) {
|
|||
}
|
||||
|
||||
client.on('message', async (message) => {
|
||||
if (message.protocolVersion && message.protocolVersion != 2) {
|
||||
if (message.protocolVersion && message.protocolVersion !== 2) {
|
||||
messageLogger.warn(`Protocolversion header is not correct: ${JSON.stringify(message)}`);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
const eejs = require('ep_etherpad-lite/node/eejs');
|
||||
'use strict';
|
||||
const eejs = require('../../eejs');
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
args.app.get('/admin', (req, res) => {
|
||||
if ('/' != req.path[req.path.length - 1]) return res.redirect('./admin/');
|
||||
if ('/' !== req.path[req.path.length - 1]) return res.redirect('./admin/');
|
||||
res.send(eejs.require('ep_etherpad-lite/templates/admin/index.html', {req}));
|
||||
});
|
||||
return cb();
|
||||
|
|
|
@ -4,7 +4,6 @@ const eejs = require('../../eejs');
|
|||
const settings = require('../../utils/Settings');
|
||||
const installer = require('../../../static/js/pluginfw/installer');
|
||||
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
||||
const _ = require('underscore');
|
||||
const semver = require('semver');
|
||||
const UpdateCheck = require('../../utils/UpdateCheck');
|
||||
|
||||
|
@ -51,7 +50,7 @@ exports.socketio = (hookName, args, cb) => {
|
|||
try {
|
||||
const results = await installer.getAvailablePlugins(/* maxCacheAge:*/ 60 * 10);
|
||||
|
||||
const updatable = _(plugins.plugins).keys().filter((plugin) => {
|
||||
const updatable = Object.keys(plugins.plugins).filter((plugin) => {
|
||||
if (!results[plugin]) return false;
|
||||
|
||||
const latestVersion = results[plugin].version;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
const log4js = require('log4js');
|
||||
const clientLogger = log4js.getLogger('client');
|
||||
const formidable = require('formidable');
|
||||
const apiHandler = require('../../handler/APIHandler');
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
// The Etherpad client side sends information about how a disconnect happened
|
||||
args.app.post('/ep/pad/connection-diagnostic-info', (req, res) => {
|
||||
new formidable.IncomingForm().parse(req, (err, fields, files) => {
|
||||
|
@ -15,8 +17,9 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
// The Etherpad client side sends information about client side javscript errors
|
||||
args.app.post('/jserror', (req, res) => {
|
||||
new formidable.IncomingForm().parse(req, (err, fields, files) => {
|
||||
let data;
|
||||
try {
|
||||
var data = JSON.parse(fields.errorInfo);
|
||||
data = JSON.parse(fields.errorInfo);
|
||||
} catch (e) {
|
||||
return res.end();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const stats = require('ep_etherpad-lite/node/stats');
|
||||
'use strict';
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
const stats = require('../../stats');
|
||||
|
||||
exports.expressCreateServer = (hook_name, args, cb) => {
|
||||
exports.app = args.app;
|
||||
|
||||
// Handle errors
|
||||
|
|
|
@ -1,39 +1,43 @@
|
|||
const assert = require('assert').strict;
|
||||
'use strict';
|
||||
|
||||
const hasPadAccess = require('../../padaccess');
|
||||
const settings = require('../../utils/Settings');
|
||||
const exportHandler = require('../../handler/ExportHandler');
|
||||
const importHandler = require('../../handler/ImportHandler');
|
||||
const padManager = require('../../db/PadManager');
|
||||
const readOnlyManager = require('../../db/ReadOnlyManager');
|
||||
const authorManager = require('../../db/AuthorManager');
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const securityManager = require('../../db/SecurityManager');
|
||||
const webaccess = require('./webaccess');
|
||||
|
||||
settings.importExportRateLimiting.onLimitReached = function (req, res, options) {
|
||||
settings.importExportRateLimiting.onLimitReached = (req, res, options) => {
|
||||
// when the rate limiter triggers, write a warning in the logs
|
||||
console.warn(`Import/Export rate limiter triggered on "${req.originalUrl}" for IP address ${req.ip}`);
|
||||
console.warn('Import/Export rate limiter triggered on ' +
|
||||
`"${req.originalUrl}" for IP address ${req.ip}`);
|
||||
};
|
||||
|
||||
const limiter = rateLimit(settings.importExportRateLimiting);
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
// handle export requests
|
||||
args.app.use('/p/:pad/:rev?/export/:type', limiter);
|
||||
args.app.get('/p/:pad/:rev?/export/:type', async (req, res, next) => {
|
||||
const types = ['pdf', 'doc', 'txt', 'html', 'odt', 'etherpad'];
|
||||
// send a 404 if we don't support this filetype
|
||||
if (types.indexOf(req.params.type) == -1) {
|
||||
if (types.indexOf(req.params.type) === -1) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// if abiword is disabled, and this is a format we only support with abiword, output a message
|
||||
if (settings.exportAvailable() == 'no' &&
|
||||
if (settings.exportAvailable() === 'no' &&
|
||||
['odt', 'pdf', 'doc'].indexOf(req.params.type) !== -1) {
|
||||
console.error(`Impossible to export pad "${req.params.pad}" in ${req.params.type} format. There is no converter configured`);
|
||||
console.error(`Impossible to export pad "${req.params.pad}" in ${req.params.type} format.` +
|
||||
' There is no converter configured');
|
||||
|
||||
// ACHTUNG: do not include req.params.type in res.send() because there is no HTML escaping and it would lead to an XSS
|
||||
res.send('This export is not enabled at this Etherpad instance. Set the path to Abiword or soffice (LibreOffice) in settings.json to enable this feature');
|
||||
// ACHTUNG: do not include req.params.type in res.send() because there is
|
||||
// no HTML escaping and it would lead to an XSS
|
||||
res.send('This export is not enabled at this Etherpad instance. Set the path to Abiword' +
|
||||
' or soffice (LibreOffice) in settings.json to enable this feature');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
const RESERVED_WORDS = [
|
||||
'abstract',
|
||||
'arguments',
|
||||
|
@ -65,9 +67,9 @@ const RESERVED_WORDS = [
|
|||
'yield',
|
||||
];
|
||||
|
||||
const regex = /^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/;
|
||||
const regex = /^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|'.+'|\d+)\])*?$/;
|
||||
|
||||
module.exports.check = function (inputStr) {
|
||||
module.exports.check = (inputStr) => {
|
||||
let isValid = true;
|
||||
inputStr.split('.').forEach((part) => {
|
||||
if (!regex.test(part)) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* node/hooks/express/openapi.js
|
||||
*
|
||||
|
@ -31,7 +33,9 @@ const OPENAPI_VERSION = '3.0.2'; // Swagger/OAS version
|
|||
const info = {
|
||||
title: 'Etherpad API',
|
||||
description:
|
||||
'Etherpad is a real-time collaborative editor scalable to thousands of simultaneous real time users. It provides full data export capabilities, and runs on your server, under your control.',
|
||||
'Etherpad is a real-time collaborative editor scalable to thousands of simultaneous ' +
|
||||
'real time users. It provides full data export capabilities, and runs on your server, ' +
|
||||
'under your control.',
|
||||
termsOfService: 'https://etherpad.org/',
|
||||
contact: {
|
||||
name: 'The Etherpad Foundation',
|
||||
|
@ -80,7 +84,9 @@ const resources = {
|
|||
listSessions: {
|
||||
operationId: 'listSessionsOfGroup',
|
||||
summary: '',
|
||||
responseSchema: {sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}}},
|
||||
responseSchema: {
|
||||
sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}},
|
||||
},
|
||||
},
|
||||
list: {
|
||||
operationId: 'listAllGroups',
|
||||
|
@ -109,7 +115,9 @@ const resources = {
|
|||
listSessions: {
|
||||
operationId: 'listSessionsOfAuthor',
|
||||
summary: 'returns all sessions of an author',
|
||||
responseSchema: {sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}}},
|
||||
responseSchema: {
|
||||
sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}},
|
||||
},
|
||||
},
|
||||
// We need an operation that return a UserInfo so it can be picked up by the codegen :(
|
||||
getName: {
|
||||
|
@ -153,7 +161,8 @@ const resources = {
|
|||
create: {
|
||||
operationId: 'createPad',
|
||||
description:
|
||||
'creates a new (non-group) pad. Note that if you need to create a group Pad, you should call createGroupPad',
|
||||
'creates a new (non-group) pad. Note that if you need to create a group Pad, ' +
|
||||
'you should call createGroupPad',
|
||||
},
|
||||
getText: {
|
||||
operationId: 'getText',
|
||||
|
@ -607,7 +616,7 @@ exports.expressCreateServer = (hookName, args, cb) => {
|
|||
if (createHTTPError.isHttpError(err)) {
|
||||
// pass http errors thrown by handler forward
|
||||
throw err;
|
||||
} else if (err.name == 'apierror') {
|
||||
} else if (err.name === 'apierror') {
|
||||
// parameters were wrong and the api stopped execution, pass the error
|
||||
// convert to http error
|
||||
throw new createHTTPError.BadRequest(err.message);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
const readOnlyManager = require('../../db/ReadOnlyManager');
|
||||
const hasPadAccess = require('../../padaccess');
|
||||
const exporthtml = require('../../utils/ExportHtml');
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
// serve read only pad
|
||||
args.app.get('/ro/:id', async (req, res) => {
|
||||
// translate the read only pad to a padId
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
const padManager = require('../../db/PadManager');
|
||||
const url = require('url');
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
// redirects browser to the pad's sanitized url if needed. otherwise, renders the html
|
||||
args.app.param('pad', async (req, res, next, padId) => {
|
||||
// ensure the padname is valid and the url doesn't end with a /
|
||||
|
@ -17,12 +19,12 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
next();
|
||||
} else {
|
||||
// the pad id was sanitized, so we redirect to the sanitized version
|
||||
let real_url = sanitizedPadId;
|
||||
real_url = encodeURIComponent(real_url);
|
||||
let realURL = sanitizedPadId;
|
||||
realURL = encodeURIComponent(realURL);
|
||||
const query = url.parse(req.url).query;
|
||||
if (query) real_url += `?${query}`;
|
||||
res.header('Location', real_url);
|
||||
res.status(302).send(`You should be redirected to <a href="${real_url}">${real_url}</a>`);
|
||||
if (query) realURL += `?${query}`;
|
||||
res.header('Location', realURL);
|
||||
res.status(302).send(`You should be redirected to <a href="${realURL}">${realURL}</a>`);
|
||||
}
|
||||
});
|
||||
return cb();
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const eejs = require('ep_etherpad-lite/node/eejs');
|
||||
const toolbar = require('ep_etherpad-lite/node/utils/toolbar');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
const eejs = require('../../eejs');
|
||||
const toolbar = require('../../utils/toolbar');
|
||||
const hooks = require('../../../static/js/pluginfw/hooks');
|
||||
const settings = require('../../utils/Settings');
|
||||
const webaccess = require('./webaccess');
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
// expose current stats
|
||||
args.app.get('/stats', (req, res) => {
|
||||
res.json(require('ep_etherpad-lite/node/stats').toJSON());
|
||||
res.json(require('../../stats').toJSON());
|
||||
});
|
||||
|
||||
// serve index.html under /
|
||||
|
@ -24,7 +26,14 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
|
||||
// serve robots.txt
|
||||
args.app.get('/robots.txt', (req, res) => {
|
||||
let filePath = path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'robots.txt');
|
||||
let filePath = path.join(
|
||||
settings.root,
|
||||
'src',
|
||||
'static',
|
||||
'skins',
|
||||
settings.skinName,
|
||||
'robots.txt'
|
||||
);
|
||||
res.sendFile(filePath, (err) => {
|
||||
// there is no custom robots.txt, send the default robots.txt which dissallows all
|
||||
if (err) {
|
||||
|
@ -66,7 +75,14 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
|
||||
// serve favicon.ico from all path levels except as a pad name
|
||||
args.app.get(/\/favicon.ico$/, (req, res) => {
|
||||
let filePath = path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'favicon.ico');
|
||||
let filePath = path.join(
|
||||
settings.root,
|
||||
'src',
|
||||
'static',
|
||||
'skins',
|
||||
settings.skinName,
|
||||
'favicon.ico'
|
||||
);
|
||||
|
||||
res.sendFile(filePath, (err) => {
|
||||
// there is no custom favicon, send the default favicon
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
const minify = require('../../utils/Minify');
|
||||
const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs');
|
||||
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
||||
const CachingMiddleware = require('../../utils/caching_middleware');
|
||||
const settings = require('../../utils/Settings');
|
||||
const Yajsml = require('etherpad-yajsml');
|
||||
const _ = require('underscore');
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
// Cache both minified and static.
|
||||
const assetCache = new CachingMiddleware();
|
||||
args.app.all(/\/javascripts\/(.*)/, assetCache.handle);
|
||||
|
@ -34,7 +35,8 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
args.app.use(jsServer.handle.bind(jsServer));
|
||||
|
||||
// serve plugin definitions
|
||||
// not very static, but served here so that client can do require("pluginfw/static/js/plugin-definitions.js");
|
||||
// not very static, but served here so that client can do
|
||||
// require("pluginfw/static/js/plugin-definitions.js");
|
||||
args.app.get('/pluginfw/plugin-definitions.json', (req, res, next) => {
|
||||
const clientParts = _(plugins.parts)
|
||||
.filter((part) => _(part).has('client_hooks'));
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const npm = require('npm');
|
||||
const fs = require('fs');
|
||||
const util = require('util');
|
||||
|
||||
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
args.app.get('/tests/frontend/specs_list.js', async (req, res) => {
|
||||
const [coreTests, pluginTests] = await Promise.all([
|
||||
exports.getCoreTests(),
|
||||
|
@ -24,9 +26,9 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
// path.join seems to normalize by default, but we'll just be explicit
|
||||
const rootTestFolder = path.normalize(path.join(npm.root, '../tests/frontend/'));
|
||||
|
||||
const url2FilePath = function (url) {
|
||||
const url2FilePath = (url) => {
|
||||
let subPath = url.substr('/tests/frontend'.length);
|
||||
if (subPath == '') {
|
||||
if (subPath === '') {
|
||||
subPath = 'index.html';
|
||||
}
|
||||
subPath = subPath.split('?')[0];
|
||||
|
@ -49,8 +51,9 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
|
||||
content = `describe(${JSON.stringify(specFileName)}, function(){ ${content} });`;
|
||||
|
||||
if(!specFilePath.endsWith('index.html')) res.setHeader('content-type', 'application/javascript');
|
||||
|
||||
if (!specFilePath.endsWith('index.html')) {
|
||||
res.setHeader('content-type', 'application/javascript');
|
||||
}
|
||||
res.send(content);
|
||||
});
|
||||
});
|
||||
|
@ -69,7 +72,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
|
||||
const readdir = util.promisify(fs.readdir);
|
||||
|
||||
exports.getPluginTests = async function (callback) {
|
||||
exports.getPluginTests = async (callback) => {
|
||||
const moduleDir = 'node_modules/';
|
||||
const specPath = '/static/tests/frontend/specs/';
|
||||
const staticDir = '/static/plugins/';
|
||||
|
@ -88,7 +91,4 @@ exports.getPluginTests = async function (callback) {
|
|||
return Promise.all(promises).then(() => pluginSpecs);
|
||||
};
|
||||
|
||||
exports.getCoreTests = function () {
|
||||
// get the core test specs
|
||||
return readdir('tests/frontend/specs');
|
||||
};
|
||||
exports.getCoreTests = () => readdir('tests/frontend/specs');
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
const languages = require('languages4translatewiki');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const _ = require('underscore');
|
||||
const npm = require('npm');
|
||||
const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs.js').plugins;
|
||||
const semver = require('semver');
|
||||
const plugins = require('../../static/js/pluginfw/plugin_defs.js').plugins;
|
||||
const existsSync = require('../utils/path_exists');
|
||||
const settings = require('../utils/Settings')
|
||||
;
|
||||
|
||||
const settings = require('../utils/Settings');
|
||||
|
||||
// returns all existing messages merged together and grouped by langcode
|
||||
// {es: {"foo": "string"}, en:...}
|
||||
function getAllLocales() {
|
||||
const getAllLocales = () => {
|
||||
const locales2paths = {};
|
||||
|
||||
// Puts the paths of all locale files contained in a given directory
|
||||
// into `locales2paths` (files from various dirs are grouped by lang code)
|
||||
// (only json files with valid language code as name)
|
||||
function extractLangs(dir) {
|
||||
const extractLangs = (dir) => {
|
||||
if (!existsSync(dir)) return;
|
||||
let stat = fs.lstatSync(dir);
|
||||
if (!stat.isDirectory() || stat.isSymbolicLink()) return;
|
||||
|
@ -31,12 +30,12 @@ function getAllLocales() {
|
|||
const ext = path.extname(file);
|
||||
const locale = path.basename(file, ext).toLowerCase();
|
||||
|
||||
if ((ext == '.json') && languages.isValid(locale)) {
|
||||
if ((ext === '.json') && languages.isValid(locale)) {
|
||||
if (!locales2paths[locale]) locales2paths[locale] = [];
|
||||
locales2paths[locale].push(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// add core supported languages first
|
||||
extractLangs(`${npm.root}/ep_etherpad-lite/locales`);
|
||||
|
@ -78,29 +77,29 @@ function getAllLocales() {
|
|||
}
|
||||
|
||||
return locales;
|
||||
}
|
||||
};
|
||||
|
||||
// returns a hash of all available languages availables with nativeName and direction
|
||||
// e.g. { es: {nativeName: "español", direction: "ltr"}, ... }
|
||||
function getAvailableLangs(locales) {
|
||||
const getAvailableLangs = (locales) => {
|
||||
const result = {};
|
||||
_.each(_.keys(locales), (langcode) => {
|
||||
result[langcode] = languages.getLanguageInfo(langcode);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// returns locale index that will be served in /locales.json
|
||||
const generateLocaleIndex = function (locales) {
|
||||
const generateLocaleIndex = (locales) => {
|
||||
const result = _.clone(locales); // keep English strings
|
||||
_.each(_.keys(locales), (langcode) => {
|
||||
if (langcode != 'en') result[langcode] = `locales/${langcode}.json`;
|
||||
if (langcode !== 'en') result[langcode] = `locales/${langcode}.json`;
|
||||
});
|
||||
return JSON.stringify(result);
|
||||
};
|
||||
|
||||
|
||||
exports.expressCreateServer = function (n, args, cb) {
|
||||
exports.expressCreateServer = (n, args, cb) => {
|
||||
// regenerate locales on server restart
|
||||
const locales = getAllLocales();
|
||||
const localeIndex = generateLocaleIndex(locales);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
const securityManager = require('./db/SecurityManager');
|
||||
|
||||
// checks for padAccess
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Controls the communication with the Abiword application
|
||||
*/
|
||||
|
@ -25,11 +26,12 @@ const os = require('os');
|
|||
|
||||
let doConvertTask;
|
||||
|
||||
// on windows we have to spawn a process for each convertion, cause the plugin abicommand doesn't exist on this platform
|
||||
// on windows we have to spawn a process for each convertion,
|
||||
// cause the plugin abicommand doesn't exist on this platform
|
||||
if (os.type().indexOf('Windows') > -1) {
|
||||
let stdoutBuffer = '';
|
||||
|
||||
doConvertTask = function (task, callback) {
|
||||
doConvertTask = (task, callback) => {
|
||||
// span an abiword process to perform the conversion
|
||||
const abiword = spawn(settings.abiword, [`--to=${task.destFile}`, task.srcFile]);
|
||||
|
||||
|
@ -46,11 +48,11 @@ if (os.type().indexOf('Windows') > -1) {
|
|||
|
||||
// throw exceptions if abiword is dieing
|
||||
abiword.on('exit', (code) => {
|
||||
if (code != 0) {
|
||||
if (code !== 0) {
|
||||
return callback(`Abiword died with exit code ${code}`);
|
||||
}
|
||||
|
||||
if (stdoutBuffer != '') {
|
||||
if (stdoutBuffer !== '') {
|
||||
console.log(stdoutBuffer);
|
||||
}
|
||||
|
||||
|
@ -58,17 +60,17 @@ if (os.type().indexOf('Windows') > -1) {
|
|||
});
|
||||
};
|
||||
|
||||
exports.convertFile = function (srcFile, destFile, type, callback) {
|
||||
exports.convertFile = (srcFile, destFile, type, callback) => {
|
||||
doConvertTask({srcFile, destFile, type}, callback);
|
||||
};
|
||||
}
|
||||
// on unix operating systems, we can start abiword with abicommand and communicate with it via stdin/stdout
|
||||
// on unix operating systems, we can start abiword with abicommand and
|
||||
// communicate with it via stdin/stdout
|
||||
// thats much faster, about factor 10
|
||||
else {
|
||||
} else {
|
||||
// spawn the abiword process
|
||||
let abiword;
|
||||
let stdoutCallback = null;
|
||||
var spawnAbiword = function () {
|
||||
const spawnAbiword = () => {
|
||||
abiword = spawn(settings.abiword, ['--plugin', 'AbiCommand']);
|
||||
let stdoutBuffer = '';
|
||||
let firstPrompt = true;
|
||||
|
@ -90,9 +92,9 @@ else {
|
|||
stdoutBuffer += data.toString();
|
||||
|
||||
// we're searching for the prompt, cause this means everything we need is in the buffer
|
||||
if (stdoutBuffer.search('AbiWord:>') != -1) {
|
||||
if (stdoutBuffer.search('AbiWord:>') !== -1) {
|
||||
// filter the feedback message
|
||||
const err = stdoutBuffer.search('OK') != -1 ? null : stdoutBuffer;
|
||||
const err = stdoutBuffer.search('OK') !== -1 ? null : stdoutBuffer;
|
||||
|
||||
// reset the buffer
|
||||
stdoutBuffer = '';
|
||||
|
@ -110,10 +112,10 @@ else {
|
|||
};
|
||||
spawnAbiword();
|
||||
|
||||
doConvertTask = function (task, callback) {
|
||||
doConvertTask = (task, callback) => {
|
||||
abiword.stdin.write(`convert ${task.srcFile} ${task.destFile} ${task.type}\n`);
|
||||
// create a callback that calls the task callback and the caller callback
|
||||
stdoutCallback = function (err) {
|
||||
stdoutCallback = (err) => {
|
||||
callback();
|
||||
console.log('queue continue');
|
||||
try {
|
||||
|
@ -126,7 +128,7 @@ else {
|
|||
|
||||
// Queue with the converts we have to do
|
||||
const queue = async.queue(doConvertTask, 1);
|
||||
exports.convertFile = function (srcFile, destFile, type, callback) {
|
||||
exports.convertFile = (srcFile, destFile, type, callback) => {
|
||||
queue.push({srcFile, destFile, type, callback});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Library for deterministic relative filename expansion for Etherpad.
|
||||
*/
|
||||
|
@ -40,7 +41,7 @@ let etherpadRoot = null;
|
|||
* @return {string[]|boolean} The shortened array, or false if there was no
|
||||
* overlap.
|
||||
*/
|
||||
const popIfEndsWith = function (stringArray, lastDesiredElements) {
|
||||
const popIfEndsWith = (stringArray, lastDesiredElements) => {
|
||||
if (stringArray.length <= lastDesiredElements.length) {
|
||||
absPathLogger.debug(`In order to pop "${lastDesiredElements.join(path.sep)}" from "${stringArray.join(path.sep)}", it should contain at least ${lastDesiredElements.length + 1} elements`);
|
||||
|
||||
|
@ -72,8 +73,8 @@ const popIfEndsWith = function (stringArray, lastDesiredElements) {
|
|||
* @return {string} The identified absolute base path. If such path cannot be
|
||||
* identified, prints a log and exits the application.
|
||||
*/
|
||||
exports.findEtherpadRoot = function () {
|
||||
if (etherpadRoot !== null) {
|
||||
exports.findEtherpadRoot = () => {
|
||||
if (etherpadRoot != null) {
|
||||
return etherpadRoot;
|
||||
}
|
||||
|
||||
|
@ -126,7 +127,7 @@ exports.findEtherpadRoot = function () {
|
|||
* it is returned unchanged. Otherwise it is interpreted
|
||||
* relative to exports.root.
|
||||
*/
|
||||
exports.makeAbsolute = function (somePath) {
|
||||
exports.makeAbsolute = (somePath) => {
|
||||
if (path.isAbsolute(somePath)) {
|
||||
return somePath;
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ exports.makeAbsolute = function (somePath) {
|
|||
* a subdirectory of the base one
|
||||
* @return {boolean}
|
||||
*/
|
||||
exports.isSubdir = function (parent, arbitraryDir) {
|
||||
exports.isSubdir = (parent, arbitraryDir) => {
|
||||
// modified from: https://stackoverflow.com/questions/37521893/determine-if-a-path-is-subdirectory-of-another-in-node-js#45242825
|
||||
const relative = path.relative(parent, arbitraryDir);
|
||||
const isSubdir = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The CLI module handles command line parameters
|
||||
*/
|
||||
|
@ -30,22 +31,22 @@ for (let i = 0; i < argv.length; i++) {
|
|||
arg = argv[i];
|
||||
|
||||
// Override location of settings.json file
|
||||
if (prevArg == '--settings' || prevArg == '-s') {
|
||||
if (prevArg === '--settings' || prevArg === '-s') {
|
||||
exports.argv.settings = arg;
|
||||
}
|
||||
|
||||
// Override location of credentials.json file
|
||||
if (prevArg == '--credentials') {
|
||||
if (prevArg === '--credentials') {
|
||||
exports.argv.credentials = arg;
|
||||
}
|
||||
|
||||
// Override location of settings.json file
|
||||
if (prevArg == '--sessionkey') {
|
||||
if (prevArg === '--sessionkey') {
|
||||
exports.argv.sessionkey = arg;
|
||||
}
|
||||
|
||||
// Override location of settings.json file
|
||||
if (prevArg == '--apikey') {
|
||||
if (prevArg === '--apikey') {
|
||||
exports.argv.apikey = arg;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* 2014 John McLear (Etherpad Foundation / McLear Ltd)
|
||||
*
|
||||
|
@ -16,9 +17,9 @@
|
|||
|
||||
|
||||
const db = require('../db/DB');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
|
||||
exports.getPadRaw = async function (padId) {
|
||||
exports.getPadRaw = async (padId) => {
|
||||
const padKey = `pad:${padId}`;
|
||||
const padcontent = await db.get(padKey);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Helpers for export requests
|
||||
*/
|
||||
|
@ -18,9 +19,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
|
||||
exports.getPadPlainText = function (pad, revNum) {
|
||||
exports.getPadPlainText = (pad, revNum) => {
|
||||
const _analyzeLine = exports._analyzeLine;
|
||||
const atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext);
|
||||
const textLines = atext.text.slice(0, -1).split('\n');
|
||||
|
@ -43,7 +44,7 @@ exports.getPadPlainText = function (pad, revNum) {
|
|||
};
|
||||
|
||||
|
||||
exports._analyzeLine = function (text, aline, apool) {
|
||||
exports._analyzeLine = (text, aline, apool) => {
|
||||
const line = {};
|
||||
|
||||
// identify list
|
||||
|
@ -81,6 +82,5 @@ exports._analyzeLine = function (text, aline, apool) {
|
|||
};
|
||||
|
||||
|
||||
exports._encodeWhitespace = function (s) {
|
||||
return s.replace(/[^\x21-\x7E\s\t\n\r]/gu, (c) => `&#${c.codePointAt(0)};`);
|
||||
};
|
||||
exports._encodeWhitespace =
|
||||
(s) => s.replace(/[^\x21-\x7E\s\t\n\r]/gu, (c) => `&#${c.codePointAt(0)};`);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Copyright 2009 Google Inc.
|
||||
*
|
||||
|
@ -14,32 +15,29 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
const padManager = require('../db/PadManager');
|
||||
const _ = require('underscore');
|
||||
const Security = require('ep_etherpad-lite/static/js/security');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
const eejs = require('ep_etherpad-lite/node/eejs');
|
||||
const Security = require('../../static/js/security');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
const eejs = require('../eejs');
|
||||
const _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||
const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
|
||||
const padutils = require('../../static/js/pad_utils').padutils;
|
||||
|
||||
async function getPadHTML(pad, revNum) {
|
||||
const getPadHTML = async (pad, revNum) => {
|
||||
let atext = pad.atext;
|
||||
|
||||
// fetch revision atext
|
||||
if (revNum != undefined) {
|
||||
if (revNum !== undefined) {
|
||||
atext = await pad.getInternalRevisionAText(revNum);
|
||||
}
|
||||
|
||||
// convert atext to html
|
||||
return await getHTMLFromAtext(pad, atext);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getPadHTML = getPadHTML;
|
||||
exports.getHTMLFromAtext = getHTMLFromAtext;
|
||||
|
||||
async function getHTMLFromAtext(pad, atext, authorColors) {
|
||||
const getHTMLFromAtext = async (pad, atext, authorColors) => {
|
||||
const apool = pad.apool();
|
||||
const textLines = atext.text.slice(0, -1).split('\n');
|
||||
const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
|
||||
|
@ -72,9 +70,7 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
const anumMap = {};
|
||||
let css = '';
|
||||
|
||||
const stripDotFromAuthorID = function (id) {
|
||||
return id.replace(/\./g, '_');
|
||||
};
|
||||
const stripDotFromAuthorID = (id) => id.replace(/\./g, '_');
|
||||
|
||||
if (authorColors) {
|
||||
css += '<style>\n';
|
||||
|
@ -85,15 +81,14 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
// skip non author attributes
|
||||
if (attr[0] === 'author' && attr[1] !== '') {
|
||||
// add to props array
|
||||
var propName = `author${stripDotFromAuthorID(attr[1])}`;
|
||||
var newLength = props.push(propName);
|
||||
const propName = `author${stripDotFromAuthorID(attr[1])}`;
|
||||
const newLength = props.push(propName);
|
||||
anumMap[a] = newLength - 1;
|
||||
|
||||
css += `.${propName} {background-color: ${authorColors[attr[1]]}}\n`;
|
||||
} else if (attr[0] === 'removed') {
|
||||
var propName = 'removed';
|
||||
|
||||
var newLength = props.push(propName);
|
||||
const propName = 'removed';
|
||||
const newLength = props.push(propName);
|
||||
anumMap[a] = newLength - 1;
|
||||
|
||||
css += '.removed {text-decoration: line-through; ' +
|
||||
|
@ -122,7 +117,7 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
});
|
||||
|
||||
function getLineHTML(text, attribs) {
|
||||
const getLineHTML = (text, attribs) => {
|
||||
// Use order of tags (b/i/u) as order of nesting, for simplicity
|
||||
// and decent nesting. For example,
|
||||
// <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i>
|
||||
|
@ -132,7 +127,7 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
const assem = Changeset.stringAssembler();
|
||||
const openTags = [];
|
||||
|
||||
function getSpanClassFor(i) {
|
||||
const getSpanClassFor = (i) => {
|
||||
// return if author colors are disabled
|
||||
if (!authorColors) return false;
|
||||
|
||||
|
@ -153,16 +148,16 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// tags added by exportHtmlAdditionalTagsWithData will be exported as <span> with
|
||||
// data attributes
|
||||
function isSpanWithData(i) {
|
||||
const isSpanWithData = (i) => {
|
||||
const property = props[i];
|
||||
return _.isArray(property);
|
||||
}
|
||||
};
|
||||
|
||||
function emitOpenTag(i) {
|
||||
const emitOpenTag = (i) => {
|
||||
openTags.unshift(i);
|
||||
const spanClass = getSpanClassFor(i);
|
||||
|
||||
|
@ -175,10 +170,10 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
assem.append(tags[i]);
|
||||
assem.append('>');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// this closes an open tag and removes its reference from openTags
|
||||
function emitCloseTag(i) {
|
||||
const emitCloseTag = (i) => {
|
||||
openTags.shift();
|
||||
const spanClass = getSpanClassFor(i);
|
||||
const spanWithData = isSpanWithData(i);
|
||||
|
@ -190,13 +185,13 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
assem.append(tags[i]);
|
||||
assem.append('>');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const urls = padutils.findURLs(text);
|
||||
|
||||
let idx = 0;
|
||||
|
||||
function processNextChars(numChars) {
|
||||
const processNextChars = (numChars) => {
|
||||
if (numChars <= 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -208,7 +203,7 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
// based on the attribs used
|
||||
while (iter.hasNext()) {
|
||||
const o = iter.next();
|
||||
var usedAttribs = [];
|
||||
const usedAttribs = [];
|
||||
|
||||
// mark all attribs as used
|
||||
Changeset.eachAttribNumber(o.attribs, (a) => {
|
||||
|
@ -218,7 +213,7 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
});
|
||||
let outermostTag = -1;
|
||||
// find the outer most open tag that is no longer used
|
||||
for (var i = openTags.length - 1; i >= 0; i--) {
|
||||
for (let i = openTags.length - 1; i >= 0; i--) {
|
||||
if (usedAttribs.indexOf(openTags[i]) === -1) {
|
||||
outermostTag = i;
|
||||
break;
|
||||
|
@ -234,7 +229,7 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
|
||||
// open all tags that are used but not open
|
||||
for (i = 0; i < usedAttribs.length; i++) {
|
||||
for (let i = 0; i < usedAttribs.length; i++) {
|
||||
if (openTags.indexOf(usedAttribs[i]) === -1) {
|
||||
emitOpenTag(usedAttribs[i]);
|
||||
}
|
||||
|
@ -258,14 +253,16 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
while (openTags.length > 0) {
|
||||
emitCloseTag(openTags[0]);
|
||||
}
|
||||
} // end processNextChars
|
||||
};
|
||||
// end processNextChars
|
||||
if (urls) {
|
||||
urls.forEach((urlData) => {
|
||||
const startIndex = urlData[0];
|
||||
const url = urlData[1];
|
||||
const urlLength = url.length;
|
||||
processNextChars(startIndex - idx);
|
||||
// Using rel="noreferrer" stops leaking the URL/location of the exported HTML when clicking links in the document.
|
||||
// Using rel="noreferrer" stops leaking the URL/location of the exported HTML
|
||||
// when clicking links in the document.
|
||||
// Not all browsers understand this attribute, but it's part of the HTML5 standard.
|
||||
// https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer
|
||||
// Additionally, we do rel="noopener" to ensure a higher level of referrer security.
|
||||
|
@ -280,7 +277,8 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
processNextChars(text.length - idx);
|
||||
|
||||
return _processSpaces(assem.toString());
|
||||
} // end getLineHTML
|
||||
};
|
||||
// end getLineHTML
|
||||
const pieces = [css];
|
||||
|
||||
// Need to deal with constraints imposed on HTML lists; can
|
||||
|
@ -292,11 +290,11 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
// => keeps track of the parents level of indentation
|
||||
let openLists = [];
|
||||
for (let i = 0; i < textLines.length; i++) {
|
||||
var context;
|
||||
var line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
let context;
|
||||
const line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
const lineContent = getLineHTML(line.text, line.aline);
|
||||
if (line.listLevel)// If we are inside a list
|
||||
{
|
||||
// If we are inside a list
|
||||
if (line.listLevel) {
|
||||
context = {
|
||||
line,
|
||||
lineContent,
|
||||
|
@ -315,8 +313,11 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
await hooks.aCallAll('getLineHTMLForExport', context);
|
||||
// To create list parent elements
|
||||
if ((!prevLine || prevLine.listLevel !== line.listLevel) || (prevLine && line.listTypeName !== prevLine.listTypeName)) {
|
||||
const exists = _.find(openLists, (item) => (item.level === line.listLevel && item.type === line.listTypeName));
|
||||
if ((!prevLine || prevLine.listLevel !== line.listLevel) ||
|
||||
(prevLine && line.listTypeName !== prevLine.listTypeName)) {
|
||||
const exists = _.find(openLists, (item) => (
|
||||
item.level === line.listLevel && item.type === line.listTypeName)
|
||||
);
|
||||
if (!exists) {
|
||||
let prevLevel = 0;
|
||||
if (prevLine && prevLine.listLevel) {
|
||||
|
@ -326,23 +327,33 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
prevLevel = 0;
|
||||
}
|
||||
|
||||
for (var diff = prevLevel; diff < line.listLevel; diff++) {
|
||||
for (let diff = prevLevel; diff < line.listLevel; diff++) {
|
||||
openLists.push({level: diff, type: line.listTypeName});
|
||||
const prevPiece = pieces[pieces.length - 1];
|
||||
|
||||
if (prevPiece.indexOf('<ul') === 0 || prevPiece.indexOf('<ol') === 0 || prevPiece.indexOf('</li>') === 0) {
|
||||
if (prevPiece.indexOf('<ul') === 0 ||
|
||||
prevPiece.indexOf('<ol') === 0 ||
|
||||
prevPiece.indexOf('</li>') === 0) {
|
||||
/*
|
||||
uncommenting this breaks nested ols..
|
||||
if the previous item is NOT a ul, NOT an ol OR closing li then close the list
|
||||
so we consider this HTML, I inserted ** where it throws a problem in Example Wrong..
|
||||
<ol><li>one</li><li><ol><li>1.1</li><li><ol><li>1.1.1</li></ol></li></ol></li><li>two</li></ol>
|
||||
so we consider this HTML,
|
||||
I inserted ** where it throws a problem in Example Wrong..
|
||||
<ol><li>one</li><li><ol><li>1.1</li><li><ol><li>1.1.1</li></ol></li></ol>
|
||||
</li><li>two</li></ol>
|
||||
|
||||
Note that closing the li then re-opening for another li item here is wrong. The correct markup is
|
||||
Note that closing the li then re-opening for another li item here is wrong.
|
||||
The correct markup is
|
||||
<ol><li>one<ol><li>1.1<ol><li>1.1.1</li></ol></li></ol></li><li>two</li></ol>
|
||||
|
||||
Exmaple Right: <ol class="number"><li>one</li><ol start="2" class="number"><li>1.1</li><ol start="3" class="number"><li>1.1.1</li></ol></li></ol><li>two</li></ol>
|
||||
Example Wrong: <ol class="number"><li>one</li>**</li>**<ol start="2" class="number"><li>1.1</li>**</li>**<ol start="3" class="number"><li>1.1.1</li></ol></li></ol><li>two</li></ol>
|
||||
So it's firing wrong where the current piece is an li and the previous piece is an ol and next piece is an ol
|
||||
Exmaple Right: <ol class="number"><li>one</li><ol start="2" class="number">
|
||||
<li>1.1</li><ol start="3" class="number"><li>1.1.1</li></ol></li></ol>
|
||||
<li>two</li></ol>
|
||||
Example Wrong: <ol class="number"><li>one</li>**</li>**
|
||||
<ol start="2" class="number"><li>1.1</li>**</li>**<ol start="3" class="number">
|
||||
<li>1.1.1</li></ol></li></ol><li>two</li></ol>
|
||||
So it's firing wrong where the current piece is an li and the previous piece is
|
||||
an ol and next piece is an ol
|
||||
So to remedy this we can say if next piece is NOT an OL or UL.
|
||||
// pieces.push("</li>");
|
||||
|
||||
|
@ -350,14 +361,16 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
if ((nextLine.listTypeName === 'number') && (nextLine.text === '')) {
|
||||
// is the listTypeName check needed here? null text might be completely fine!
|
||||
// TODO Check against Uls
|
||||
// don't do anything because the next item is a nested ol openener so we need to keep the li open
|
||||
// don't do anything because the next item is a nested ol openener so
|
||||
// we need to keep the li open
|
||||
} else {
|
||||
pieces.push('<li>');
|
||||
}
|
||||
}
|
||||
|
||||
if (line.listTypeName === 'number') {
|
||||
// We introduce line.start here, this is useful for continuing Ordered list line numbers
|
||||
// We introduce line.start here, this is useful for continuing
|
||||
// Ordered list line numbers
|
||||
// in case you have a bullet in a list IE you Want
|
||||
// 1. hello
|
||||
// * foo
|
||||
|
@ -384,18 +397,24 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
|
||||
// To close list elements
|
||||
if (nextLine && nextLine.listLevel === line.listLevel && line.listTypeName === nextLine.listTypeName) {
|
||||
if (nextLine &&
|
||||
nextLine.listLevel === line.listLevel &&
|
||||
line.listTypeName === nextLine.listTypeName) {
|
||||
if (context.lineContent) {
|
||||
if ((nextLine.listTypeName === 'number') && (nextLine.text === '')) {
|
||||
// is the listTypeName check needed here? null text might be completely fine!
|
||||
// TODO Check against Uls
|
||||
// don't do anything because the next item is a nested ol openener so we need to keep the li open
|
||||
// don't do anything because the next item is a nested ol openener so we need to
|
||||
// keep the li open
|
||||
} else {
|
||||
pieces.push('</li>');
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((!nextLine || !nextLine.listLevel || nextLine.listLevel < line.listLevel) || (nextLine && line.listTypeName !== nextLine.listTypeName)) {
|
||||
if ((!nextLine ||
|
||||
!nextLine.listLevel ||
|
||||
nextLine.listLevel < line.listLevel) ||
|
||||
(nextLine && line.listTypeName !== nextLine.listTypeName)) {
|
||||
let nextLevel = 0;
|
||||
if (nextLine && nextLine.listLevel) {
|
||||
nextLevel = nextLine.listLevel;
|
||||
|
@ -404,10 +423,11 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
nextLevel = 0;
|
||||
}
|
||||
|
||||
for (var diff = nextLevel; diff < line.listLevel; diff++) {
|
||||
for (let diff = nextLevel; diff < line.listLevel; diff++) {
|
||||
openLists = openLists.filter((el) => el.level !== diff && el.type !== line.listTypeName);
|
||||
|
||||
if (pieces[pieces.length - 1].indexOf('</ul') === 0 || pieces[pieces.length - 1].indexOf('</ol') === 0) {
|
||||
if (pieces[pieces.length - 1].indexOf('</ul') === 0 ||
|
||||
pieces[pieces.length - 1].indexOf('</ol') === 0) {
|
||||
pieces.push('</li>');
|
||||
}
|
||||
|
||||
|
@ -418,8 +438,8 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else// outside any list, need to close line.listLevel of lists
|
||||
{
|
||||
} else {
|
||||
// outside any list, need to close line.listLevel of lists
|
||||
context = {
|
||||
line,
|
||||
lineContent,
|
||||
|
@ -435,9 +455,9 @@ async function getHTMLFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
|
||||
return pieces.join('');
|
||||
}
|
||||
};
|
||||
|
||||
exports.getPadHTMLDocument = async function (padId, revNum) {
|
||||
exports.getPadHTMLDocument = async (padId, revNum) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
|
||||
// Include some Styles into the Head for Export
|
||||
|
@ -461,7 +481,7 @@ exports.getPadHTMLDocument = async function (padId, revNum) {
|
|||
};
|
||||
|
||||
// copied from ACE
|
||||
function _processSpaces(s) {
|
||||
const _processSpaces = (s) => {
|
||||
const doesWrap = true;
|
||||
if (s.indexOf('<') < 0 && !doesWrap) {
|
||||
// short-cut
|
||||
|
@ -476,34 +496,37 @@ function _processSpaces(s) {
|
|||
let beforeSpace = false;
|
||||
// last space in a run is normal, others are nbsp,
|
||||
// end of line is nbsp
|
||||
for (var i = parts.length - 1; i >= 0; i--) {
|
||||
var p = parts[i];
|
||||
if (p == ' ') {
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
const p = parts[i];
|
||||
if (p === ' ') {
|
||||
if (endOfLine || beforeSpace) parts[i] = ' ';
|
||||
endOfLine = false;
|
||||
beforeSpace = true;
|
||||
} else if (p.charAt(0) != '<') {
|
||||
} else if (p.charAt(0) !== '<') {
|
||||
endOfLine = false;
|
||||
beforeSpace = false;
|
||||
}
|
||||
}
|
||||
// beginning of line is nbsp
|
||||
for (i = 0; i < parts.length; i++) {
|
||||
p = parts[i];
|
||||
if (p == ' ') {
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const p = parts[i];
|
||||
if (p === ' ') {
|
||||
parts[i] = ' ';
|
||||
break;
|
||||
} else if (p.charAt(0) != '<') {
|
||||
} else if (p.charAt(0) !== '<') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < parts.length; i++) {
|
||||
p = parts[i];
|
||||
if (p == ' ') {
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const p = parts[i];
|
||||
if (p === ' ') {
|
||||
parts[i] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
};
|
||||
|
||||
exports.getPadHTML = getPadHTML;
|
||||
exports.getHTMLFromAtext = getHTMLFromAtext;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* TXT export
|
||||
*/
|
||||
|
@ -18,15 +19,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
const padManager = require('../db/PadManager');
|
||||
const _analyzeLine = require('./ExportHelper')._analyzeLine;
|
||||
|
||||
// This is slightly different than the HTML method as it passes the output to getTXTFromAText
|
||||
const getPadTXT = async function (pad, revNum) {
|
||||
const getPadTXT = async (pad, revNum) => {
|
||||
let atext = pad.atext;
|
||||
|
||||
if (revNum != undefined) {
|
||||
if (revNum !== undefined) {
|
||||
// fetch revision atext
|
||||
atext = await pad.getInternalRevisionAText(revNum);
|
||||
}
|
||||
|
@ -37,7 +38,7 @@ const getPadTXT = async function (pad, revNum) {
|
|||
|
||||
// This is different than the functionality provided in ExportHtml as it provides formatting
|
||||
// functionality that is designed specifically for TXT exports
|
||||
function getTXTFromAtext(pad, atext, authorColors) {
|
||||
const getTXTFromAtext = (pad, atext, authorColors) => {
|
||||
const apool = pad.apool();
|
||||
const textLines = atext.text.slice(0, -1).split('\n');
|
||||
const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
|
||||
|
@ -53,7 +54,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
});
|
||||
|
||||
function getLineTXT(text, attribs) {
|
||||
const getLineTXT = (text, attribs) => {
|
||||
const propVals = [false, false, false];
|
||||
const ENTER = 1;
|
||||
const STAY = 2;
|
||||
|
@ -69,7 +70,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
|
||||
let idx = 0;
|
||||
|
||||
function processNextChars(numChars) {
|
||||
const processNextChars = (numChars) => {
|
||||
if (numChars <= 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -79,7 +80,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
|
||||
while (iter.hasNext()) {
|
||||
const o = iter.next();
|
||||
var propChanged = false;
|
||||
let propChanged = false;
|
||||
|
||||
Changeset.eachAttribNumber(o.attribs, (a) => {
|
||||
if (a in anumMap) {
|
||||
|
@ -94,7 +95,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
});
|
||||
|
||||
for (var i = 0; i < propVals.length; i++) {
|
||||
for (let i = 0; i < propVals.length; i++) {
|
||||
if (propVals[i] === true) {
|
||||
propVals[i] = LEAVE;
|
||||
propChanged = true;
|
||||
|
@ -110,7 +111,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
// leaving bold (e.g.) also leaves italics, etc.
|
||||
let left = false;
|
||||
|
||||
for (var i = 0; i < propVals.length; i++) {
|
||||
for (let i = 0; i < propVals.length; i++) {
|
||||
const v = propVals[i];
|
||||
|
||||
if (!left) {
|
||||
|
@ -123,9 +124,9 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
}
|
||||
|
||||
var tags2close = [];
|
||||
const tags2close = [];
|
||||
|
||||
for (var i = propVals.length - 1; i >= 0; i--) {
|
||||
for (let i = propVals.length - 1; i >= 0; i--) {
|
||||
if (propVals[i] === LEAVE) {
|
||||
// emitCloseTag(i);
|
||||
tags2close.push(i);
|
||||
|
@ -136,7 +137,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < propVals.length; i++) {
|
||||
for (let i = 0; i < propVals.length; i++) {
|
||||
if (propVals[i] === ENTER || propVals[i] === STAY) {
|
||||
propVals[i] = true;
|
||||
}
|
||||
|
@ -163,18 +164,20 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
assem.append(s);
|
||||
} // end iteration over spans in line
|
||||
|
||||
var tags2close = [];
|
||||
for (var i = propVals.length - 1; i >= 0; i--) {
|
||||
const tags2close = [];
|
||||
for (let i = propVals.length - 1; i >= 0; i--) {
|
||||
if (propVals[i]) {
|
||||
tags2close.push(i);
|
||||
propVals[i] = false;
|
||||
}
|
||||
}
|
||||
} // end processNextChars
|
||||
};
|
||||
// end processNextChars
|
||||
|
||||
processNextChars(text.length - idx);
|
||||
return (assem.toString());
|
||||
} // end getLineHTML
|
||||
};
|
||||
// end getLineHTML
|
||||
|
||||
const pieces = [css];
|
||||
|
||||
|
@ -193,7 +196,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
const line = _analyzeLine(textLines[i], attribLines[i], apool);
|
||||
let lineContent = getLineTXT(line.text, line.aline);
|
||||
|
||||
if (line.listTypeName == 'bullet') {
|
||||
if (line.listTypeName === 'bullet') {
|
||||
lineContent = `* ${lineContent}`; // add a bullet
|
||||
}
|
||||
|
||||
|
@ -212,7 +215,7 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
}
|
||||
|
||||
if (line.listTypeName == 'number') {
|
||||
if (line.listTypeName === 'number') {
|
||||
/*
|
||||
* listLevel == amount of indentation
|
||||
* listNumber(s) == item number
|
||||
|
@ -249,11 +252,11 @@ function getTXTFromAtext(pad, atext, authorColors) {
|
|||
}
|
||||
|
||||
return pieces.join('');
|
||||
}
|
||||
};
|
||||
|
||||
exports.getTXTFromAtext = getTXTFromAtext;
|
||||
|
||||
exports.getPadTXTDocument = async function (padId, revNum) {
|
||||
exports.getPadTXTDocument = async (padId, revNum) => {
|
||||
const pad = await padManager.getPad(padId);
|
||||
return getPadTXT(pad, revNum);
|
||||
};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// 'use strict';
|
||||
// Uncommenting above breaks tests.
|
||||
/**
|
||||
* 2014 John McLear (Etherpad Foundation / McLear Ltd)
|
||||
*
|
||||
|
@ -14,12 +16,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const log4js = require('log4js');
|
||||
const db = require('../db/DB');
|
||||
const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks');
|
||||
const hooks = require('../../static/js/pluginfw/hooks');
|
||||
|
||||
exports.setPadRaw = function (padId, records) {
|
||||
records = JSON.parse(records);
|
||||
exports.setPadRaw = (padId, r) => {
|
||||
const records = JSON.parse(r);
|
||||
|
||||
Object.keys(records).forEach(async (key) => {
|
||||
let value = records[key];
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Copyright Yaco Sistemas S.L. 2011.
|
||||
*
|
||||
|
@ -15,8 +16,8 @@
|
|||
*/
|
||||
|
||||
const log4js = require('log4js');
|
||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
||||
const contentcollector = require('ep_etherpad-lite/static/js/contentcollector');
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
const contentcollector = require('../../static/js/contentcollector');
|
||||
const cheerio = require('cheerio');
|
||||
const rehype = require('rehype');
|
||||
const minifyWhitespace = require('rehype-minify-whitespace');
|
||||
|
@ -69,7 +70,7 @@ exports.setPadHTML = async (pad, html) => {
|
|||
apiLogger.debug(newText);
|
||||
const newAttribs = `${result.lineAttribs.join('|1+1')}|1+1`;
|
||||
|
||||
function eachAttribRun(attribs, func /* (startInNewText, endInNewText, attribs)*/) {
|
||||
const eachAttribRun = (attribs, func /* (startInNewText, endInNewText, attribs)*/) => {
|
||||
const attribsIter = Changeset.opIterator(attribs);
|
||||
let textIndex = 0;
|
||||
const newTextStart = 0;
|
||||
|
@ -82,7 +83,7 @@ exports.setPadHTML = async (pad, html) => {
|
|||
}
|
||||
textIndex = nextIndex;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// create a new changeset with a helper builder object
|
||||
const builder = Changeset.builder(1);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Controls the communication with LibreOffice
|
||||
*/
|
||||
|
@ -24,52 +25,9 @@ const path = require('path');
|
|||
const settings = require('./Settings');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
// Conversion tasks will be queued up, so we don't overload the system
|
||||
const queue = async.queue(doConvertTask, 1);
|
||||
|
||||
const libreOfficeLogger = log4js.getLogger('LibreOffice');
|
||||
|
||||
/**
|
||||
* Convert a file from one type to another
|
||||
*
|
||||
* @param {String} srcFile The path on disk to convert
|
||||
* @param {String} destFile The path on disk where the converted file should be stored
|
||||
* @param {String} type The type to convert into
|
||||
* @param {Function} callback Standard callback function
|
||||
*/
|
||||
exports.convertFile = function (srcFile, destFile, type, callback) {
|
||||
// Used for the moving of the file, not the conversion
|
||||
const fileExtension = type;
|
||||
|
||||
if (type === 'html') {
|
||||
// "html:XHTML Writer File:UTF8" does a better job than normal html exports
|
||||
if (path.extname(srcFile).toLowerCase() === '.doc') {
|
||||
type = 'html';
|
||||
}
|
||||
// PDF files need to be converted with LO Draw ref https://github.com/ether/etherpad-lite/issues/4151
|
||||
if (path.extname(srcFile).toLowerCase() === '.pdf') {
|
||||
type = 'html:XHTML Draw File';
|
||||
}
|
||||
}
|
||||
|
||||
// soffice can't convert from html to doc directly (verified with LO 5 and 6)
|
||||
// we need to convert to odt first, then to doc
|
||||
// to avoid `Error: no export filter for /tmp/xxxx.doc` error
|
||||
if (type === 'doc') {
|
||||
queue.push({
|
||||
srcFile,
|
||||
destFile: destFile.replace(/\.doc$/, '.odt'),
|
||||
type: 'odt',
|
||||
callback() {
|
||||
queue.push({srcFile: srcFile.replace(/\.html$/, '.odt'), destFile, type, callback, fileExtension});
|
||||
},
|
||||
});
|
||||
} else {
|
||||
queue.push({srcFile, destFile, type, callback, fileExtension});
|
||||
}
|
||||
};
|
||||
|
||||
function doConvertTask(task, callback) {
|
||||
const doConvertTask = (task, callback) => {
|
||||
const tmpDir = os.tmpdir();
|
||||
|
||||
async.series([
|
||||
|
@ -77,8 +35,10 @@ function doConvertTask(task, callback) {
|
|||
* use LibreOffice to convert task.srcFile to another format, given in
|
||||
* task.type
|
||||
*/
|
||||
function (callback) {
|
||||
libreOfficeLogger.debug(`Converting ${task.srcFile} to format ${task.type}. The result will be put in ${tmpDir}`);
|
||||
(callback) => {
|
||||
libreOfficeLogger.debug(
|
||||
`Converting ${task.srcFile} to format ${task.type}. The result will be put in ${tmpDir}`
|
||||
);
|
||||
const soffice = spawn(settings.soffice, [
|
||||
'--headless',
|
||||
'--invisible',
|
||||
|
@ -112,7 +72,7 @@ function doConvertTask(task, callback) {
|
|||
|
||||
soffice.on('exit', (code) => {
|
||||
clearTimeout(hangTimeout);
|
||||
if (code != 0) {
|
||||
if (code !== 0) {
|
||||
// Throw an exception if libreoffice failed
|
||||
return callback(`LibreOffice died with exit code ${code} and message: ${stdoutBuffer}`);
|
||||
}
|
||||
|
@ -123,10 +83,10 @@ function doConvertTask(task, callback) {
|
|||
},
|
||||
|
||||
// Move the converted file to the correct place
|
||||
function (callback) {
|
||||
(callback) => {
|
||||
const filename = path.basename(task.srcFile);
|
||||
const sourceFilename = `${filename.substr(0, filename.lastIndexOf('.'))}.${task.fileExtension}`;
|
||||
const sourcePath = path.join(tmpDir, sourceFilename);
|
||||
const sourceFile = `${filename.substr(0, filename.lastIndexOf('.'))}.${task.fileExtension}`;
|
||||
const sourcePath = path.join(tmpDir, sourceFile);
|
||||
libreOfficeLogger.debug(`Renaming ${sourcePath} to ${task.destFile}`);
|
||||
fs.rename(sourcePath, task.destFile, callback);
|
||||
},
|
||||
|
@ -137,4 +97,55 @@ function doConvertTask(task, callback) {
|
|||
// Invoke the callback for the task
|
||||
task.callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
// Conversion tasks will be queued up, so we don't overload the system
|
||||
const queue = async.queue(doConvertTask, 1);
|
||||
|
||||
/**
|
||||
* Convert a file from one type to another
|
||||
*
|
||||
* @param {String} srcFile The path on disk to convert
|
||||
* @param {String} destFile The path on disk where the converted file should be stored
|
||||
* @param {String} type The type to convert into
|
||||
* @param {Function} callback Standard callback function
|
||||
*/
|
||||
exports.convertFile = (srcFile, destFile, type, callback) => {
|
||||
// Used for the moving of the file, not the conversion
|
||||
const fileExtension = type;
|
||||
|
||||
if (type === 'html') {
|
||||
// "html:XHTML Writer File:UTF8" does a better job than normal html exports
|
||||
if (path.extname(srcFile).toLowerCase() === '.doc') {
|
||||
type = 'html';
|
||||
}
|
||||
// PDF files need to be converted with LO Draw ref https://github.com/ether/etherpad-lite/issues/4151
|
||||
if (path.extname(srcFile).toLowerCase() === '.pdf') {
|
||||
type = 'html:XHTML Draw File';
|
||||
}
|
||||
}
|
||||
|
||||
// soffice can't convert from html to doc directly (verified with LO 5 and 6)
|
||||
// we need to convert to odt first, then to doc
|
||||
// to avoid `Error: no export filter for /tmp/xxxx.doc` error
|
||||
if (type === 'doc') {
|
||||
queue.push({
|
||||
srcFile,
|
||||
destFile: destFile.replace(/\.doc$/, '.odt'),
|
||||
type: 'odt',
|
||||
callback: () => {
|
||||
queue.push(
|
||||
{
|
||||
srcFile: srcFile.replace(/\.html$/, '.odt'),
|
||||
destFile,
|
||||
type,
|
||||
callback,
|
||||
fileExtension,
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
queue.push({srcFile, destFile, type, callback, fileExtension});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Worker thread to minify JS & CSS files out of the main NodeJS thread
|
||||
*/
|
||||
|
@ -7,12 +8,9 @@ const Terser = require('terser');
|
|||
const path = require('path');
|
||||
const Threads = require('threads');
|
||||
|
||||
function compressJS(content) {
|
||||
return Terser.minify(content);
|
||||
}
|
||||
const compressJS = (content) => Terser.minify(content);
|
||||
|
||||
function compressCSS(filename, ROOT_DIR) {
|
||||
return new Promise((res, rej) => {
|
||||
const compressCSS = (filename, ROOT_DIR) => new Promise((res, rej) => {
|
||||
try {
|
||||
const absPath = path.join(ROOT_DIR, filename);
|
||||
|
||||
|
@ -57,7 +55,6 @@ function compressCSS(filename, ROOT_DIR) {
|
|||
callback(null, content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Threads.expose({
|
||||
compressJS,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Checks related to Node runtime version
|
||||
*/
|
||||
|
@ -25,7 +26,7 @@ const semver = require('semver');
|
|||
*
|
||||
* @param {String} minNodeVersion Minimum required Node version
|
||||
*/
|
||||
exports.enforceMinNodeVersion = function (minNodeVersion) {
|
||||
exports.enforceMinNodeVersion = (minNodeVersion) => {
|
||||
const currentNodeVersion = process.version;
|
||||
|
||||
// we cannot use template literals, since we still do not know if we are
|
||||
|
@ -41,10 +42,12 @@ exports.enforceMinNodeVersion = function (minNodeVersion) {
|
|||
/**
|
||||
* Prints a warning if running on a supported but deprecated Node version
|
||||
*
|
||||
* @param {String} lowestNonDeprecatedNodeVersion all Node version less than this one are deprecated
|
||||
* @param {Function} epRemovalVersion Etherpad version that will remove support for deprecated Node releases
|
||||
* @param {String} lowestNonDeprecatedNodeVersion all Node version less than this one are
|
||||
* deprecated
|
||||
* @param {Function} epRemovalVersion Etherpad version that will remove support for deprecated
|
||||
* Node releases
|
||||
*/
|
||||
exports.checkDeprecationStatus = function (lowestNonDeprecatedNodeVersion, epRemovalVersion) {
|
||||
exports.checkDeprecationStatus = (lowestNonDeprecatedNodeVersion, epRemovalVersion) => {
|
||||
const currentNodeVersion = process.version;
|
||||
|
||||
if (semver.lt(currentNodeVersion, lowestNonDeprecatedNodeVersion)) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The Settings module reads the settings out of settings.json and provides
|
||||
* this information to the other modules
|
||||
|
@ -31,7 +32,6 @@ const fs = require('fs');
|
|||
const os = require('os');
|
||||
const path = require('path');
|
||||
const argv = require('./Cli').argv;
|
||||
const npm = require('npm/lib/npm.js');
|
||||
const jsonminify = require('jsonminify');
|
||||
const log4js = require('log4js');
|
||||
const randomString = require('./randomstring');
|
||||
|
@ -381,29 +381,30 @@ exports.commitRateLimiting = {
|
|||
exports.importMaxFileSize = 50 * 1024 * 1024;
|
||||
|
||||
// checks if abiword is avaiable
|
||||
exports.abiwordAvailable = function () {
|
||||
exports.abiwordAvailable = () => {
|
||||
if (exports.abiword != null) {
|
||||
return os.type().indexOf('Windows') != -1 ? 'withoutPDF' : 'yes';
|
||||
return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes';
|
||||
} else {
|
||||
return 'no';
|
||||
}
|
||||
};
|
||||
|
||||
exports.sofficeAvailable = function () {
|
||||
exports.sofficeAvailable = () => {
|
||||
if (exports.soffice != null) {
|
||||
return os.type().indexOf('Windows') != -1 ? 'withoutPDF' : 'yes';
|
||||
return os.type().indexOf('Windows') !== -1 ? 'withoutPDF' : 'yes';
|
||||
} else {
|
||||
return 'no';
|
||||
}
|
||||
};
|
||||
|
||||
exports.exportAvailable = function () {
|
||||
exports.exportAvailable = () => {
|
||||
const abiword = exports.abiwordAvailable();
|
||||
const soffice = exports.sofficeAvailable();
|
||||
|
||||
if (abiword == 'no' && soffice == 'no') {
|
||||
if (abiword === 'no' && soffice === 'no') {
|
||||
return 'no';
|
||||
} else if ((abiword == 'withoutPDF' && soffice == 'no') || (abiword == 'no' && soffice == 'withoutPDF')) {
|
||||
} else if ((abiword === 'withoutPDF' && soffice === 'no') ||
|
||||
(abiword === 'no' && soffice === 'withoutPDF')) {
|
||||
return 'withoutPDF';
|
||||
} else {
|
||||
return 'yes';
|
||||
|
@ -411,7 +412,7 @@ exports.exportAvailable = function () {
|
|||
};
|
||||
|
||||
// Provide git version if available
|
||||
exports.getGitCommit = function () {
|
||||
exports.getGitCommit = () => {
|
||||
let version = '';
|
||||
try {
|
||||
let rootPath = exports.root;
|
||||
|
@ -436,9 +437,7 @@ exports.getGitCommit = function () {
|
|||
};
|
||||
|
||||
// Return etherpad version from package.json
|
||||
exports.getEpVersion = function () {
|
||||
return require('ep_etherpad-lite/package.json').version;
|
||||
};
|
||||
exports.getEpVersion = () => require('../../package.json').version;
|
||||
|
||||
/**
|
||||
* Receives a settingsObj and, if the property name is a valid configuration
|
||||
|
@ -447,7 +446,7 @@ exports.getEpVersion = function () {
|
|||
* This code refactors a previous version that copied & pasted the same code for
|
||||
* both "settings.json" and "credentials.json".
|
||||
*/
|
||||
function storeSettings(settingsObj) {
|
||||
const storeSettings = (settingsObj) => {
|
||||
for (const i in settingsObj) {
|
||||
// test if the setting starts with a lowercase character
|
||||
if (i.charAt(0).search('[a-z]') !== 0) {
|
||||
|
@ -456,7 +455,7 @@ function storeSettings(settingsObj) {
|
|||
|
||||
// we know this setting, so we overwrite it
|
||||
// or it's a settings hash, specific to a plugin
|
||||
if (exports[i] !== undefined || i.indexOf('ep_') == 0) {
|
||||
if (exports[i] !== undefined || i.indexOf('ep_') === 0) {
|
||||
if (_.isObject(settingsObj[i]) && !_.isArray(settingsObj[i])) {
|
||||
exports[i] = _.defaults(settingsObj[i], exports[i]);
|
||||
} else {
|
||||
|
@ -467,7 +466,7 @@ function storeSettings(settingsObj) {
|
|||
console.warn(`Unknown Setting: '${i}'. This setting doesn't exist or it was removed`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* If stringValue is a numeric string, or its value is "true" or "false", coerce
|
||||
|
@ -481,7 +480,7 @@ function storeSettings(settingsObj) {
|
|||
* short syntax "${ABIWORD}", and not "${ABIWORD:null}": the latter would result
|
||||
* in the literal string "null", instead.
|
||||
*/
|
||||
function coerceValue(stringValue) {
|
||||
const coerceValue = (stringValue) => {
|
||||
// cooked from https://stackoverflow.com/questions/175739/built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number
|
||||
const isNumeric = !isNaN(stringValue) && !isNaN(parseFloat(stringValue) && isFinite(stringValue));
|
||||
|
||||
|
@ -502,7 +501,7 @@ function coerceValue(stringValue) {
|
|||
|
||||
// otherwise, return this value as-is
|
||||
return stringValue;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a javascript object containing Etherpad's configuration, and returns
|
||||
|
@ -540,7 +539,7 @@ function coerceValue(stringValue) {
|
|||
*
|
||||
* see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter
|
||||
*/
|
||||
function lookupEnvironmentVariables(obj) {
|
||||
const lookupEnvironmentVariables = (obj) => {
|
||||
const stringifiedAndReplaced = JSON.stringify(obj, (key, value) => {
|
||||
/*
|
||||
* the first invocation of replacer() is with an empty key. Just go on, or
|
||||
|
@ -569,7 +568,7 @@ function lookupEnvironmentVariables(obj) {
|
|||
// MUXATOR 2019-03-21: we could use named capture groups here once we migrate to nodejs v10
|
||||
const match = value.match(/^\$\{([^:]*)(:((.|\n)*))?\}$/);
|
||||
|
||||
if (match === null) {
|
||||
if (match == null) {
|
||||
// no match: use the value literally, without any substitution
|
||||
|
||||
return value;
|
||||
|
@ -613,7 +612,7 @@ function lookupEnvironmentVariables(obj) {
|
|||
const newSettings = JSON.parse(stringifiedAndReplaced);
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* - reads the JSON configuration file settingsFilename from disk
|
||||
|
@ -623,7 +622,7 @@ function lookupEnvironmentVariables(obj) {
|
|||
*
|
||||
* The isSettings variable only controls the error logging.
|
||||
*/
|
||||
function parseSettings(settingsFilename, isSettings) {
|
||||
const parseSettings = (settingsFilename, isSettings) => {
|
||||
let settingsStr = '';
|
||||
|
||||
let settingsType, notFoundMessage, notFoundFunction;
|
||||
|
@ -663,9 +662,9 @@ function parseSettings(settingsFilename, isSettings) {
|
|||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.reloadSettings = function reloadSettings() {
|
||||
exports.reloadSettings = () => {
|
||||
// Discover where the settings file lives
|
||||
const settingsFilename = absolutePaths.makeAbsolute(argv.settings || 'settings.json');
|
||||
|
||||
|
@ -695,7 +694,7 @@ exports.reloadSettings = function reloadSettings() {
|
|||
const skinBasePath = path.join(exports.root, 'src', 'static', 'skins');
|
||||
const countPieces = exports.skinName.split(path.sep).length;
|
||||
|
||||
if (countPieces != 1) {
|
||||
if (countPieces !== 1) {
|
||||
console.error(`skinName must be the name of a directory under "${skinBasePath}". This is not valid: "${exports.skinName}". Falling back to the default "colibris".`);
|
||||
|
||||
exports.skinName = 'colibris';
|
||||
|
@ -766,7 +765,7 @@ exports.reloadSettings = function reloadSettings() {
|
|||
}
|
||||
|
||||
if (exports.dbType === 'dirty') {
|
||||
const dirtyWarning = 'DirtyDB is used. This is fine for testing but not recommended for production.';
|
||||
const dirtyWarning = 'DirtyDB is used. This is not recommended for production.';
|
||||
if (!exports.suppressErrorsInPadText) {
|
||||
exports.defaultPadText = `${exports.defaultPadText}\nWarning: ${dirtyWarning}${suppressDisableMsg}`;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Tidy up the HTML in a given file
|
||||
*/
|
||||
|
@ -6,7 +7,7 @@ const log4js = require('log4js');
|
|||
const settings = require('./Settings');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
exports.tidy = function (srcFile) {
|
||||
exports.tidy = (srcFile) => {
|
||||
const logger = log4js.getLogger('TidyHtml');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
'use strict';
|
||||
const semver = require('semver');
|
||||
const settings = require('./Settings');
|
||||
const request = require('request');
|
||||
|
||||
let infos;
|
||||
|
||||
function loadEtherpadInformations() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const loadEtherpadInformations = () => new Promise((resolve, reject) => {
|
||||
request('https://static.etherpad.org/info.json', (er, response, body) => {
|
||||
if (er) return reject(er);
|
||||
|
||||
|
@ -17,14 +17,13 @@ function loadEtherpadInformations() {
|
|||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.getLatestVersion = function () {
|
||||
exports.getLatestVersion = () => {
|
||||
exports.needsUpdate();
|
||||
return infos.latestVersion;
|
||||
};
|
||||
|
||||
exports.needsUpdate = function (cb) {
|
||||
exports.needsUpdate = (cb) => {
|
||||
loadEtherpadInformations().then((info) => {
|
||||
if (semver.gt(info.latestVersion, settings.getEpVersion())) {
|
||||
if (cb) return cb(true);
|
||||
|
@ -35,7 +34,7 @@ exports.needsUpdate = function (cb) {
|
|||
});
|
||||
};
|
||||
|
||||
exports.check = function () {
|
||||
exports.check = () => {
|
||||
exports.needsUpdate((needsUpdate) => {
|
||||
if (needsUpdate) {
|
||||
console.warn(`Update available: Download the actual version ${infos.latestVersion}`);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
/*
|
||||
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||
*
|
||||
|
@ -47,18 +49,16 @@ CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined;
|
|||
|
||||
const responseCache = {};
|
||||
|
||||
function djb2Hash(data) {
|
||||
const djb2Hash = (data) => {
|
||||
const chars = data.split('').map((str) => str.charCodeAt(0));
|
||||
return `${chars.reduce((prev, curr) => ((prev << 5) + prev) + curr, 5381)}`;
|
||||
}
|
||||
};
|
||||
|
||||
function generateCacheKeyWithSha256(path) {
|
||||
return _crypto.createHash('sha256').update(path).digest('hex');
|
||||
}
|
||||
const generateCacheKeyWithSha256 =
|
||||
(path) => _crypto.createHash('sha256').update(path).digest('hex');
|
||||
|
||||
function generateCacheKeyWithDjb2(path) {
|
||||
return Buffer.from(djb2Hash(path)).toString('hex');
|
||||
}
|
||||
const generateCacheKeyWithDjb2 =
|
||||
(path) => Buffer.from(djb2Hash(path)).toString('hex');
|
||||
|
||||
let generateCacheKey;
|
||||
|
||||
|
@ -66,7 +66,7 @@ if (_crypto) {
|
|||
generateCacheKey = generateCacheKeyWithSha256;
|
||||
} else {
|
||||
generateCacheKey = generateCacheKeyWithDjb2;
|
||||
console.warn('No crypto support in this nodejs runtime. A fallback to Djb2 (weaker) will be used.');
|
||||
console.warn('No crypto support in this nodejs runtime. Djb2 (weaker) will be used.');
|
||||
}
|
||||
|
||||
// MIMIC https://github.com/microsoft/TypeScript/commit/9677b0641cc5ba7d8b701b4f892ed7e54ceaee9a - END
|
||||
|
@ -80,8 +80,8 @@ if (_crypto) {
|
|||
function CachingMiddleware() {
|
||||
}
|
||||
CachingMiddleware.prototype = new function () {
|
||||
function handle(req, res, next) {
|
||||
if (!(req.method == 'GET' || req.method == 'HEAD') || !CACHE_DIR) {
|
||||
const handle = (req, res, next) => {
|
||||
if (!(req.method === 'GET' || req.method === 'HEAD') || !CACHE_DIR) {
|
||||
return next(undefined, req, res);
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ CachingMiddleware.prototype = new function () {
|
|||
const old_res = {};
|
||||
|
||||
const supportsGzip =
|
||||
(req.get('Accept-Encoding') || '').indexOf('gzip') != -1;
|
||||
(req.get('Accept-Encoding') || '').indexOf('gzip') !== -1;
|
||||
|
||||
const path = require('url').parse(req.url).path;
|
||||
const cacheKey = generateCacheKey(path);
|
||||
|
@ -116,7 +116,7 @@ CachingMiddleware.prototype = new function () {
|
|||
|
||||
const _headers = {};
|
||||
old_res.setHeader = res.setHeader;
|
||||
res.setHeader = function (key, value) {
|
||||
res.setHeader = (key, value) => {
|
||||
// Don't set cookies, see issue #707
|
||||
if (key.toLowerCase() === 'set-cookie') return;
|
||||
|
||||
|
@ -126,11 +126,8 @@ CachingMiddleware.prototype = new function () {
|
|||
|
||||
old_res.writeHead = res.writeHead;
|
||||
res.writeHead = function (status, headers) {
|
||||
const lastModified = (res.getHeader('last-modified') &&
|
||||
new Date(res.getHeader('last-modified')));
|
||||
|
||||
res.writeHead = old_res.writeHead;
|
||||
if (status == 200) {
|
||||
if (status === 200) {
|
||||
// Update cache
|
||||
let buffer = '';
|
||||
|
||||
|
@ -169,7 +166,7 @@ CachingMiddleware.prototype = new function () {
|
|||
respond();
|
||||
});
|
||||
};
|
||||
} else if (status == 304) {
|
||||
} else if (status === 304) {
|
||||
// Nothing new changed from the cached version.
|
||||
old_res.write = res.write;
|
||||
old_res.end = res.end;
|
||||
|
@ -204,10 +201,10 @@ CachingMiddleware.prototype = new function () {
|
|||
const lastModified = (headers['last-modified'] &&
|
||||
new Date(headers['last-modified']));
|
||||
|
||||
if (statusCode == 200 && lastModified <= modifiedSince) {
|
||||
if (statusCode === 200 && lastModified <= modifiedSince) {
|
||||
res.writeHead(304, headers);
|
||||
res.end();
|
||||
} else if (req.method == 'GET') {
|
||||
} else if (req.method === 'GET') {
|
||||
const readStream = fs.createReadStream(pathStr);
|
||||
res.writeHead(statusCode, headers);
|
||||
readStream.pipe(res);
|
||||
|
@ -217,7 +214,7 @@ CachingMiddleware.prototype = new function () {
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.handle = handle;
|
||||
}();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* CustomError
|
||||
*
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
const Changeset = require('../../static/js/Changeset');
|
||||
const exportHtml = require('./ExportHtml');
|
||||
|
||||
|
@ -125,7 +126,7 @@ PadDiff.prototype._addAuthors = function (authors) {
|
|||
|
||||
// add to array if not in the array
|
||||
authors.forEach((author) => {
|
||||
if (self._authors.indexOf(author) == -1) {
|
||||
if (self._authors.indexOf(author) === -1) {
|
||||
self._authors.push(author);
|
||||
}
|
||||
});
|
||||
|
@ -138,7 +139,6 @@ PadDiff.prototype._createDiffAtext = async function () {
|
|||
let atext = await this._createClearStartAtext(this._fromRev);
|
||||
|
||||
let superChangeset = null;
|
||||
const rev = this._fromRev + 1;
|
||||
|
||||
for (let rev = this._fromRev + 1; rev <= this._toRev; rev += bulkSize) {
|
||||
// get the bulk
|
||||
|
@ -161,7 +161,7 @@ PadDiff.prototype._createDiffAtext = async function () {
|
|||
addedAuthors.push(authors[i]);
|
||||
|
||||
// compose it with the superChangset
|
||||
if (superChangeset === null) {
|
||||
if (superChangeset == null) {
|
||||
superChangeset = changeset;
|
||||
} else {
|
||||
superChangeset = Changeset.composeWithDeletions(superChangeset, changeset, this._pad.pool);
|
||||
|
@ -172,7 +172,8 @@ PadDiff.prototype._createDiffAtext = async function () {
|
|||
this._addAuthors(addedAuthors);
|
||||
}
|
||||
|
||||
// if there are only clearAuthorship changesets, we don't get a superChangeset, so we can skip this step
|
||||
// if there are only clearAuthorship changesets, we don't get a superChangeset,
|
||||
// so we can skip this step
|
||||
if (superChangeset) {
|
||||
const deletionChangeset = this._createDeletionChangeset(superChangeset, atext, this._pad.pool);
|
||||
|
||||
|
@ -205,7 +206,8 @@ PadDiff.prototype.getHtml = async function () {
|
|||
};
|
||||
|
||||
PadDiff.prototype.getAuthors = async function () {
|
||||
// check if html was already produced, if not produce it, this generates the author array at the same time
|
||||
// check if html was already produced, if not produce it, this generates
|
||||
// the author array at the same time
|
||||
if (this._html == null) {
|
||||
await this.getHtml();
|
||||
}
|
||||
|
@ -213,7 +215,7 @@ PadDiff.prototype.getAuthors = async function () {
|
|||
return self._authors;
|
||||
};
|
||||
|
||||
PadDiff.prototype._extendChangesetWithAuthor = function (changeset, author, apool) {
|
||||
PadDiff.prototype._extendChangesetWithAuthor = (changeset, author, apool) => {
|
||||
// unpack
|
||||
const unpacked = Changeset.unpack(changeset);
|
||||
|
||||
|
@ -245,7 +247,8 @@ PadDiff.prototype._extendChangesetWithAuthor = function (changeset, author, apoo
|
|||
return Changeset.pack(unpacked.oldLen, unpacked.newLen, assem.toString(), unpacked.charBank);
|
||||
};
|
||||
|
||||
// this method is 80% like Changeset.inverse. I just changed so instead of reverting, it adds deletions and attribute changes to to the atext.
|
||||
// this method is 80% like Changeset.inverse. I just changed so instead of reverting,
|
||||
// it adds deletions and attribute changes to to the atext.
|
||||
PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
||||
const lines = Changeset.splitTextLines(startAText.text);
|
||||
const alines = Changeset.splitAttributionLines(startAText.attribs, startAText.text);
|
||||
|
@ -254,21 +257,21 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
// They may be arrays or objects with .get(i) and .length methods.
|
||||
// They include final newlines on lines.
|
||||
|
||||
function lines_get(idx) {
|
||||
const linesGet = (idx) => {
|
||||
if (lines.get) {
|
||||
return lines.get(idx);
|
||||
} else {
|
||||
return lines[idx];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function alines_get(idx) {
|
||||
const aLinesGet = (idx) => {
|
||||
if (alines.get) {
|
||||
return alines.get(idx);
|
||||
} else {
|
||||
return alines[idx];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let curLine = 0;
|
||||
let curChar = 0;
|
||||
|
@ -280,10 +283,10 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
const csIter = Changeset.opIterator(unpacked.ops);
|
||||
const builder = Changeset.builder(unpacked.newLen);
|
||||
|
||||
function consumeAttribRuns(numChars, func /* (len, attribs, endsLine)*/) {
|
||||
if ((!curLineOpIter) || (curLineOpIterLine != curLine)) {
|
||||
const consumeAttribRuns = (numChars, func /* (len, attribs, endsLine)*/) => {
|
||||
if ((!curLineOpIter) || (curLineOpIterLine !== curLine)) {
|
||||
// create curLineOpIter and advance it to curChar
|
||||
curLineOpIter = Changeset.opIterator(alines_get(curLine));
|
||||
curLineOpIter = Changeset.opIterator(aLinesGet(curLine));
|
||||
curLineOpIterLine = curLine;
|
||||
let indexIntoLine = 0;
|
||||
let done = false;
|
||||
|
@ -304,7 +307,7 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
curChar = 0;
|
||||
curLineOpIterLine = curLine;
|
||||
curLineNextOp.chars = 0;
|
||||
curLineOpIter = Changeset.opIterator(alines_get(curLine));
|
||||
curLineOpIter = Changeset.opIterator(aLinesGet(curLine));
|
||||
}
|
||||
|
||||
if (!curLineNextOp.chars) {
|
||||
|
@ -313,7 +316,8 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
|
||||
const charsToUse = Math.min(numChars, curLineNextOp.chars);
|
||||
|
||||
func(charsToUse, curLineNextOp.attribs, charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0);
|
||||
func(charsToUse, curLineNextOp.attribs,
|
||||
charsToUse === curLineNextOp.chars && curLineNextOp.lines > 0);
|
||||
numChars -= charsToUse;
|
||||
curLineNextOp.chars -= charsToUse;
|
||||
curChar += charsToUse;
|
||||
|
@ -323,64 +327,66 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
curLine++;
|
||||
curChar = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function skip(N, L) {
|
||||
const skip = (N, L) => {
|
||||
if (L) {
|
||||
curLine += L;
|
||||
curChar = 0;
|
||||
} else if (curLineOpIter && curLineOpIterLine == curLine) {
|
||||
} else if (curLineOpIter && curLineOpIterLine === curLine) {
|
||||
consumeAttribRuns(N, () => {});
|
||||
} else {
|
||||
curChar += N;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function nextText(numChars) {
|
||||
const nextText = (numChars) => {
|
||||
let len = 0;
|
||||
const assem = Changeset.stringAssembler();
|
||||
const firstString = lines_get(curLine).substring(curChar);
|
||||
const firstString = linesGet(curLine).substring(curChar);
|
||||
len += firstString.length;
|
||||
assem.append(firstString);
|
||||
|
||||
let lineNum = curLine + 1;
|
||||
|
||||
while (len < numChars) {
|
||||
const nextString = lines_get(lineNum);
|
||||
const nextString = linesGet(lineNum);
|
||||
len += nextString.length;
|
||||
assem.append(nextString);
|
||||
lineNum++;
|
||||
}
|
||||
|
||||
return assem.toString().substring(0, numChars);
|
||||
}
|
||||
};
|
||||
|
||||
function cachedStrFunc(func) {
|
||||
const cachedStrFunc = (func) => {
|
||||
const cache = {};
|
||||
|
||||
return function (s) {
|
||||
return (s) => {
|
||||
if (!cache[s]) {
|
||||
cache[s] = func(s);
|
||||
}
|
||||
return cache[s];
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const attribKeys = [];
|
||||
const attribValues = [];
|
||||
|
||||
// iterate over all operators of this changeset
|
||||
while (csIter.hasNext()) {
|
||||
var csOp = csIter.next();
|
||||
const csOp = csIter.next();
|
||||
|
||||
if (csOp.opcode == '=') {
|
||||
var textBank = nextText(csOp.chars);
|
||||
if (csOp.opcode === '=') {
|
||||
const textBank = nextText(csOp.chars);
|
||||
|
||||
// decide if this equal operator is an attribution change or not. We can see this by checkinf if attribs is set.
|
||||
// If the text this operator applies to is only a star, than this is a false positive and should be ignored
|
||||
if (csOp.attribs && textBank != '*') {
|
||||
// decide if this equal operator is an attribution change or not.
|
||||
// We can see this by checkinf if attribs is set.
|
||||
// If the text this operator applies to is only a star,
|
||||
// than this is a false positive and should be ignored
|
||||
if (csOp.attribs && textBank !== '*') {
|
||||
const deletedAttrib = apool.putAttrib(['removed', true]);
|
||||
var authorAttrib = apool.putAttrib(['author', '']);
|
||||
let authorAttrib = apool.putAttrib(['author', '']);
|
||||
|
||||
attribKeys.length = 0;
|
||||
attribValues.length = 0;
|
||||
|
@ -393,14 +399,14 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
}
|
||||
});
|
||||
|
||||
var undoBackToAttribs = cachedStrFunc((attribs) => {
|
||||
const undoBackToAttribs = cachedStrFunc((attribs) => {
|
||||
const backAttribs = [];
|
||||
for (let i = 0; i < attribKeys.length; i++) {
|
||||
const appliedKey = attribKeys[i];
|
||||
const appliedValue = attribValues[i];
|
||||
const oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, apool);
|
||||
|
||||
if (appliedValue != oldValue) {
|
||||
if (appliedValue !== oldValue) {
|
||||
backAttribs.push([appliedKey, oldValue]);
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +414,8 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
return Changeset.makeAttribsString('=', backAttribs, apool);
|
||||
});
|
||||
|
||||
var oldAttribsAddition = `*${Changeset.numToString(deletedAttrib)}*${Changeset.numToString(authorAttrib)}`;
|
||||
const oldAttribsAddition =
|
||||
`*${Changeset.numToString(deletedAttrib)}*${Changeset.numToString(authorAttrib)}`;
|
||||
|
||||
let textLeftToProcess = textBank;
|
||||
|
||||
|
@ -427,7 +434,7 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
}
|
||||
|
||||
// get the text we want to procceed in this step
|
||||
var processText = textLeftToProcess.substr(0, lengthToProcess);
|
||||
const processText = textLeftToProcess.substr(0, lengthToProcess);
|
||||
|
||||
textLeftToProcess = textLeftToProcess.substr(lengthToProcess);
|
||||
|
||||
|
@ -437,13 +444,14 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
// consume the attributes of this linebreak
|
||||
consumeAttribRuns(1, () => {});
|
||||
} else {
|
||||
// add the old text via an insert, but add a deletion attribute + the author attribute of the author who deleted it
|
||||
var textBankIndex = 0;
|
||||
// add the old text via an insert, but add a deletion attribute +
|
||||
// the author attribute of the author who deleted it
|
||||
let textBankIndex = 0;
|
||||
consumeAttribRuns(lengthToProcess, (len, attribs, endsLine) => {
|
||||
// get the old attributes back
|
||||
var attribs = (undoBackToAttribs(attribs) || '') + oldAttribsAddition;
|
||||
const oldAttribs = (undoBackToAttribs(attribs) || '') + oldAttribsAddition;
|
||||
|
||||
builder.insert(processText.substr(textBankIndex, len), attribs);
|
||||
builder.insert(processText.substr(textBankIndex, len), oldAttribs);
|
||||
textBankIndex += len;
|
||||
});
|
||||
|
||||
|
@ -454,11 +462,11 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
|
|||
skip(csOp.chars, csOp.lines);
|
||||
builder.keep(csOp.chars, csOp.lines);
|
||||
}
|
||||
} else if (csOp.opcode == '+') {
|
||||
} else if (csOp.opcode === '+') {
|
||||
builder.keep(csOp.chars, csOp.lines);
|
||||
} else if (csOp.opcode == '-') {
|
||||
var textBank = nextText(csOp.chars);
|
||||
var textBankIndex = 0;
|
||||
} else if (csOp.opcode === '-') {
|
||||
const textBank = nextText(csOp.chars);
|
||||
let textBankIndex = 0;
|
||||
|
||||
consumeAttribRuns(csOp.chars, (len, attribs, endsLine) => {
|
||||
builder.insert(textBank.substr(textBankIndex, len), attribs + csOp.attribs);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
const fs = require('fs');
|
||||
|
||||
const check = function (path) {
|
||||
const check = (path) => {
|
||||
const existsSync = fs.statSync || fs.existsSync || path.existsSync;
|
||||
|
||||
let result;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Helpers to manipulate promises (like async but for promises).
|
||||
*/
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
'use strict';
|
||||
/**
|
||||
* Generates a random String with the given length. Is needed to generate the Author, Group, readonly, session Ids
|
||||
* Generates a random String with the given length. Is needed to generate the
|
||||
* Author, Group, readonly, session Ids
|
||||
*/
|
||||
const crypto = require('crypto');
|
||||
|
||||
const randomString = function (len) {
|
||||
return crypto.randomBytes(len).toString('hex');
|
||||
};
|
||||
const randomString = (len) => crypto.randomBytes(len).toString('hex');
|
||||
|
||||
module.exports = randomString;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
, "gritter.js"
|
||||
, "$js-cookie/src/js.cookie.js"
|
||||
, "$tinycon/tinycon.js"
|
||||
, "excanvas.js"
|
||||
, "farbtastic.js"
|
||||
, "skin_variants.js"
|
||||
, "socketio.js"
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
'use strict';
|
||||
/**
|
||||
* The Toolbar Module creates and renders the toolbars and buttons
|
||||
*/
|
||||
const _ = require('underscore');
|
||||
let tagAttributes;
|
||||
let tag;
|
||||
let Button;
|
||||
let ButtonsGroup;
|
||||
let Separator;
|
||||
let defaultButtonAttributes;
|
||||
let removeItem;
|
||||
|
||||
removeItem = function (array, what) {
|
||||
const removeItem = (array, what) => {
|
||||
let ax;
|
||||
while ((ax = array.indexOf(what)) !== -1) {
|
||||
array.splice(ax, 1);
|
||||
|
@ -18,15 +12,13 @@ removeItem = function (array, what) {
|
|||
return array;
|
||||
};
|
||||
|
||||
defaultButtonAttributes = function (name, overrides) {
|
||||
return {
|
||||
const defaultButtonAttributes = (name, overrides) => ({
|
||||
command: name,
|
||||
localizationId: `pad.toolbar.${name}.title`,
|
||||
class: `buttonicon buttonicon-${name}`,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
tag = function (name, attributes, contents) {
|
||||
const tag = (name, attributes, contents) => {
|
||||
const aStr = tagAttributes(attributes);
|
||||
|
||||
if (_.isString(contents) && contents.length > 0) {
|
||||
|
@ -36,7 +28,7 @@ tag = function (name, attributes, contents) {
|
|||
}
|
||||
};
|
||||
|
||||
tagAttributes = function (attributes) {
|
||||
const tagAttributes = (attributes) => {
|
||||
attributes = _.reduce(attributes || {}, (o, val, name) => {
|
||||
if (!_.isUndefined(val)) {
|
||||
o[name] = val;
|
||||
|
@ -47,7 +39,7 @@ tagAttributes = function (attributes) {
|
|||
return ` ${_.map(attributes, (val, name) => `${name}="${_.escape(val)}"`).join(' ')}`;
|
||||
};
|
||||
|
||||
ButtonsGroup = function () {
|
||||
const ButtonsGroup = function () {
|
||||
this.buttons = [];
|
||||
};
|
||||
|
||||
|
@ -65,9 +57,9 @@ ButtonsGroup.prototype.addButton = function (button) {
|
|||
};
|
||||
|
||||
ButtonsGroup.prototype.render = function () {
|
||||
if (this.buttons && this.buttons.length == 1) {
|
||||
if (this.buttons && this.buttons.length === 1) {
|
||||
this.buttons[0].grouping = '';
|
||||
} else {
|
||||
} else if (this.buttons && this.buttons.length > 1) {
|
||||
_.first(this.buttons).grouping = 'grouped-left';
|
||||
_.last(this.buttons).grouping = 'grouped-right';
|
||||
_.each(this.buttons.slice(1, -1), (btn) => {
|
||||
|
@ -80,11 +72,11 @@ ButtonsGroup.prototype.render = function () {
|
|||
}).join('\n');
|
||||
};
|
||||
|
||||
Button = function (attributes) {
|
||||
const Button = function (attributes) {
|
||||
this.attributes = attributes;
|
||||
};
|
||||
|
||||
Button.load = function (btnName) {
|
||||
Button.load = (btnName) => {
|
||||
const button = module.exports.availableButtons[btnName];
|
||||
try {
|
||||
if (button.constructor === Button || button.constructor === SelectButton) {
|
||||
|
@ -108,14 +100,17 @@ _.extend(Button.prototype, {
|
|||
};
|
||||
return tag('li', liAttributes,
|
||||
tag('a', {'class': this.grouping, 'data-l10n-id': this.attributes.localizationId},
|
||||
tag('button', {'class': ` ${this.attributes.class}`, 'data-l10n-id': this.attributes.localizationId})
|
||||
tag('button', {
|
||||
'class': ` ${this.attributes.class}`,
|
||||
'data-l10n-id': this.attributes.localizationId,
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
var SelectButton = function (attributes) {
|
||||
const SelectButton = function (attributes) {
|
||||
this.attributes = attributes;
|
||||
this.options = [];
|
||||
};
|
||||
|
@ -155,7 +150,7 @@ _.extend(SelectButton.prototype, Button.prototype, {
|
|||
},
|
||||
});
|
||||
|
||||
Separator = function () {};
|
||||
const Separator = function () {};
|
||||
Separator.prototype.render = function () {
|
||||
return tag('li', {class: 'separator'});
|
||||
};
|
||||
|
@ -235,15 +230,11 @@ module.exports = {
|
|||
this.availableButtons[buttonName] = buttonInfo;
|
||||
},
|
||||
|
||||
button(attributes) {
|
||||
return new Button(attributes);
|
||||
},
|
||||
separator() {
|
||||
return (new Separator()).render();
|
||||
},
|
||||
selectButton(attributes) {
|
||||
return new SelectButton(attributes);
|
||||
},
|
||||
button: (attributes) => new Button(attributes),
|
||||
|
||||
separator: () => (new Separator()).render(),
|
||||
|
||||
selectButton: (attributes) => new SelectButton(attributes),
|
||||
|
||||
/*
|
||||
* Valid values for whichMenu: 'left' | 'right' | 'timeslider-right'
|
||||
|
@ -271,7 +262,8 @@ module.exports = {
|
|||
* sufficient to visit a single read only pad to cause the disappearence
|
||||
* of the star button from all the pads.
|
||||
*/
|
||||
if ((buttons[0].indexOf('savedrevision') === -1) && (whichMenu === 'right') && (page === 'pad')) {
|
||||
if ((buttons[0].indexOf('savedrevision') === -1) &&
|
||||
(whichMenu === 'right') && (page === 'pad')) {
|
||||
buttons[0].push('savedrevision');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
"security": "1.0.0",
|
||||
"semver": "5.6.0",
|
||||
"slide": "1.1.6",
|
||||
"socket.io": "^2.3.0",
|
||||
"socket.io": "^2.4.1",
|
||||
"terser": "^4.7.0",
|
||||
"threads": "^1.4.0",
|
||||
"tiny-worker": "^2.3.0",
|
||||
|
@ -102,7 +102,6 @@
|
|||
"/static/js/admin/jquery.autosize.js",
|
||||
"/static/js/admin/minify.json.js",
|
||||
"/static/js/browser.js",
|
||||
"/static/js/excanvas.js",
|
||||
"/static/js/farbtastic.js",
|
||||
"/static/js/gritter.js",
|
||||
"/static/js/html10n.js",
|
||||
|
@ -150,7 +149,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "nyc mocha --timeout 30000 --recursive ../tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs --reporter mochawesome",
|
||||
"test": "nyc mocha --timeout 120000 --recursive ../tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs --reporter mochawesome",
|
||||
"test-container": "nyc mocha --timeout 5000 ../tests/container/specs/api --reporter mochawesome"
|
||||
},
|
||||
"version": "1.8.7",
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2006 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
document.createElement("canvas").getContext||(function(){var s=Math,j=s.round,F=s.sin,G=s.cos,V=s.abs,W=s.sqrt,k=10,v=k/2;function X(){return this.context_||(this.context_=new H(this))}var L=Array.prototype.slice;function Y(b,a){var c=L.call(arguments,2);return function(){return b.apply(a,c.concat(L.call(arguments)))}}var M={init:function(b){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var a=b||document;a.createElement("canvas");a.attachEvent("onreadystatechange",Y(this.init_,this,a))}},init_:function(b){b.namespaces.g_vml_||
|
||||
b.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");b.namespaces.g_o_||b.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");if(!b.styleSheets.ex_canvas_){var a=b.createStyleSheet();a.owningElement.id="ex_canvas_";a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}g_o_\\:*{behavior:url(#default#VML)}"}var c=b.getElementsByTagName("canvas"),d=0;for(;d<c.length;d++)this.initElement(c[d])},
|
||||
initElement:function(b){if(!b.getContext){b.getContext=X;b.innerHTML="";b.attachEvent("onpropertychange",Z);b.attachEvent("onresize",$);var a=b.attributes;if(a.width&&a.width.specified)b.style.width=a.width.nodeValue+"px";else b.width=b.clientWidth;if(a.height&&a.height.specified)b.style.height=a.height.nodeValue+"px";else b.height=b.clientHeight}return b}};function Z(b){var a=b.srcElement;switch(b.propertyName){case "width":a.style.width=a.attributes.width.nodeValue+"px";a.getContext().clearRect();
|
||||
break;case "height":a.style.height=a.attributes.height.nodeValue+"px";a.getContext().clearRect();break}}function $(b){var a=b.srcElement;if(a.firstChild){a.firstChild.style.width=a.clientWidth+"px";a.firstChild.style.height=a.clientHeight+"px"}}M.init();var N=[],B=0;for(;B<16;B++){var C=0;for(;C<16;C++)N[B*16+C]=B.toString(16)+C.toString(16)}function I(){return[[1,0,0],[0,1,0],[0,0,1]]}function y(b,a){var c=I(),d=0;for(;d<3;d++){var f=0;for(;f<3;f++){var h=0,g=0;for(;g<3;g++)h+=b[d][g]*a[g][f];c[d][f]=
|
||||
h}}return c}function O(b,a){a.fillStyle=b.fillStyle;a.lineCap=b.lineCap;a.lineJoin=b.lineJoin;a.lineWidth=b.lineWidth;a.miterLimit=b.miterLimit;a.shadowBlur=b.shadowBlur;a.shadowColor=b.shadowColor;a.shadowOffsetX=b.shadowOffsetX;a.shadowOffsetY=b.shadowOffsetY;a.strokeStyle=b.strokeStyle;a.globalAlpha=b.globalAlpha;a.arcScaleX_=b.arcScaleX_;a.arcScaleY_=b.arcScaleY_;a.lineScale_=b.lineScale_}function P(b){var a,c=1;b=String(b);if(b.substring(0,3)=="rgb"){var d=b.indexOf("(",3),f=b.indexOf(")",d+
|
||||
1),h=b.substring(d+1,f).split(",");a="#";var g=0;for(;g<3;g++)a+=N[Number(h[g])];if(h.length==4&&b.substr(3,1)=="a")c=h[3]}else a=b;return{color:a,alpha:c}}function aa(b){switch(b){case "butt":return"flat";case "round":return"round";case "square":default:return"square"}}function H(b){this.m_=I();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=k*1;this.globalAlpha=1;this.canvas=b;
|
||||
var a=b.ownerDocument.createElement("div");a.style.width=b.clientWidth+"px";a.style.height=b.clientHeight+"px";a.style.overflow="hidden";a.style.position="absolute";b.appendChild(a);this.element_=a;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}var i=H.prototype;i.clearRect=function(){this.element_.innerHTML=""};i.beginPath=function(){this.currentPath_=[]};i.moveTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};
|
||||
i.lineTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};i.bezierCurveTo=function(b,a,c,d,f,h){var g=this.getCoords_(f,h),l=this.getCoords_(b,a),e=this.getCoords_(c,d);Q(this,l,e,g)};function Q(b,a,c,d){b.currentPath_.push({type:"bezierCurveTo",cp1x:a.x,cp1y:a.y,cp2x:c.x,cp2y:c.y,x:d.x,y:d.y});b.currentX_=d.x;b.currentY_=d.y}i.quadraticCurveTo=function(b,a,c,d){var f=this.getCoords_(b,a),h=this.getCoords_(c,d),g={x:this.currentX_+
|
||||
0.6666666666666666*(f.x-this.currentX_),y:this.currentY_+0.6666666666666666*(f.y-this.currentY_)};Q(this,g,{x:g.x+(h.x-this.currentX_)/3,y:g.y+(h.y-this.currentY_)/3},h)};i.arc=function(b,a,c,d,f,h){c*=k;var g=h?"at":"wa",l=b+G(d)*c-v,e=a+F(d)*c-v,m=b+G(f)*c-v,r=a+F(f)*c-v;if(l==m&&!h)l+=0.125;var n=this.getCoords_(b,a),o=this.getCoords_(l,e),q=this.getCoords_(m,r);this.currentPath_.push({type:g,x:n.x,y:n.y,radius:c,xStart:o.x,yStart:o.y,xEnd:q.x,yEnd:q.y})};i.rect=function(b,a,c,d){this.moveTo(b,
|
||||
a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath()};i.strokeRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.stroke();this.currentPath_=f};i.fillRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.fill();this.currentPath_=f};i.createLinearGradient=function(b,
|
||||
a,c,d){var f=new D("gradient");f.x0_=b;f.y0_=a;f.x1_=c;f.y1_=d;return f};i.createRadialGradient=function(b,a,c,d,f,h){var g=new D("gradientradial");g.x0_=b;g.y0_=a;g.r0_=c;g.x1_=d;g.y1_=f;g.r1_=h;return g};i.drawImage=function(b){var a,c,d,f,h,g,l,e,m=b.runtimeStyle.width,r=b.runtimeStyle.height;b.runtimeStyle.width="auto";b.runtimeStyle.height="auto";var n=b.width,o=b.height;b.runtimeStyle.width=m;b.runtimeStyle.height=r;if(arguments.length==3){a=arguments[1];c=arguments[2];h=g=0;l=d=n;e=f=o}else if(arguments.length==
|
||||
5){a=arguments[1];c=arguments[2];d=arguments[3];f=arguments[4];h=g=0;l=n;e=o}else if(arguments.length==9){h=arguments[1];g=arguments[2];l=arguments[3];e=arguments[4];a=arguments[5];c=arguments[6];d=arguments[7];f=arguments[8]}else throw Error("Invalid number of arguments");var q=this.getCoords_(a,c),t=[];t.push(" <g_vml_:group",' coordsize="',k*10,",",k*10,'"',' coordorigin="0,0"',' style="width:',10,"px;height:",10,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]){var E=[];E.push("M11=",
|
||||
this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",j(q.x/k),",","Dy=",j(q.y/k),"");var p=q,z=this.getCoords_(a+d,c),w=this.getCoords_(a,c+f),x=this.getCoords_(a+d,c+f);p.x=s.max(p.x,z.x,w.x,x.x);p.y=s.max(p.y,z.y,w.y,x.y);t.push("padding:0 ",j(p.x/k),"px ",j(p.y/k),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",E.join(""),", sizingmethod='clip');")}else t.push("top:",j(q.y/k),"px;left:",j(q.x/k),"px;");t.push(' ">','<g_vml_:image src="',b.src,
|
||||
'"',' style="width:',k*d,"px;"," height:",k*f,'px;"',' cropleft="',h/n,'"',' croptop="',g/o,'"',' cropright="',(n-h-l)/n,'"',' cropbottom="',(o-g-e)/o,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};i.stroke=function(b){var a=[],c=P(b?this.fillStyle:this.strokeStyle),d=c.color,f=c.alpha*this.globalAlpha;a.push("<g_vml_:shape",' filled="',!!b,'"',' style="position:absolute;width:',10,"px;height:",10,'px;"',' coordorigin="0 0" coordsize="',k*10," ",k*10,'"',' stroked="',
|
||||
!b,'"',' path="');var h={x:null,y:null},g={x:null,y:null},l=0;for(;l<this.currentPath_.length;l++){var e=this.currentPath_[l];switch(e.type){case "moveTo":a.push(" m ",j(e.x),",",j(e.y));break;case "lineTo":a.push(" l ",j(e.x),",",j(e.y));break;case "close":a.push(" x ");e=null;break;case "bezierCurveTo":a.push(" c ",j(e.cp1x),",",j(e.cp1y),",",j(e.cp2x),",",j(e.cp2y),",",j(e.x),",",j(e.y));break;case "at":case "wa":a.push(" ",e.type," ",j(e.x-this.arcScaleX_*e.radius),",",j(e.y-this.arcScaleY_*e.radius),
|
||||
" ",j(e.x+this.arcScaleX_*e.radius),",",j(e.y+this.arcScaleY_*e.radius)," ",j(e.xStart),",",j(e.yStart)," ",j(e.xEnd),",",j(e.yEnd));break}if(e){if(h.x==null||e.x<h.x)h.x=e.x;if(g.x==null||e.x>g.x)g.x=e.x;if(h.y==null||e.y<h.y)h.y=e.y;if(g.y==null||e.y>g.y)g.y=e.y}}a.push(' ">');if(b)if(typeof this.fillStyle=="object"){var m=this.fillStyle,r=0,n={x:0,y:0},o=0,q=1;if(m.type_=="gradient"){var t=m.x1_/this.arcScaleX_,E=m.y1_/this.arcScaleY_,p=this.getCoords_(m.x0_/this.arcScaleX_,m.y0_/this.arcScaleY_),
|
||||
z=this.getCoords_(t,E);r=Math.atan2(z.x-p.x,z.y-p.y)*180/Math.PI;if(r<0)r+=360;if(r<1.0E-6)r=0}else{var p=this.getCoords_(m.x0_,m.y0_),w=g.x-h.x,x=g.y-h.y;n={x:(p.x-h.x)/w,y:(p.y-h.y)/x};w/=this.arcScaleX_*k;x/=this.arcScaleY_*k;var R=s.max(w,x);o=2*m.r0_/R;q=2*m.r1_/R-o}var u=m.colors_;u.sort(function(ba,ca){return ba.offset-ca.offset});var J=u.length,da=u[0].color,ea=u[J-1].color,fa=u[0].alpha*this.globalAlpha,ga=u[J-1].alpha*this.globalAlpha,S=[],l=0;for(;l<J;l++){var T=u[l];S.push(T.offset*q+
|
||||
o+" "+T.color)}a.push('<g_vml_:fill type="',m.type_,'"',' method="none" focus="100%"',' color="',da,'"',' color2="',ea,'"',' colors="',S.join(","),'"',' opacity="',ga,'"',' g_o_:opacity2="',fa,'"',' angle="',r,'"',' focusposition="',n.x,",",n.y,'" />')}else a.push('<g_vml_:fill color="',d,'" opacity="',f,'" />');else{var K=this.lineScale_*this.lineWidth;if(K<1)f*=K;a.push("<g_vml_:stroke",' opacity="',f,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',aa(this.lineCap),
|
||||
'"',' weight="',K,'px"',' color="',d,'" />')}a.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",a.join(""))};i.fill=function(){this.stroke(true)};i.closePath=function(){this.currentPath_.push({type:"close"})};i.getCoords_=function(b,a){var c=this.m_;return{x:k*(b*c[0][0]+a*c[1][0]+c[2][0])-v,y:k*(b*c[0][1]+a*c[1][1]+c[2][1])-v}};i.save=function(){var b={};O(this,b);this.aStack_.push(b);this.mStack_.push(this.m_);this.m_=y(I(),this.m_)};i.restore=function(){O(this.aStack_.pop(),
|
||||
this);this.m_=this.mStack_.pop()};function ha(b){var a=0;for(;a<3;a++){var c=0;for(;c<2;c++)if(!isFinite(b[a][c])||isNaN(b[a][c]))return false}return true}function A(b,a,c){if(!!ha(a)){b.m_=a;if(c)b.lineScale_=W(V(a[0][0]*a[1][1]-a[0][1]*a[1][0]))}}i.translate=function(b,a){A(this,y([[1,0,0],[0,1,0],[b,a,1]],this.m_),false)};i.rotate=function(b){var a=G(b),c=F(b);A(this,y([[a,c,0],[-c,a,0],[0,0,1]],this.m_),false)};i.scale=function(b,a){this.arcScaleX_*=b;this.arcScaleY_*=a;A(this,y([[b,0,0],[0,a,
|
||||
0],[0,0,1]],this.m_),true)};i.transform=function(b,a,c,d,f,h){A(this,y([[b,a,0],[c,d,0],[f,h,1]],this.m_),true)};i.setTransform=function(b,a,c,d,f,h){A(this,[[b,a,0],[c,d,0],[f,h,1]],true)};i.clip=function(){};i.arcTo=function(){};i.createPattern=function(){return new U};function D(b){this.type_=b;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}D.prototype.addColorStop=function(b,a){a=P(a);this.colors_.push({offset:b,color:a.color,alpha:a.alpha})};function U(){}G_vmlCanvasManager=
|
||||
M;CanvasRenderingContext2D=H;CanvasGradient=D;CanvasPattern=U})();
|
|
@ -1,6 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
// Farbtastic 2.0 alpha
|
||||
// Original can be found at:
|
||||
// https://github.com/mattfarina/farbtastic/blob/71ca15f4a09c8e5a08a1b0d1cf37ef028adf22f0/src/farbtastic.js
|
||||
// Licensed under the terms of the GNU General Public License v2.0:
|
||||
// https://github.com/mattfarina/farbtastic/blob/71ca15f4a09c8e5a08a1b0d1cf37ef028adf22f0/LICENSE.txt
|
||||
// edited by Sebastian Castro <sebastian.castro@protonmail.com> on 2020-04-06
|
||||
(function ($) {
|
||||
|
||||
|
@ -84,16 +86,6 @@ $._farbtastic = function (container, options) {
|
|||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//excanvas-compatible building of canvases
|
||||
fb._makeCanvas = function(className){
|
||||
var c = document.createElement('canvas');
|
||||
if (!c.getContext) { // excanvas hack
|
||||
c = window.G_vmlCanvasManager.initElement(c);
|
||||
c.getContext(); //this creates the excanvas children
|
||||
}
|
||||
$(c).addClass(className);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the color picker widget.
|
||||
|
@ -109,15 +101,27 @@ $._farbtastic = function (container, options) {
|
|||
.html(
|
||||
'<div class="farbtastic" style="position: relative">' +
|
||||
'<div class="farbtastic-solid"></div>' +
|
||||
'<canvas class="farbtastic-mask"></canvas>' +
|
||||
'<canvas class="farbtastic-overlay"></canvas>' +
|
||||
'</div>'
|
||||
)
|
||||
.children('.farbtastic')
|
||||
.append(fb._makeCanvas('farbtastic-mask'))
|
||||
.append(fb._makeCanvas('farbtastic-overlay'))
|
||||
.end()
|
||||
.find('*').attr(dim).css(dim).end()
|
||||
.find('div>*').css('position', 'absolute');
|
||||
|
||||
// IE Fix: Recreate canvas elements with doc.createElement and excanvas.
|
||||
browser.msie && $('canvas', container).each(function () {
|
||||
// Fetch info.
|
||||
var attr = { 'class': $(this).attr('class'), style: this.getAttribute('style') },
|
||||
e = document.createElement('canvas');
|
||||
// Replace element.
|
||||
$(this).before($(e).attr(attr)).remove();
|
||||
// Init with explorerCanvas.
|
||||
G_vmlCanvasManager && G_vmlCanvasManager.initElement(e);
|
||||
// Set explorerCanvas elements dimensions and absolute positioning.
|
||||
$(e).attr(dim).css(dim).css('position', 'absolute')
|
||||
.find('*').attr(dim).css(dim);
|
||||
});
|
||||
|
||||
// Determine layout
|
||||
fb.radius = (options.width - options.wheelWidth) / 2 - 1;
|
||||
fb.square = Math.floor((fb.radius - options.wheelWidth / 2) * 0.7) - 1;
|
||||
|
@ -160,12 +164,12 @@ $._farbtastic = function (container, options) {
|
|||
m.lineWidth = w / r;
|
||||
m.scale(r, r);
|
||||
// Each segment goes from angle1 to angle2.
|
||||
for (let i = 0; i <= n; ++i) {
|
||||
for (var i = 0; i <= n; ++i) {
|
||||
var d2 = i / n,
|
||||
angle2 = d2 * Math.PI * 2,
|
||||
// Endpoints
|
||||
x1 = Math.sin(angle1), y1 = -Math.cos(angle1);
|
||||
const x2 = Math.sin(angle2), y2 = -Math.cos(angle2),
|
||||
x2 = Math.sin(angle2), y2 = -Math.cos(angle2),
|
||||
// Midpoint chosen so that the endpoints are tangent to the circle.
|
||||
am = (angle1 + angle2) / 2,
|
||||
tan = 1 / Math.cos((angle2 - angle1) / 2),
|
||||
|
@ -173,6 +177,26 @@ $._farbtastic = function (container, options) {
|
|||
// New color
|
||||
color2 = fb.pack(fb.HSLToRGB([d2, 1, 0.5]));
|
||||
if (i > 0) {
|
||||
if (browser.msie) {
|
||||
// IE's gradient calculations mess up the colors. Correct along the diagonals.
|
||||
var corr = (1 + Math.min(Math.abs(Math.tan(angle1)), Math.abs(Math.tan(Math.PI / 2 - angle1)))) / n;
|
||||
color1 = fb.pack(fb.HSLToRGB([d1 - 0.15 * corr, 1, 0.5]));
|
||||
color2 = fb.pack(fb.HSLToRGB([d2 + 0.15 * corr, 1, 0.5]));
|
||||
// Create gradient fill between the endpoints.
|
||||
var grad = m.createLinearGradient(x1, y1, x2, y2);
|
||||
grad.addColorStop(0, color1);
|
||||
grad.addColorStop(1, color2);
|
||||
m.fillStyle = grad;
|
||||
// Draw quadratic curve segment as a fill.
|
||||
var r1 = (r + w / 2) / r, r2 = (r - w / 2) / r; // inner/outer radius.
|
||||
m.beginPath();
|
||||
m.moveTo(x1 * r1, y1 * r1);
|
||||
m.quadraticCurveTo(xm * r1, ym * r1, x2 * r1, y2 * r1);
|
||||
m.lineTo(x2 * r2, y2 * r2);
|
||||
m.quadraticCurveTo(xm * r2, ym * r2, x1 * r2, y1 * r2);
|
||||
m.fill();
|
||||
}
|
||||
else {
|
||||
// Create gradient fill between the endpoints.
|
||||
var grad = m.createLinearGradient(x1, y1, x2, y2);
|
||||
grad.addColorStop(0, color1);
|
||||
|
@ -184,6 +208,7 @@ $._farbtastic = function (container, options) {
|
|||
m.quadraticCurveTo(xm, ym, x2, y2);
|
||||
m.stroke();
|
||||
}
|
||||
}
|
||||
// Prevent seams where curves join.
|
||||
angle1 = angle2 - nudge; color1 = color2; d1 = d2;
|
||||
}
|
||||
|
@ -226,7 +251,7 @@ $._farbtastic = function (container, options) {
|
|||
var ctx = buffer.getContext('2d');
|
||||
var frame = ctx.getImageData(0, 0, sz + 1, sz + 1);
|
||||
|
||||
let i = 0;
|
||||
var i = 0;
|
||||
calculateMask(sz, sz, function (x, y, c, a) {
|
||||
frame.data[i++] = frame.data[i++] = frame.data[i++] = c * 255;
|
||||
frame.data[i++] = a * 255;
|
||||
|
@ -301,7 +326,7 @@ $._farbtastic = function (container, options) {
|
|||
|
||||
// Update the overlay canvas.
|
||||
fb.ctxOverlay.clearRect(-fb.mid, -fb.mid, sz, sz);
|
||||
for (let i in circles) {
|
||||
for (i in circles) {
|
||||
var c = circles[i];
|
||||
fb.ctxOverlay.lineWidth = c.lw;
|
||||
fb.ctxOverlay.strokeStyle = c.c;
|
||||
|
|
|
@ -28,7 +28,6 @@ let socket;
|
|||
// assigns to the global `$` and augments it with plugins.
|
||||
require('./jquery');
|
||||
require('./farbtastic');
|
||||
require('./excanvas');
|
||||
require('./gritter');
|
||||
|
||||
const Cookies = require('./pad_utils').Cookies;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global __dirname, __filename, afterEach, before, beforeEach, describe, it, require */
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* Import and Export tests for the /p/whateverPadId/import and /p/whateverPadId/export endpoints.
|
||||
|
@ -6,11 +6,11 @@
|
|||
|
||||
const assert = require('assert').strict;
|
||||
const common = require('../../common');
|
||||
const superagent = require(`${__dirname}/../../../../src/node_modules/superagent`);
|
||||
const superagent = require('ep_etherpad-lite/node_modules/superagent');
|
||||
const fs = require('fs');
|
||||
const settings = require(`${__dirname}/../../../../src/node/utils/Settings`);
|
||||
const padManager = require(`${__dirname}/../../../../src/node/db/PadManager`);
|
||||
const plugins = require(`${__dirname}/../../../../src/static/js/pluginfw/plugin_defs`);
|
||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||
const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs');
|
||||
|
||||
const padText = fs.readFileSync('../tests/backend/specs/api/test.txt');
|
||||
const etherpadDoc = fs.readFileSync('../tests/backend/specs/api/test.etherpad');
|
||||
|
@ -122,7 +122,7 @@ describe(__filename, function () {
|
|||
|
||||
|
||||
describe('Import/Export tests requiring AbiWord/LibreOffice', function () {
|
||||
before(function () {
|
||||
before(async function () {
|
||||
if ((!settings.abiword || settings.abiword.indexOf('/') === -1) &&
|
||||
(!settings.soffice || settings.soffice.indexOf('/') === -1)) {
|
||||
this.skip();
|
||||
|
@ -149,7 +149,8 @@ describe(__filename, function () {
|
|||
await agent.post(`/p/${testPadId}/import`)
|
||||
.attach('file', wordXDoc, {
|
||||
filename: '/test.docx',
|
||||
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
contentType:
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
})
|
||||
.expect(200)
|
||||
.expect(/FrameCall\('undefined', 'ok'\);/);
|
||||
|
@ -352,7 +353,7 @@ describe(__filename, function () {
|
|||
}); // End of tests.
|
||||
|
||||
|
||||
var endPoint = function (point, version) {
|
||||
const endPoint = (point, version) => {
|
||||
version = version || apiVersion;
|
||||
return `/api/${version}/${point}?apikey=${apiKey}`;
|
||||
};
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
const assert = require('assert');
|
||||
os = require('os'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
TidyHtml = null,
|
||||
Settings = null;
|
||||
'use strict';
|
||||
|
||||
const npm = require('../../../../src/node_modules/npm/lib/npm.js');
|
||||
const nodeify = require('../../../../src/node_modules/nodeify');
|
||||
const assert = require('assert');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
let TidyHtml;
|
||||
let Settings;
|
||||
const npm = require('ep_etherpad-lite/node_modules/npm/lib/npm.js');
|
||||
const nodeify = require('ep_etherpad-lite/node_modules/nodeify');
|
||||
|
||||
describe(__filename, function () {
|
||||
describe('tidyHtml', function () {
|
||||
before(function (done) {
|
||||
npm.load({}, (err) => {
|
||||
assert.ok(!err);
|
||||
TidyHtml = require('../../../../src/node/utils/TidyHtml');
|
||||
Settings = require('../../../../src/node/utils/Settings');
|
||||
TidyHtml = require('ep_etherpad-lite/node/utils/TidyHtml');
|
||||
Settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
function tidy(file, callback) {
|
||||
return nodeify(TidyHtml.tidy(file), callback);
|
||||
}
|
||||
const tidy = (file, callback) => nodeify(TidyHtml.tidy(file), callback);
|
||||
|
||||
it('Tidies HTML', function (done) {
|
||||
// If the user hasn't configured Tidy, we skip this tests as it's required for this test
|
||||
|
@ -53,7 +52,7 @@ describe(__filename, function () {
|
|||
'</html>',
|
||||
].join('\n');
|
||||
assert.notStrictEqual(cleanedHtml.indexOf(expectedHtml), -1);
|
||||
return done();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -65,7 +64,7 @@ describe(__filename, function () {
|
|||
|
||||
tidy('/some/none/existing/file.html', (err) => {
|
||||
assert.ok(err);
|
||||
return done();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue