|
|
@@ -24,7 +24,890 @@
|
|
|
* Source.
|
|
|
*/
|
|
|
|
|
|
- /* global globalThis, window, document, fetch, DOMParser, getComputedStyle, setTimeout, clearTimeout, NodeFilter, Readability, isProbablyReaderable, matchMedia, TextDecoder, Node, URL, MouseEvent, Blob, prompt, MutationObserver, FileReader, Worker, navigator */
|
|
|
+ /* global document, globalThis, getComputedStyle, FileReader, Image, OffscreenCanvas, createImageBitmap */
|
|
|
+
|
|
|
+ const singlefile = globalThis.singlefile;
|
|
|
+
|
|
|
+ const CLOSE_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAABhmlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AYht+mSlUqHewg4hChOogFURFHqWIRLJS2QqsOJpf+CE0akhQXR8G14ODPYtXBxVlXB1dBEPwBcXNzUnSREr9LCi1ivOO4h/e+9+XuO0Col5lqdowDqmYZqXhMzOZWxMAruhGiOYohiZl6Ir2Qgef4uoeP73dRnuVd9+foVfImA3wi8SzTDYt4nXh609I57xOHWUlSiM+Jxwy6IPEj12WX3zgXHRZ4ZtjIpOaIw8RisY3lNmYlQyWeIo4oqkb5QtZlhfMWZ7VcZc178hcG89pymuu0BhHHIhJIQoSMKjZQhoUo7RopJlJ0HvPwDzj+JLlkcm2AkWMeFaiQHD/4H/zurVmYnHCTgjGg88W2P4aBwC7QqNn297FtN04A/zNwpbX8lTow80l6raVFjoDQNnBx3dLkPeByB+h/0iVDciQ/LaFQAN7P6JtyQN8t0LPq9q15jtMHIEO9WroBDg6BkSJlr3m8u6u9b//WNPv3A6mTcr3f/E/sAAAABmJLR0QAigCKAIrj2uckAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5QkPDysvCdPVuwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAELSURBVHja7ZpLFsIwDAPj3v/OsGHDe1BIa8tKO7Mnlkw+dpoxAAAAAGCfx4ur6Yx/B337UUS4mp/VuWUEcjSfOgO+BXCZCWe0hSqQo/npBLglIUNLdAV2MH84Ad1JyIwdLkK6YoabIHWscBWmihHuAqvHtv+XqmdXOK9TxdKy3axUm2vZkXXGgPJksTuz1bVFeeU2Y6ijsLIpXbtKa1kDs2ews69o7+A+ihJ2lvI+/lcS1G21zUVG18XKNm4OS4BNkGOQQohSmGaIdpgLESvzyiRwKepsXjE2H0ZWMF8Zi4+jK5mviM0DiRXNZ2rhkdTK5jO0xermz2o8dCnq+FS2XNNVH0sDAAAA3JYnre9cH8BZmhEAAAAASUVORK5CYII=";
|
|
|
+
|
|
|
+ const SINGLE_FILE_UI_ELEMENT_CLASS = singlefile.helper.SINGLE_FILE_UI_ELEMENT_CLASS;
|
|
|
+ const SHARE_PAGE_BAR_TAGNAME = "singlefile-share-page-bar";
|
|
|
+ let EMBEDDED_IMAGE_BUTTON_MESSAGE$1, SHARE_PAGE_BUTTON_MESSAGE$1, ERROR_TITLE_MESSAGE$1;
|
|
|
+
|
|
|
+ const CSS_PROPERTIES = new Set(Array.from(getComputedStyle(document.documentElement)));
|
|
|
+
|
|
|
+ function setLabels(labels) {
|
|
|
+ ({ EMBEDDED_IMAGE_BUTTON_MESSAGE: EMBEDDED_IMAGE_BUTTON_MESSAGE$1, SHARE_PAGE_BUTTON_MESSAGE: SHARE_PAGE_BUTTON_MESSAGE$1, ERROR_TITLE_MESSAGE: ERROR_TITLE_MESSAGE$1 } = labels);
|
|
|
+ }
|
|
|
+
|
|
|
+ function getSharePageBar() {
|
|
|
+ let resolvePromise;
|
|
|
+ return {
|
|
|
+ display: async function () {
|
|
|
+ return new Promise(resolve => {
|
|
|
+ resolvePromise = resolve;
|
|
|
+ displayBar(SHARE_PAGE_BAR_TAGNAME, "", { buttonLabel: SHARE_PAGE_BUTTON_MESSAGE$1, buttonOnclick: resolve });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ hide: function () {
|
|
|
+ const barElement = document.querySelector(SHARE_PAGE_BAR_TAGNAME);
|
|
|
+ if (barElement) {
|
|
|
+ barElement.remove();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ cancel: function () {
|
|
|
+ this.hide();
|
|
|
+ if (resolvePromise) {
|
|
|
+ resolvePromise(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ function displayBar(tagName, message, { link, buttonLabel, buttonOnclick } = {}) {
|
|
|
+ try {
|
|
|
+ const barElement = document.querySelector(tagName);
|
|
|
+ if (!barElement) {
|
|
|
+ const barElement = createElement(tagName);
|
|
|
+ const shadowRoot = barElement.attachShadow({ mode: "open" });
|
|
|
+ const styleElement = document.createElement("style");
|
|
|
+ styleElement.textContent = `
|
|
|
+ .container {
|
|
|
+ background-color: #ff6c00;
|
|
|
+ color: white;
|
|
|
+ display: flex;
|
|
|
+ position: fixed;
|
|
|
+ top: 0px;
|
|
|
+ left: 0px;
|
|
|
+ right: 0px;
|
|
|
+ height: auto;
|
|
|
+ width: auto;
|
|
|
+ min-height: 24px;
|
|
|
+ min-width: 24px;
|
|
|
+ z-index: 2147483647;
|
|
|
+ margin: 0;
|
|
|
+ padding: 2px;
|
|
|
+ font-family: Arial;
|
|
|
+ }
|
|
|
+ .singlefile-open-file-bar.container, .singlefile-share-page-bar.container {
|
|
|
+ background-color: gainsboro;
|
|
|
+ border-block-end: gray 1px solid;
|
|
|
+ }
|
|
|
+ .text {
|
|
|
+ flex: 1;
|
|
|
+ padding-top: 4px;
|
|
|
+ padding-bottom: 4px;
|
|
|
+ padding-left: 8px;
|
|
|
+ }
|
|
|
+ button {
|
|
|
+ background-color: grey;
|
|
|
+ color: white;
|
|
|
+ border: 1px solid darkgrey;
|
|
|
+ padding: 3px;
|
|
|
+ padding-left: 8px;
|
|
|
+ padding-right: 8px;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ .close-button {
|
|
|
+ opacity: .7;
|
|
|
+ padding-left: 8px;
|
|
|
+ padding-right: 8px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: opacity 250ms;
|
|
|
+ height: 16px;
|
|
|
+ font-size: .8rem;
|
|
|
+ align-self: center;
|
|
|
+ }
|
|
|
+ .singlefile-open-file-bar button, .singlefile-share-page-bar button{
|
|
|
+ background-color: dimgrey;
|
|
|
+ }
|
|
|
+ .singlefile-open-file-bar .close-button, .singlefile-share-page-bar .close-button{
|
|
|
+ filter: invert(1);
|
|
|
+ }
|
|
|
+ a {
|
|
|
+ color: #303036;
|
|
|
+ }
|
|
|
+ .close-button:hover {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ `;
|
|
|
+ shadowRoot.appendChild(styleElement);
|
|
|
+ const containerElement = document.createElement("div");
|
|
|
+ containerElement.classList.add(tagName);
|
|
|
+ containerElement.classList.add("container");
|
|
|
+ const textElement = document.createElement("span");
|
|
|
+ textElement.classList.add("text");
|
|
|
+ const content = message.split("__DOC_LINK__");
|
|
|
+ textElement.textContent = content[0];
|
|
|
+ if (link && content.length == 2) {
|
|
|
+ const linkElement = document.createElement("a");
|
|
|
+ linkElement.textContent = link;
|
|
|
+ linkElement.href = link;
|
|
|
+ linkElement.target = "_blank";
|
|
|
+ textElement.appendChild(linkElement);
|
|
|
+ textElement.appendChild(document.createTextNode(content[1]));
|
|
|
+ }
|
|
|
+ if (buttonLabel && buttonOnclick) {
|
|
|
+ const buttonElement = document.createElement("button");
|
|
|
+ buttonElement.textContent = buttonLabel;
|
|
|
+ buttonElement.onclick = () => buttonOnclick();
|
|
|
+ textElement.appendChild(buttonElement);
|
|
|
+ }
|
|
|
+ containerElement.appendChild(textElement);
|
|
|
+ const closeElement = document.createElement("img");
|
|
|
+ closeElement.classList.add("close-button");
|
|
|
+ containerElement.appendChild(closeElement);
|
|
|
+ shadowRoot.appendChild(containerElement);
|
|
|
+ closeElement.src = CLOSE_ICON;
|
|
|
+ closeElement.onclick = event => {
|
|
|
+ if (event.button === 0) {
|
|
|
+ if (buttonOnclick) {
|
|
|
+ buttonOnclick(true);
|
|
|
+ }
|
|
|
+ barElement.remove();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ document.documentElement.appendChild(barElement);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ // iignored
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function createElement(tagName, parentElement) {
|
|
|
+ const element = document.createElement(tagName);
|
|
|
+ element.className = SINGLE_FILE_UI_ELEMENT_CLASS;
|
|
|
+ if (parentElement) {
|
|
|
+ parentElement.appendChild(element);
|
|
|
+ }
|
|
|
+ CSS_PROPERTIES.forEach(property => element.style.setProperty(property, "initial", "important"));
|
|
|
+ return element;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* global TextEncoder, TextDecoder */
|
|
|
+ const TYPE_REFERENCE = 0;
|
|
|
+ const SPECIAL_TYPES = [TYPE_REFERENCE];
|
|
|
+ const EMPTY_SLOT_VALUE = Symbol();
|
|
|
+
|
|
|
+ const textEncoder = new TextEncoder();
|
|
|
+ const textDecoder = new TextDecoder();
|
|
|
+ const types = new Array(256);
|
|
|
+ let typeIndex = 0;
|
|
|
+
|
|
|
+ registerType(serializeCircularReference, parseCircularReference, testCircularReference, TYPE_REFERENCE);
|
|
|
+ registerType(null, parseObject, testObject);
|
|
|
+ registerType(serializeArray, parseArray, testArray);
|
|
|
+ registerType(serializeString, parseString, testString);
|
|
|
+ registerType(serializeTypedArray, parseFloat64Array, testFloat64Array);
|
|
|
+ registerType(serializeTypedArray, parseFloat32Array, testFloat32Array);
|
|
|
+ registerType(serializeTypedArray, parseUint32Array, testUint32Array);
|
|
|
+ registerType(serializeTypedArray, parseInt32Array, testInt32Array);
|
|
|
+ registerType(serializeTypedArray, parseUint16Array, testUint16Array);
|
|
|
+ registerType(serializeTypedArray, parseInt16Array, testInt16Array);
|
|
|
+ registerType(serializeTypedArray, parseUint8ClampedArray, testUint8ClampedArray);
|
|
|
+ registerType(serializeTypedArray, parseUint8Array, testUint8Array);
|
|
|
+ registerType(serializeTypedArray, parseInt8Array, testInt8Array);
|
|
|
+ registerType(serializeArrayBuffer, parseArrayBuffer, testArrayBuffer);
|
|
|
+ registerType(serializeNumber, parseNumber, testNumber);
|
|
|
+ registerType(serializeUint32, parseUint32, testUint32);
|
|
|
+ registerType(serializeInt32, parseInt32, testInt32);
|
|
|
+ registerType(serializeUint16, parseUint16, testUint16);
|
|
|
+ registerType(serializeInt16, parseInt16, testInt16);
|
|
|
+ registerType(serializeUint8, parseUint8, testUint8);
|
|
|
+ registerType(serializeInt8, parseInt8, testInt8);
|
|
|
+ registerType(null, parseUndefined, testUndefined);
|
|
|
+ registerType(null, parseNull, testNull);
|
|
|
+ registerType(null, parseNaN, testNaN);
|
|
|
+ registerType(serializeBoolean, parseBoolean, testBoolean);
|
|
|
+ registerType(serializeSymbol, parseSymbol, testSymbol);
|
|
|
+ registerType(null, parseEmptySlot, testEmptySlot);
|
|
|
+ registerType(serializeMap, parseMap, testMap);
|
|
|
+ registerType(serializeSet, parseSet, testSet);
|
|
|
+ registerType(serializeDate, parseDate, testDate);
|
|
|
+ registerType(serializeError, parseError, testError);
|
|
|
+ registerType(serializeRegExp, parseRegExp, testRegExp);
|
|
|
+ registerType(serializeStringObject, parseStringObject, testStringObject);
|
|
|
+ registerType(serializeNumberObject, parseNumberObject, testNumberObject);
|
|
|
+ registerType(serializeBooleanObject, parseBooleanObject, testBooleanObject);
|
|
|
+
|
|
|
+ function registerType(serialize, parse, test, type) {
|
|
|
+ if (type === undefined) {
|
|
|
+ typeIndex++;
|
|
|
+ if (types.length - typeIndex >= SPECIAL_TYPES.length) {
|
|
|
+ types[types.length - typeIndex] = { serialize, parse, test };
|
|
|
+ } else {
|
|
|
+ throw new Error("Reached maximum number of custom types");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ types[type] = { serialize, parse, test };
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeValue(data, value) {
|
|
|
+ const type = types.findIndex(({ test } = {}) => test && test(value, data));
|
|
|
+ data.addObject(value);
|
|
|
+ await data.append(new Uint8Array([type]));
|
|
|
+ const serialize = types[type].serialize;
|
|
|
+ if (serialize) {
|
|
|
+ await serialize(data, value);
|
|
|
+ }
|
|
|
+ if (type != TYPE_REFERENCE && testObject(value)) {
|
|
|
+ await serializeSymbols(data, value);
|
|
|
+ await serializeOwnProperties(data, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeSymbols(data, value) {
|
|
|
+ const ownPropertySymbols = Object.getOwnPropertySymbols(value);
|
|
|
+ const symbols = ownPropertySymbols.map(propertySymbol => [propertySymbol, value[propertySymbol]]);
|
|
|
+ await serializeArray(data, symbols);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeOwnProperties(data, value) {
|
|
|
+ if (!ArrayBuffer.isView(value)) {
|
|
|
+ let entries = Object.entries(value);
|
|
|
+ if (testArray(value)) {
|
|
|
+ entries = entries.filter(([key]) => !testInteger(Number(key)));
|
|
|
+ }
|
|
|
+ await serializeValue(data, entries.length);
|
|
|
+ for (const [key, value] of entries) {
|
|
|
+ await serializeString(data, key);
|
|
|
+ await serializeValue(data, value);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ await serializeValue(data, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeCircularReference(data, value) {
|
|
|
+ const index = data.objects.indexOf(value);
|
|
|
+ await serializeValue(data, index);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeArray(data, array) {
|
|
|
+ await serializeValue(data, array.length);
|
|
|
+ const notEmptyIndexes = Object.keys(array).filter(key => testInteger(Number(key))).map(key => Number(key));
|
|
|
+ let indexNotEmptyIndexes = 0, currentNotEmptyIndex = notEmptyIndexes[indexNotEmptyIndexes];
|
|
|
+ for (const [indexArray, value] of array.entries()) {
|
|
|
+ if (currentNotEmptyIndex == indexArray) {
|
|
|
+ currentNotEmptyIndex = notEmptyIndexes[++indexNotEmptyIndexes];
|
|
|
+ await serializeValue(data, value);
|
|
|
+ } else {
|
|
|
+ await serializeValue(data, EMPTY_SLOT_VALUE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeString(data, string) {
|
|
|
+ const encodedString = textEncoder.encode(string);
|
|
|
+ await serializeValue(data, encodedString.length);
|
|
|
+ await data.append(encodedString);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeTypedArray(data, array) {
|
|
|
+ await serializeValue(data, array.length);
|
|
|
+ await data.append(array.constructor.name == "Uint8Array" ? array : new Uint8Array(array.buffer));
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeArrayBuffer(data, arrayBuffer) {
|
|
|
+ await serializeValue(data, arrayBuffer.byteLength);
|
|
|
+ await data.append(new Uint8Array(arrayBuffer));
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeNumber(data, number) {
|
|
|
+ const serializedNumber = new Uint8Array(new Float64Array([number]).buffer);
|
|
|
+ await data.append(serializedNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeUint32(data, number) {
|
|
|
+ const serializedNumber = new Uint8Array(new Uint32Array([number]).buffer);
|
|
|
+ await data.append(serializedNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeInt32(data, number) {
|
|
|
+ const serializedNumber = new Uint8Array(new Int32Array([number]).buffer);
|
|
|
+ await data.append(serializedNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeUint16(data, number) {
|
|
|
+ const serializedNumber = new Uint8Array(new Uint16Array([number]).buffer);
|
|
|
+ await data.append(serializedNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeInt16(data, number) {
|
|
|
+ const serializedNumber = new Uint8Array(new Int16Array([number]).buffer);
|
|
|
+ await data.append(serializedNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeUint8(data, number) {
|
|
|
+ const serializedNumber = new Uint8Array([number]);
|
|
|
+ await data.append(serializedNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeInt8(data, number) {
|
|
|
+ const serializedNumber = new Uint8Array(new Int8Array([number]).buffer);
|
|
|
+ await data.append(serializedNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeBoolean(data, boolean) {
|
|
|
+ const serializedBoolean = new Uint8Array([Number(boolean)]);
|
|
|
+ await data.append(serializedBoolean);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeMap(data, map) {
|
|
|
+ const entries = map.entries();
|
|
|
+ await serializeValue(data, map.size);
|
|
|
+ for (const [key, value] of entries) {
|
|
|
+ await serializeValue(data, key);
|
|
|
+ await serializeValue(data, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeSet(data, set) {
|
|
|
+ await serializeValue(data, set.size);
|
|
|
+ for (const value of set) {
|
|
|
+ await serializeValue(data, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeDate(data, date) {
|
|
|
+ await serializeNumber(data, date.getTime());
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeError(data, error) {
|
|
|
+ await serializeString(data, error.message);
|
|
|
+ await serializeString(data, error.stack);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeRegExp(data, regExp) {
|
|
|
+ await serializeString(data, regExp.source);
|
|
|
+ await serializeString(data, regExp.flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeStringObject(data, string) {
|
|
|
+ await serializeString(data, string.valueOf());
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeNumberObject(data, number) {
|
|
|
+ await serializeNumber(data, number.valueOf());
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeBooleanObject(data, boolean) {
|
|
|
+ await serializeBoolean(data, boolean.valueOf());
|
|
|
+ }
|
|
|
+
|
|
|
+ async function serializeSymbol(data, symbol) {
|
|
|
+ await serializeString(data, symbol.description);
|
|
|
+ }
|
|
|
+
|
|
|
+ class Reference {
|
|
|
+ constructor(index, data) {
|
|
|
+ this.index = index;
|
|
|
+ this.data = data;
|
|
|
+ }
|
|
|
+
|
|
|
+ getObject() {
|
|
|
+ return this.data.objects[this.index];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseValue(data) {
|
|
|
+ const array = await data.consume(1);
|
|
|
+ const parserType = array[0];
|
|
|
+ const parse = types[parserType].parse;
|
|
|
+ const valueId = data.getObjectId();
|
|
|
+ const result = await parse(data);
|
|
|
+ if (parserType != TYPE_REFERENCE && testObject(result)) {
|
|
|
+ await parseSymbols(data, result);
|
|
|
+ await parseOwnProperties(data, result);
|
|
|
+ }
|
|
|
+ data.resolveObject(valueId, result);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseSymbols(data, value) {
|
|
|
+ const symbols = await parseArray(data);
|
|
|
+ data.setObject([symbols], symbols => symbols.forEach(([symbol, propertyValue]) => value[symbol] = propertyValue));
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseOwnProperties(data, object) {
|
|
|
+ const size = await parseValue(data);
|
|
|
+ if (size) {
|
|
|
+ await parseNextProperty();
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseNextProperty(indexKey = 0) {
|
|
|
+ const key = await parseString(data);
|
|
|
+ const value = await parseValue(data);
|
|
|
+ data.setObject([value], value => object[key] = value);
|
|
|
+ if (indexKey < size - 1) {
|
|
|
+ await parseNextProperty(indexKey + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseCircularReference(data) {
|
|
|
+ const index = await parseValue(data);
|
|
|
+ const result = new Reference(index, data);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseObject() {
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseArray(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = new Array(length);
|
|
|
+ if (length) {
|
|
|
+ await parseNextSlot();
|
|
|
+ }
|
|
|
+ return array;
|
|
|
+
|
|
|
+ async function parseNextSlot(indexArray = 0) {
|
|
|
+ const value = await parseValue(data);
|
|
|
+ if (!testEmptySlot(value)) {
|
|
|
+ data.setObject([value], value => array[indexArray] = value);
|
|
|
+ }
|
|
|
+ if (indexArray < length - 1) {
|
|
|
+ await parseNextSlot(indexArray + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseEmptySlot() {
|
|
|
+ return EMPTY_SLOT_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseString(data) {
|
|
|
+ const size = await parseValue(data);
|
|
|
+ const array = await data.consume(size);
|
|
|
+ return textDecoder.decode(array);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseFloat64Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length * 8);
|
|
|
+ return new Float64Array(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseFloat32Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length * 4);
|
|
|
+ return new Float32Array(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseUint32Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length * 4);
|
|
|
+ return new Uint32Array(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseInt32Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length * 4);
|
|
|
+ return new Int32Array(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseUint16Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length * 2);
|
|
|
+ return new Uint16Array(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseInt16Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length * 2);
|
|
|
+ return new Int16Array(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseUint8ClampedArray(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length);
|
|
|
+ return new Uint8ClampedArray(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseUint8Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length);
|
|
|
+ return array;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseInt8Array(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length);
|
|
|
+ return new Int8Array(array.buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseArrayBuffer(data) {
|
|
|
+ const length = await parseValue(data);
|
|
|
+ const array = await data.consume(length);
|
|
|
+ return array.buffer;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseNumber(data) {
|
|
|
+ const array = await data.consume(8);
|
|
|
+ return new Float64Array(array.buffer)[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseUint32(data) {
|
|
|
+ const array = await data.consume(4);
|
|
|
+ return new Uint32Array(array.buffer)[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseInt32(data) {
|
|
|
+ const array = await data.consume(4);
|
|
|
+ return new Int32Array(array.buffer)[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseUint16(data) {
|
|
|
+ const array = await data.consume(2);
|
|
|
+ return new Uint16Array(array.buffer)[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseInt16(data) {
|
|
|
+ const array = await data.consume(2);
|
|
|
+ return new Int16Array(array.buffer)[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseUint8(data) {
|
|
|
+ const array = await data.consume(1);
|
|
|
+ return new Uint8Array(array.buffer)[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseInt8(data) {
|
|
|
+ const array = await data.consume(1);
|
|
|
+ return new Int8Array(array.buffer)[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseUndefined() {
|
|
|
+ return undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseNull() {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ function parseNaN() {
|
|
|
+ return NaN;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseBoolean(data) {
|
|
|
+ const array = await data.consume(1);
|
|
|
+ return Boolean(array[0]);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseMap(data) {
|
|
|
+ const size = await parseValue(data);
|
|
|
+ const map = new Map();
|
|
|
+ if (size) {
|
|
|
+ await parseNextEntry();
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+
|
|
|
+ async function parseNextEntry(indexKey = 0) {
|
|
|
+ const key = await parseValue(data);
|
|
|
+ const value = await parseValue(data);
|
|
|
+ data.setObject([key, value], (key, value) => map.set(key, value));
|
|
|
+ if (indexKey < size - 1) {
|
|
|
+ await parseNextEntry(indexKey + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseSet(data) {
|
|
|
+ const size = await parseValue(data);
|
|
|
+ const set = new Set();
|
|
|
+ if (size) {
|
|
|
+ await parseNextEntry();
|
|
|
+ }
|
|
|
+ return set;
|
|
|
+
|
|
|
+ async function parseNextEntry(indexKey = 0) {
|
|
|
+ const value = await parseValue(data);
|
|
|
+ data.setObject([value], value => set.add(value));
|
|
|
+ if (indexKey < size - 1) {
|
|
|
+ await parseNextEntry(indexKey + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseDate(data) {
|
|
|
+ const milliseconds = await parseNumber(data);
|
|
|
+ return new Date(milliseconds);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseError(data) {
|
|
|
+ const message = await parseString(data);
|
|
|
+ const stack = await parseString(data);
|
|
|
+ const error = new Error(message);
|
|
|
+ error.stack = stack;
|
|
|
+ return error;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseRegExp(data) {
|
|
|
+ const source = await parseString(data);
|
|
|
+ const flags = await parseString(data);
|
|
|
+ return new RegExp(source, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseStringObject(data) {
|
|
|
+ return new String(await parseString(data));
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseNumberObject(data) {
|
|
|
+ return new Number(await parseNumber(data));
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseBooleanObject(data) {
|
|
|
+ return new Boolean(await parseBoolean(data));
|
|
|
+ }
|
|
|
+
|
|
|
+ async function parseSymbol(data) {
|
|
|
+ const description = await parseString(data);
|
|
|
+ return Symbol(description);
|
|
|
+ }
|
|
|
+
|
|
|
+ function testCircularReference(value, data) {
|
|
|
+ return testObject(value) && data.objects.includes(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ function testObject(value) {
|
|
|
+ return value === Object(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ function testArray(value) {
|
|
|
+ return typeof value.length == "number";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testEmptySlot(value) {
|
|
|
+ return value === EMPTY_SLOT_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testString(value) {
|
|
|
+ return typeof value == "string";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testFloat64Array(value) {
|
|
|
+ return value.constructor.name == "Float64Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUint32Array(value) {
|
|
|
+ return value.constructor.name == "Uint32Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testInt32Array(value) {
|
|
|
+ return value.constructor.name == "Int32Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUint16Array(value) {
|
|
|
+ return value.constructor.name == "Uint16Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testFloat32Array(value) {
|
|
|
+ return value.constructor.name == "Float32Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testInt16Array(value) {
|
|
|
+ return value.constructor.name == "Int16Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUint8ClampedArray(value) {
|
|
|
+ return value.constructor.name == "Uint8ClampedArray";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUint8Array(value) {
|
|
|
+ return value.constructor.name == "Uint8Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testInt8Array(value) {
|
|
|
+ return value.constructor.name == "Int8Array";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testArrayBuffer(value) {
|
|
|
+ return value.constructor.name == "ArrayBuffer";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testNumber(value) {
|
|
|
+ return typeof value == "number";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUint32(value) {
|
|
|
+ return testInteger(value) && value >= 0 && value <= 4294967295;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testInt32(value) {
|
|
|
+ return testInteger(value) && value >= -2147483648 && value <= 2147483647;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUint16(value) {
|
|
|
+ return testInteger(value) && value >= 0 && value <= 65535;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testInt16(value) {
|
|
|
+ return testInteger(value) && value >= -32768 && value <= 32767;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUint8(value) {
|
|
|
+ return testInteger(value) && value >= 0 && value <= 255;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testInt8(value) {
|
|
|
+ return testInteger(value) && value >= -128 && value <= 127;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testInteger(value) {
|
|
|
+ return testNumber(value) && Number.isInteger(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ function testUndefined(value) {
|
|
|
+ return value === undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testNull(value) {
|
|
|
+ return value === null;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testNaN(value) {
|
|
|
+ return Number.isNaN(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ function testBoolean(value) {
|
|
|
+ return typeof value == "boolean";
|
|
|
+ }
|
|
|
+
|
|
|
+ function testMap(value) {
|
|
|
+ return value instanceof Map;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testSet(value) {
|
|
|
+ return value instanceof Set;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testDate(value) {
|
|
|
+ return value instanceof Date;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testError(value) {
|
|
|
+ return value instanceof Error;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testRegExp(value) {
|
|
|
+ return value instanceof RegExp;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testStringObject(value) {
|
|
|
+ return value instanceof String;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testNumberObject(value) {
|
|
|
+ return value instanceof Number;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testBooleanObject(value) {
|
|
|
+ return value instanceof Boolean;
|
|
|
+ }
|
|
|
+
|
|
|
+ function testSymbol(value) {
|
|
|
+ return typeof value == "symbol";
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Copyright 2010-2020 Gildas Lormeau
|
|
|
+ * contact : gildas.lormeau <at> gmail.com
|
|
|
+ *
|
|
|
+ * This file is part of SingleFile.
|
|
|
+ *
|
|
|
+ * The code in this file is free software: you can redistribute it and/or
|
|
|
+ * modify it under the terms of the GNU Affero General Public License
|
|
|
+ * (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
|
+ * of the License, or (at your option) any later version.
|
|
|
+ *
|
|
|
+ * The code in this file is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
|
+ * General Public License for more details.
|
|
|
+ *
|
|
|
+ * As additional permission under GNU AGPL version 3 section 7, you may
|
|
|
+ * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
|
+ * AGPL normally required by section 4, provided you include this license
|
|
|
+ * notice and a URL through which recipients can access the Corresponding
|
|
|
+ * Source.
|
|
|
+ */
|
|
|
+
|
|
|
+ let EMBEDDED_IMAGE_BUTTON_MESSAGE, SHARE_PAGE_BUTTON_MESSAGE, ERROR_TITLE_MESSAGE;
|
|
|
+
|
|
|
+ try {
|
|
|
+ EMBEDDED_IMAGE_BUTTON_MESSAGE = browser.i18n.getMessage("topPanelEmbeddedImageButton");
|
|
|
+ SHARE_PAGE_BUTTON_MESSAGE = browser.i18n.getMessage("topPanelSharePageButton");
|
|
|
+ ERROR_TITLE_MESSAGE = browser.i18n.getMessage("topPanelError");
|
|
|
+ } catch (error) {
|
|
|
+ // ignored
|
|
|
+ }
|
|
|
+
|
|
|
+ let sharePageBar;
|
|
|
+ setLabels({
|
|
|
+ EMBEDDED_IMAGE_BUTTON_MESSAGE,
|
|
|
+ SHARE_PAGE_BUTTON_MESSAGE,
|
|
|
+ ERROR_TITLE_MESSAGE
|
|
|
+ });
|
|
|
+
|
|
|
+ async function downloadPageForeground(pageData, options) {
|
|
|
+ if (options.sharePage && navigator.share) {
|
|
|
+ await sharePage(pageData);
|
|
|
+ } else {
|
|
|
+ if (pageData.filename && pageData.filename.length) {
|
|
|
+ const link = document.createElement("a");
|
|
|
+ link.download = pageData.filename;
|
|
|
+ link.href = URL.createObjectURL(new Blob([pageData.content], { type: "text/html" }));
|
|
|
+ link.dispatchEvent(new MouseEvent("click"));
|
|
|
+ return new Promise(resolve => setTimeout(() => { URL.revokeObjectURL(link.href); resolve(); }, 1000));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function sharePage(pageData) {
|
|
|
+ sharePageBar = getSharePageBar();
|
|
|
+ const cancelled = await sharePageBar.display();
|
|
|
+ if (!cancelled) {
|
|
|
+ const data = { files: [new File([pageData.content], pageData.filename)] };
|
|
|
+ try {
|
|
|
+ await navigator.share(data);
|
|
|
+ sharePageBar.hide();
|
|
|
+ } catch (error) {
|
|
|
+ sharePageBar.hide();
|
|
|
+ if (error.name === "AbortError") {
|
|
|
+ await sharePage(pageData);
|
|
|
+ } else {
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Copyright 2010-2020 Gildas Lormeau
|
|
|
+ * contact : gildas.lormeau <at> gmail.com
|
|
|
+ *
|
|
|
+ * This file is part of SingleFile.
|
|
|
+ *
|
|
|
+ * The code in this file is free software: you can redistribute it and/or
|
|
|
+ * modify it under the terms of the GNU Affero General Public License
|
|
|
+ * (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
|
+ * of the License, or (at your option) any later version.
|
|
|
+ *
|
|
|
+ * The code in this file is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
|
+ * General Public License for more details.
|
|
|
+ *
|
|
|
+ * As additional permission under GNU AGPL version 3 section 7, you may
|
|
|
+ * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
|
+ * AGPL normally required by section 4, provided you include this license
|
|
|
+ * notice and a URL through which recipients can access the Corresponding
|
|
|
+ * Source.
|
|
|
+ */
|
|
|
|
|
|
(globalThis => {
|
|
|
|
|
|
@@ -1089,6 +1972,9 @@ pre code {
|
|
|
pageOptions.visitDate = new Date(pageOptions.visitDate);
|
|
|
filename = await singlefile.helper.formatFilename(content, document, pageOptions);
|
|
|
}
|
|
|
+ if (message.sharePage) {
|
|
|
+ setLabels(message.labels);
|
|
|
+ }
|
|
|
if (pageCompressContent) {
|
|
|
const viewport = document.head.querySelector("meta[name=viewport]");
|
|
|
window.parent.postMessage(JSON.stringify({
|
|
|
@@ -1100,17 +1986,17 @@ pre code {
|
|
|
url: pageUrl,
|
|
|
viewport: viewport ? viewport.content : null,
|
|
|
compressContent: true,
|
|
|
- foregroundSave: message.foregroundSave
|
|
|
+ foregroundSave: message.foregroundSave,
|
|
|
+ sharePage: message.sharePage
|
|
|
}), "*");
|
|
|
} else {
|
|
|
- if (message.foregroundSave) {
|
|
|
- if (filename || (message.filename && message.filename.length)) {
|
|
|
- const link = document.createElement("a");
|
|
|
- link.download = filename || message.filename;
|
|
|
- link.href = URL.createObjectURL(new Blob([content], { type: "text/html" }));
|
|
|
- link.dispatchEvent(new MouseEvent("click"));
|
|
|
+ if (message.foregroundSave || message.sharePage) {
|
|
|
+ try {
|
|
|
+ await downloadPageForeground({ content, filename: filename || message.filename }, { sharePage: message.sharePage });
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error); // eslint-disable-line no-console
|
|
|
+ window.parent.postMessage(JSON.stringify({ method: "onError", error: error.message }), "*");
|
|
|
}
|
|
|
- return new Promise(resolve => setTimeout(resolve, 1));
|
|
|
} else {
|
|
|
window.parent.postMessage(JSON.stringify({
|
|
|
method: "setContent",
|
|
|
@@ -1135,10 +2021,12 @@ pre code {
|
|
|
}), "*");
|
|
|
}
|
|
|
if (message.method == "download") {
|
|
|
- const link = document.createElement("a");
|
|
|
- link.download = message.filename;
|
|
|
- link.href = URL.createObjectURL(new Blob([new Uint8Array(message.content)], { type: "text/html" }));
|
|
|
- link.dispatchEvent(new MouseEvent("click"));
|
|
|
+ try {
|
|
|
+ await downloadPageForeground({ content: message.content, filename: message.filename }, { sharePage: message.sharePage });
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error); // eslint-disable-line no-console
|
|
|
+ window.parent.postMessage(JSON.stringify({ method: "onError", error: error.message }), "*");
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
window.onresize = reflowNotes;
|