mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-09 17:55:37 +02:00
Enable auto-formatting of the entire code-base using Prettier (issue 11444)
Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes). Prettier is being used for a couple of reasons: - To be consistent with `mozilla-central`, where Prettier is already in use across the tree. - To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters. Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some). Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long. *Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit. (On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
This commit is contained in:
parent
8ec1dfde49
commit
de36b2aaba
205 changed files with 40024 additions and 31859 deletions
|
@ -14,15 +14,15 @@
|
|||
*/
|
||||
/* eslint no-var: error */
|
||||
|
||||
import './compatibility';
|
||||
import "./compatibility";
|
||||
|
||||
const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
||||
const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
|
||||
|
||||
const NativeImageDecoding = {
|
||||
NONE: 'none',
|
||||
DECODE: 'decode',
|
||||
DISPLAY: 'display',
|
||||
NONE: "none",
|
||||
DECODE: "decode",
|
||||
DISPLAY: "display",
|
||||
};
|
||||
|
||||
// Permission flags from Table 22, Section 7.6.3.2 of the PDF specification.
|
||||
|
@ -86,26 +86,26 @@ const AnnotationType = {
|
|||
};
|
||||
|
||||
const AnnotationStateModelType = {
|
||||
MARKED: 'Marked',
|
||||
REVIEW: 'Review',
|
||||
MARKED: "Marked",
|
||||
REVIEW: "Review",
|
||||
};
|
||||
|
||||
const AnnotationMarkedState = {
|
||||
MARKED: 'Marked',
|
||||
UNMARKED: 'Unmarked',
|
||||
MARKED: "Marked",
|
||||
UNMARKED: "Unmarked",
|
||||
};
|
||||
|
||||
const AnnotationReviewState = {
|
||||
ACCEPTED: 'Accepted',
|
||||
REJECTED: 'Rejected',
|
||||
CANCELLED: 'Cancelled',
|
||||
COMPLETED: 'Completed',
|
||||
NONE: 'None',
|
||||
ACCEPTED: "Accepted",
|
||||
REJECTED: "Rejected",
|
||||
CANCELLED: "Cancelled",
|
||||
COMPLETED: "Completed",
|
||||
NONE: "None",
|
||||
};
|
||||
|
||||
const AnnotationReplyType = {
|
||||
GROUP: 'Group',
|
||||
REPLY: 'R',
|
||||
GROUP: "Group",
|
||||
REPLY: "R",
|
||||
};
|
||||
|
||||
const AnnotationFlag = {
|
||||
|
@ -152,30 +152,30 @@ const AnnotationBorderStyleType = {
|
|||
};
|
||||
|
||||
const StreamType = {
|
||||
UNKNOWN: 'UNKNOWN',
|
||||
FLATE: 'FLATE',
|
||||
LZW: 'LZW',
|
||||
DCT: 'DCT',
|
||||
JPX: 'JPX',
|
||||
JBIG: 'JBIG',
|
||||
A85: 'A85',
|
||||
AHX: 'AHX',
|
||||
CCF: 'CCF',
|
||||
RLX: 'RLX', // PDF short name is 'RL', but telemetry requires three chars.
|
||||
UNKNOWN: "UNKNOWN",
|
||||
FLATE: "FLATE",
|
||||
LZW: "LZW",
|
||||
DCT: "DCT",
|
||||
JPX: "JPX",
|
||||
JBIG: "JBIG",
|
||||
A85: "A85",
|
||||
AHX: "AHX",
|
||||
CCF: "CCF",
|
||||
RLX: "RLX", // PDF short name is 'RL', but telemetry requires three chars.
|
||||
};
|
||||
|
||||
const FontType = {
|
||||
UNKNOWN: 'UNKNOWN',
|
||||
TYPE1: 'TYPE1',
|
||||
TYPE1C: 'TYPE1C',
|
||||
CIDFONTTYPE0: 'CIDFONTTYPE0',
|
||||
CIDFONTTYPE0C: 'CIDFONTTYPE0C',
|
||||
TRUETYPE: 'TRUETYPE',
|
||||
CIDFONTTYPE2: 'CIDFONTTYPE2',
|
||||
TYPE3: 'TYPE3',
|
||||
OPENTYPE: 'OPENTYPE',
|
||||
TYPE0: 'TYPE0',
|
||||
MMTYPE1: 'MMTYPE1',
|
||||
UNKNOWN: "UNKNOWN",
|
||||
TYPE1: "TYPE1",
|
||||
TYPE1C: "TYPE1C",
|
||||
CIDFONTTYPE0: "CIDFONTTYPE0",
|
||||
CIDFONTTYPE0C: "CIDFONTTYPE0C",
|
||||
TRUETYPE: "TRUETYPE",
|
||||
CIDFONTTYPE2: "CIDFONTTYPE2",
|
||||
TYPE3: "TYPE3",
|
||||
OPENTYPE: "OPENTYPE",
|
||||
TYPE0: "TYPE0",
|
||||
MMTYPE1: "MMTYPE1",
|
||||
};
|
||||
|
||||
const VerbosityLevel = {
|
||||
|
@ -288,12 +288,12 @@ const OPS = {
|
|||
};
|
||||
|
||||
const UNSUPPORTED_FEATURES = {
|
||||
unknown: 'unknown',
|
||||
forms: 'forms',
|
||||
javaScript: 'javaScript',
|
||||
smask: 'smask',
|
||||
shadingPattern: 'shadingPattern',
|
||||
font: 'font',
|
||||
unknown: "unknown",
|
||||
forms: "forms",
|
||||
javaScript: "javaScript",
|
||||
smask: "smask",
|
||||
shadingPattern: "shadingPattern",
|
||||
font: "font",
|
||||
};
|
||||
|
||||
const PasswordResponses = {
|
||||
|
@ -344,7 +344,7 @@ function isSameOrigin(baseUrl, otherUrl) {
|
|||
let base;
|
||||
try {
|
||||
base = new URL(baseUrl);
|
||||
if (!base.origin || base.origin === 'null') {
|
||||
if (!base.origin || base.origin === "null") {
|
||||
return false; // non-HTTP url
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -361,11 +361,11 @@ function _isValidProtocol(url) {
|
|||
return false;
|
||||
}
|
||||
switch (url.protocol) {
|
||||
case 'http:':
|
||||
case 'https:':
|
||||
case 'ftp:':
|
||||
case 'mailto:':
|
||||
case 'tel:':
|
||||
case "http:":
|
||||
case "https:":
|
||||
case "ftp:":
|
||||
case "mailto:":
|
||||
case "tel:":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -388,22 +388,26 @@ function createValidAbsoluteUrl(url, baseUrl) {
|
|||
if (_isValidProtocol(absoluteUrl)) {
|
||||
return absoluteUrl;
|
||||
}
|
||||
} catch (ex) { /* `new URL()` will throw on incorrect data. */ }
|
||||
} catch (ex) {
|
||||
/* `new URL()` will throw on incorrect data. */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function shadow(obj, prop, value) {
|
||||
Object.defineProperty(obj, prop, { value,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false, });
|
||||
Object.defineProperty(obj, prop, {
|
||||
value,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
const BaseException = (function BaseExceptionClosure() {
|
||||
function BaseException(message) {
|
||||
if (this.constructor === BaseException) {
|
||||
unreachable('Cannot initialize BaseException.');
|
||||
unreachable("Cannot initialize BaseException.");
|
||||
}
|
||||
this.message = message;
|
||||
this.name = this.constructor.name;
|
||||
|
@ -428,9 +432,9 @@ class UnknownErrorException extends BaseException {
|
|||
}
|
||||
}
|
||||
|
||||
class InvalidPDFException extends BaseException { }
|
||||
class InvalidPDFException extends BaseException {}
|
||||
|
||||
class MissingPDFException extends BaseException { }
|
||||
class MissingPDFException extends BaseException {}
|
||||
|
||||
class UnexpectedResponseException extends BaseException {
|
||||
constructor(msg, status) {
|
||||
|
@ -442,26 +446,28 @@ class UnexpectedResponseException extends BaseException {
|
|||
/**
|
||||
* Error caused during parsing PDF data.
|
||||
*/
|
||||
class FormatError extends BaseException { }
|
||||
class FormatError extends BaseException {}
|
||||
|
||||
/**
|
||||
* Error used to indicate task cancellation.
|
||||
*/
|
||||
class AbortException extends BaseException { }
|
||||
class AbortException extends BaseException {}
|
||||
|
||||
const NullCharactersRegExp = /\x00/g;
|
||||
|
||||
function removeNullCharacters(str) {
|
||||
if (typeof str !== 'string') {
|
||||
warn('The argument for removeNullCharacters must be a string.');
|
||||
if (typeof str !== "string") {
|
||||
warn("The argument for removeNullCharacters must be a string.");
|
||||
return str;
|
||||
}
|
||||
return str.replace(NullCharactersRegExp, '');
|
||||
return str.replace(NullCharactersRegExp, "");
|
||||
}
|
||||
|
||||
function bytesToString(bytes) {
|
||||
assert(bytes !== null && typeof bytes === 'object' &&
|
||||
bytes.length !== undefined, 'Invalid argument for bytesToString');
|
||||
assert(
|
||||
bytes !== null && typeof bytes === "object" && bytes.length !== undefined,
|
||||
"Invalid argument for bytesToString"
|
||||
);
|
||||
const length = bytes.length;
|
||||
const MAX_ARGUMENT_COUNT = 8192;
|
||||
if (length < MAX_ARGUMENT_COUNT) {
|
||||
|
@ -473,15 +479,15 @@ function bytesToString(bytes) {
|
|||
const chunk = bytes.subarray(i, chunkEnd);
|
||||
strBuf.push(String.fromCharCode.apply(null, chunk));
|
||||
}
|
||||
return strBuf.join('');
|
||||
return strBuf.join("");
|
||||
}
|
||||
|
||||
function stringToBytes(str) {
|
||||
assert(typeof str === 'string', 'Invalid argument for stringToBytes');
|
||||
assert(typeof str === "string", "Invalid argument for stringToBytes");
|
||||
const length = str.length;
|
||||
const bytes = new Uint8Array(length);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
bytes[i] = str.charCodeAt(i) & 0xFF;
|
||||
bytes[i] = str.charCodeAt(i) & 0xff;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
@ -507,7 +513,7 @@ function arrayByteLength(arr) {
|
|||
function arraysToBytes(arr) {
|
||||
const length = arr.length;
|
||||
// Shortcut: if first and only item is Uint8Array, return it.
|
||||
if (length === 1 && (arr[0] instanceof Uint8Array)) {
|
||||
if (length === 1 && arr[0] instanceof Uint8Array) {
|
||||
return arr[0];
|
||||
}
|
||||
let resultLength = 0;
|
||||
|
@ -519,7 +525,7 @@ function arraysToBytes(arr) {
|
|||
for (let i = 0; i < length; i++) {
|
||||
let item = arr[i];
|
||||
if (!(item instanceof Uint8Array)) {
|
||||
if (typeof item === 'string') {
|
||||
if (typeof item === "string") {
|
||||
item = stringToBytes(item);
|
||||
} else {
|
||||
item = new Uint8Array(item);
|
||||
|
@ -533,8 +539,12 @@ function arraysToBytes(arr) {
|
|||
}
|
||||
|
||||
function string32(value) {
|
||||
return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
|
||||
(value >> 8) & 0xff, value & 0xff);
|
||||
return String.fromCharCode(
|
||||
(value >> 24) & 0xff,
|
||||
(value >> 16) & 0xff,
|
||||
(value >> 8) & 0xff,
|
||||
value & 0xff
|
||||
);
|
||||
}
|
||||
|
||||
// Calculate the base 2 logarithm of the number `x`. This differs from the
|
||||
|
@ -556,8 +566,13 @@ function readUint16(data, offset) {
|
|||
}
|
||||
|
||||
function readUint32(data, offset) {
|
||||
return ((data[offset] << 24) | (data[offset + 1] << 16) |
|
||||
(data[offset + 2] << 8) | data[offset + 3]) >>> 0;
|
||||
return (
|
||||
((data[offset] << 24) |
|
||||
(data[offset + 1] << 16) |
|
||||
(data[offset + 2] << 8) |
|
||||
data[offset + 3]) >>>
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// Lazy test the endianness of the platform
|
||||
|
@ -566,20 +581,20 @@ function isLittleEndian() {
|
|||
const buffer8 = new Uint8Array(4);
|
||||
buffer8[0] = 1;
|
||||
const view32 = new Uint32Array(buffer8.buffer, 0, 1);
|
||||
return (view32[0] === 1);
|
||||
return view32[0] === 1;
|
||||
}
|
||||
|
||||
// Checks if it's possible to eval JS expressions.
|
||||
function isEvalSupported() {
|
||||
try {
|
||||
new Function(''); // eslint-disable-line no-new, no-new-func
|
||||
new Function(""); // eslint-disable-line no-new, no-new-func
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')'];
|
||||
const rgbBuf = ["rgb(", 0, ",", 0, ",", 0, ")"];
|
||||
|
||||
class Util {
|
||||
// makeCssRgb() can be called thousands of times. Using ´rgbBuf` avoids
|
||||
|
@ -588,7 +603,7 @@ class Util {
|
|||
rgbBuf[1] = r;
|
||||
rgbBuf[3] = g;
|
||||
rgbBuf[5] = b;
|
||||
return rgbBuf.join('');
|
||||
return rgbBuf.join("");
|
||||
}
|
||||
|
||||
// Concatenates two transformation matrices together and returns the result.
|
||||
|
@ -599,7 +614,7 @@ class Util {
|
|||
m1[0] * m2[2] + m1[2] * m2[3],
|
||||
m1[1] * m2[2] + m1[3] * m2[3],
|
||||
m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
|
||||
m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
|
||||
m1[1] * m2[4] + m1[3] * m2[5] + m1[5],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -628,14 +643,20 @@ class Util {
|
|||
Math.min(p1[0], p2[0], p3[0], p4[0]),
|
||||
Math.min(p1[1], p2[1], p3[1], p4[1]),
|
||||
Math.max(p1[0], p2[0], p3[0], p4[0]),
|
||||
Math.max(p1[1], p2[1], p3[1], p4[1])
|
||||
Math.max(p1[1], p2[1], p3[1], p4[1]),
|
||||
];
|
||||
}
|
||||
|
||||
static inverseTransform(m) {
|
||||
const d = m[0] * m[3] - m[1] * m[2];
|
||||
return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
|
||||
(m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
|
||||
return [
|
||||
m[3] / d,
|
||||
-m[1] / d,
|
||||
-m[2] / d,
|
||||
m[0] / d,
|
||||
(m[2] * m[5] - m[4] * m[3]) / d,
|
||||
(m[4] * m[1] - m[5] * m[0]) / d,
|
||||
];
|
||||
}
|
||||
|
||||
// Apply a generic 3d matrix M on a 3-vector v:
|
||||
|
@ -648,7 +669,7 @@ class Util {
|
|||
return [
|
||||
m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
|
||||
m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
|
||||
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
|
||||
m[6] * v[0] + m[7] * v[1] + m[8] * v[2],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -708,8 +729,10 @@ class Util {
|
|||
rect2 = Util.normalizeRect(rect2);
|
||||
|
||||
// X: first and second points belong to different rectangles?
|
||||
if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
|
||||
(orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
|
||||
if (
|
||||
(orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
|
||||
(orderedX[0] === rect2[0] && orderedX[1] === rect1[0])
|
||||
) {
|
||||
// Intersection must be between second and third points
|
||||
result[0] = orderedX[1];
|
||||
result[2] = orderedX[2];
|
||||
|
@ -718,8 +741,10 @@ class Util {
|
|||
}
|
||||
|
||||
// Y: first and second points belong to different rectangles?
|
||||
if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
|
||||
(orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
|
||||
if (
|
||||
(orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
|
||||
(orderedY[0] === rect2[1] && orderedY[1] === rect1[1])
|
||||
) {
|
||||
// Intersection must be between second and third points
|
||||
result[1] = orderedY[1];
|
||||
result[3] = orderedY[2];
|
||||
|
@ -745,18 +770,21 @@ const PDFStringTranslateTable = [
|
|||
];
|
||||
|
||||
function stringToPDFString(str) {
|
||||
const length = str.length, strBuf = [];
|
||||
if (str[0] === '\xFE' && str[1] === '\xFF') {
|
||||
const length = str.length,
|
||||
strBuf = [];
|
||||
if (str[0] === "\xFE" && str[1] === "\xFF") {
|
||||
// UTF16BE BOM
|
||||
for (let i = 2; i < length; i += 2) {
|
||||
strBuf.push(String.fromCharCode(
|
||||
(str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
|
||||
strBuf.push(
|
||||
String.fromCharCode((str.charCodeAt(i) << 8) | str.charCodeAt(i + 1))
|
||||
);
|
||||
}
|
||||
} else if (str[0] === '\xFF' && str[1] === '\xFE') {
|
||||
} else if (str[0] === "\xFF" && str[1] === "\xFE") {
|
||||
// UTF16LE BOM
|
||||
for (let i = 2; i < length; i += 2) {
|
||||
strBuf.push(String.fromCharCode(
|
||||
(str.charCodeAt(i + 1) << 8) | str.charCodeAt(i)));
|
||||
strBuf.push(
|
||||
String.fromCharCode((str.charCodeAt(i + 1) << 8) | str.charCodeAt(i))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
|
@ -764,7 +792,7 @@ function stringToPDFString(str) {
|
|||
strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
|
||||
}
|
||||
}
|
||||
return strBuf.join('');
|
||||
return strBuf.join("");
|
||||
}
|
||||
|
||||
function stringToUTF8String(str) {
|
||||
|
@ -783,19 +811,19 @@ function isEmptyObj(obj) {
|
|||
}
|
||||
|
||||
function isBool(v) {
|
||||
return typeof v === 'boolean';
|
||||
return typeof v === "boolean";
|
||||
}
|
||||
|
||||
function isNum(v) {
|
||||
return typeof v === 'number';
|
||||
return typeof v === "number";
|
||||
}
|
||||
|
||||
function isString(v) {
|
||||
return typeof v === 'string';
|
||||
return typeof v === "string";
|
||||
}
|
||||
|
||||
function isArrayBuffer(v) {
|
||||
return typeof v === 'object' && v !== null && v.byteLength !== undefined;
|
||||
return typeof v === "object" && v !== null && v.byteLength !== undefined;
|
||||
}
|
||||
|
||||
function isArrayEqual(arr1, arr2) {
|
||||
|
@ -809,7 +837,7 @@ function isArrayEqual(arr1, arr2) {
|
|||
|
||||
// Checks if ch is one of the following characters: SPACE, TAB, CR or LF.
|
||||
function isSpace(ch) {
|
||||
return (ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A);
|
||||
return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -832,7 +860,7 @@ function createPromiseCapability() {
|
|||
const capability = Object.create(null);
|
||||
let isSettled = false;
|
||||
|
||||
Object.defineProperty(capability, 'settled', {
|
||||
Object.defineProperty(capability, "settled", {
|
||||
get() {
|
||||
return isSettled;
|
||||
},
|
||||
|
@ -853,22 +881,23 @@ function createPromiseCapability() {
|
|||
const createObjectURL = (function createObjectURLClosure() {
|
||||
// Blob/createObjectURL is not available, falling back to data schema.
|
||||
const digits =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
return function createObjectURL(data, contentType, forceDataSchema = false) {
|
||||
if (!forceDataSchema && URL.createObjectURL) {
|
||||
const blob = new Blob([data], { type: contentType, });
|
||||
const blob = new Blob([data], { type: contentType });
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
let buffer = `data:${contentType};base64,`;
|
||||
for (let i = 0, ii = data.length; i < ii; i += 3) {
|
||||
const b1 = data[i] & 0xFF;
|
||||
const b2 = data[i + 1] & 0xFF;
|
||||
const b3 = data[i + 2] & 0xFF;
|
||||
const d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
|
||||
const d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
|
||||
const d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
|
||||
const b1 = data[i] & 0xff;
|
||||
const b2 = data[i + 1] & 0xff;
|
||||
const b3 = data[i + 2] & 0xff;
|
||||
const d1 = b1 >> 2,
|
||||
d2 = ((b1 & 3) << 4) | (b2 >> 4);
|
||||
const d3 = i + 1 < ii ? ((b2 & 0xf) << 2) | (b3 >> 6) : 64;
|
||||
const d4 = i + 2 < ii ? b3 & 0x3f : 64;
|
||||
buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
|
||||
}
|
||||
return buffer;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue