Gildas 2 лет назад
Родитель
Сommit
a77bf60c06

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
lib/single-file-extension-background.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
lib/single-file-extension-editor-helper.js


+ 901 - 13
lib/single-file-extension-editor.js

@@ -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;

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
lib/single-file-extension.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
lib/single-file-infobar.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
lib/single-file.js


+ 19 - 14
package-lock.json

@@ -10,7 +10,7 @@
 			"license": "AGPL-3.0-or-later",
 			"dependencies": {
 				"single-file-cli": "1.1.54",
-				"single-file-core": "1.3.24"
+				"single-file-core": "1.3.25"
 			},
 			"bin": {
 				"single-file": "cli/single-file"
@@ -835,9 +835,9 @@
 			}
 		},
 		"node_modules/http-proxy-agent": {
-			"version": "7.0.1",
-			"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.1.tgz",
-			"integrity": "sha512-My1KCEPs6A0hb4qCVzYp8iEvA8j8YqcvXLZZH8C9OFuTYpYjHE7N2dtG3mRl1HMD4+VGXpF3XcDVcxGBT7yDZQ==",
+			"version": "7.0.2",
+			"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+			"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
 			"dependencies": {
 				"agent-base": "^7.1.0",
 				"debug": "^4.3.4"
@@ -847,9 +847,9 @@
 			}
 		},
 		"node_modules/https-proxy-agent": {
-			"version": "7.0.3",
-			"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.3.tgz",
-			"integrity": "sha512-kCnwztfX0KZJSLOBrcL0emLeFako55NWMovvyPP2AjsghNk9RB1yjSI+jVumPHYZsNXegNoqupSW9IY3afSH8w==",
+			"version": "7.0.4",
+			"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
+			"integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
 			"dependencies": {
 				"agent-base": "^7.0.2",
 				"debug": "4"
@@ -1553,11 +1553,16 @@
 				"single-file": "single-file"
 			}
 		},
-		"node_modules/single-file-core": {
+		"node_modules/single-file-cli/node_modules/single-file-core": {
 			"version": "1.3.24",
 			"resolved": "https://registry.npmjs.org/single-file-core/-/single-file-core-1.3.24.tgz",
 			"integrity": "sha512-1B256mKBbNV8jXAV+hRyEv0aMa7tn0C0Ci+zx7Ya4ZXZB3b9/1MgKsB/fxVwDiL28WJSU0pxzh8ftIYubCNn9w=="
 		},
+		"node_modules/single-file-core": {
+			"version": "1.3.25",
+			"resolved": "https://registry.npmjs.org/single-file-core/-/single-file-core-1.3.25.tgz",
+			"integrity": "sha512-2KpexuzhNbkxgnkbSwmuKLQ3qacM51lObISAuQwN13UgNV430dS5TGQhtM98Yie9OOL2AmWFp1x4saZ8vTwlew=="
+		},
 		"node_modules/smart-buffer": {
 			"version": "4.2.0",
 			"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
@@ -1618,9 +1623,9 @@
 			"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
 		},
 		"node_modules/streamx": {
-			"version": "2.15.8",
-			"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.8.tgz",
-			"integrity": "sha512-6pwMeMY/SuISiRsuS8TeIrAzyFbG5gGPHFQsYjUr/pbBadaL1PCWmzKw+CHZSwainfvcF6Si6cVLq4XTEwswFQ==",
+			"version": "2.16.0",
+			"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.0.tgz",
+			"integrity": "sha512-a7Fi0PoUeusrUcMS4+HxivnZqYsw2MFEP841TIyLxTcEIucHcJsk+0ARcq3tGq1xDn+xK7sKHetvfMzI1/CzMA==",
 			"dependencies": {
 				"fast-fifo": "^1.1.0",
 				"queue-tick": "^1.0.1"
@@ -1727,9 +1732,9 @@
 			}
 		},
 		"node_modules/terser": {
-			"version": "5.27.1",
-			"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.1.tgz",
-			"integrity": "sha512-29wAr6UU/oQpnTw5HoadwjUZnFQXGdOfj0LjZ4sVxzqwHh/QVkvr7m8y9WoR4iN3FRitVduTc6KdjcW38Npsug==",
+			"version": "5.27.2",
+			"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.2.tgz",
+			"integrity": "sha512-sHXmLSkImesJ4p5apTeT63DsV4Obe1s37qT8qvwHRmVxKTBH7Rv9Wr26VcAMmLbmk9UliiwK8z+657NyJHHy/w==",
 			"dev": true,
 			"dependencies": {
 				"@jridgewell/source-map": "^0.3.3",

+ 1 - 1
package.json

@@ -12,7 +12,7 @@
 		"single-file": "./cli/single-file"
 	},
 	"dependencies": {
-		"single-file-core": "1.3.24",
+		"single-file-core": "1.3.25",
 		"single-file-cli": "1.1.54"
 	},
 	"devDependencies": {

Некоторые файлы не были показаны из-за большого количества измененных файлов