mirror of
https://github.com/TrudeEH/web.git
synced 2025-12-06 08:23:37 +00:00
21405 lines
705 KiB
JavaScript
21405 lines
705 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
|
|
if you want to view the source visit the plugins github repository
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var obsidian = require('obsidian');
|
|
var path = require('path');
|
|
var fs$2 = require('fs');
|
|
var url = require('url');
|
|
|
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
|
|
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs$2);
|
|
|
|
/******************************************************************************
|
|
Copyright (c) Microsoft Corporation.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
PERFORMANCE OF THIS SOFTWARE.
|
|
***************************************************************************** */
|
|
|
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
});
|
|
}
|
|
|
|
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
var e = new Error(message);
|
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
};
|
|
|
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
|
|
function getDefaultExportFromCjs (x) {
|
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
}
|
|
|
|
function getAugmentedNamespace(n) {
|
|
if (n.__esModule) return n;
|
|
var f = n.default;
|
|
if (typeof f == "function") {
|
|
var a = function a () {
|
|
if (this instanceof a) {
|
|
var args = [null];
|
|
args.push.apply(args, arguments);
|
|
var Ctor = Function.bind.apply(f, args);
|
|
return new Ctor();
|
|
}
|
|
return f.apply(this, arguments);
|
|
};
|
|
a.prototype = f.prototype;
|
|
} else a = {};
|
|
Object.defineProperty(a, '__esModule', {value: true});
|
|
Object.keys(n).forEach(function (k) {
|
|
var d = Object.getOwnPropertyDescriptor(n, k);
|
|
Object.defineProperty(a, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function () {
|
|
return n[k];
|
|
}
|
|
});
|
|
});
|
|
return a;
|
|
}
|
|
|
|
var lib$3 = {};
|
|
|
|
var FsPromise = {};
|
|
|
|
/**
|
|
* Module convert fs functions to promise based functions
|
|
*/
|
|
Object.defineProperty(FsPromise, "__esModule", { value: true });
|
|
FsPromise.readFile = FsPromise.writeFileSync = FsPromise.writeFile = FsPromise.read = FsPromise.open = FsPromise.close = FsPromise.stat = FsPromise.createReadStream = FsPromise.pathExists = void 0;
|
|
const fs$1 = fs__default["default"];
|
|
FsPromise.pathExists = fs$1.existsSync;
|
|
FsPromise.createReadStream = fs$1.createReadStream;
|
|
async function stat(path) {
|
|
return new Promise((resolve, reject) => {
|
|
fs$1.stat(path, (err, stats) => {
|
|
if (err)
|
|
reject(err);
|
|
else
|
|
resolve(stats);
|
|
});
|
|
});
|
|
}
|
|
FsPromise.stat = stat;
|
|
async function close(fd) {
|
|
return new Promise((resolve, reject) => {
|
|
fs$1.close(fd, err => {
|
|
if (err)
|
|
reject(err);
|
|
else
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
FsPromise.close = close;
|
|
async function open(path, mode) {
|
|
return new Promise((resolve, reject) => {
|
|
fs$1.open(path, mode, (err, fd) => {
|
|
if (err)
|
|
reject(err);
|
|
else
|
|
resolve(fd);
|
|
});
|
|
});
|
|
}
|
|
FsPromise.open = open;
|
|
async function read(fd, buffer, offset, length, position) {
|
|
return new Promise((resolve, reject) => {
|
|
fs$1.read(fd, buffer, offset, length, position, (err, bytesRead, _buffer) => {
|
|
if (err)
|
|
reject(err);
|
|
else
|
|
resolve({ bytesRead, buffer: _buffer });
|
|
});
|
|
});
|
|
}
|
|
FsPromise.read = read;
|
|
async function writeFile(path, data) {
|
|
return new Promise((resolve, reject) => {
|
|
fs$1.writeFile(path, data, err => {
|
|
if (err)
|
|
reject(err);
|
|
else
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
FsPromise.writeFile = writeFile;
|
|
function writeFileSync(path, data) {
|
|
fs$1.writeFileSync(path, data);
|
|
}
|
|
FsPromise.writeFileSync = writeFileSync;
|
|
async function readFile(path) {
|
|
return new Promise((resolve, reject) => {
|
|
fs$1.readFile(path, (err, buffer) => {
|
|
if (err)
|
|
reject(err);
|
|
else
|
|
resolve(buffer);
|
|
});
|
|
});
|
|
}
|
|
FsPromise.readFile = readFile;
|
|
|
|
var core$3 = {};
|
|
|
|
var ReadStreamTokenizer$1 = {};
|
|
|
|
var AbstractTokenizer$1 = {};
|
|
|
|
var lib$2 = {};
|
|
|
|
var EndOfFileStream = {};
|
|
|
|
(function (exports) {
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.EndOfStreamError = exports.defaultMessages = void 0;
|
|
exports.defaultMessages = 'End-Of-Stream';
|
|
/**
|
|
* Thrown on read operation of the end of file or stream has been reached
|
|
*/
|
|
class EndOfStreamError extends Error {
|
|
constructor() {
|
|
super(exports.defaultMessages);
|
|
}
|
|
}
|
|
exports.EndOfStreamError = EndOfStreamError;
|
|
} (EndOfFileStream));
|
|
|
|
var StreamReader = {};
|
|
|
|
var Deferred$1 = {};
|
|
|
|
Object.defineProperty(Deferred$1, "__esModule", { value: true });
|
|
Deferred$1.Deferred = void 0;
|
|
class Deferred {
|
|
constructor() {
|
|
this.resolve = () => null;
|
|
this.reject = () => null;
|
|
this.promise = new Promise((resolve, reject) => {
|
|
this.reject = reject;
|
|
this.resolve = resolve;
|
|
});
|
|
}
|
|
}
|
|
Deferred$1.Deferred = Deferred;
|
|
|
|
(function (exports) {
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.StreamReader = exports.EndOfStreamError = void 0;
|
|
const EndOfFileStream_1 = EndOfFileStream;
|
|
const Deferred_1 = Deferred$1;
|
|
var EndOfFileStream_2 = EndOfFileStream;
|
|
Object.defineProperty(exports, "EndOfStreamError", { enumerable: true, get: function () { return EndOfFileStream_2.EndOfStreamError; } });
|
|
const maxStreamReadSize = 1 * 1024 * 1024; // Maximum request length on read-stream operation
|
|
class StreamReader {
|
|
constructor(s) {
|
|
this.s = s;
|
|
/**
|
|
* Deferred used for postponed read request (as not data is yet available to read)
|
|
*/
|
|
this.deferred = null;
|
|
this.endOfStream = false;
|
|
/**
|
|
* Store peeked data
|
|
* @type {Array}
|
|
*/
|
|
this.peekQueue = [];
|
|
if (!s.read || !s.once) {
|
|
throw new Error('Expected an instance of stream.Readable');
|
|
}
|
|
this.s.once('end', () => this.reject(new EndOfFileStream_1.EndOfStreamError()));
|
|
this.s.once('error', err => this.reject(err));
|
|
this.s.once('close', () => this.reject(new Error('Stream closed')));
|
|
}
|
|
/**
|
|
* Read ahead (peek) from stream. Subsequent read or peeks will return the same data
|
|
* @param uint8Array - Uint8Array (or Buffer) to store data read from stream in
|
|
* @param offset - Offset target
|
|
* @param length - Number of bytes to read
|
|
* @returns Number of bytes peeked
|
|
*/
|
|
async peek(uint8Array, offset, length) {
|
|
const bytesRead = await this.read(uint8Array, offset, length);
|
|
this.peekQueue.push(uint8Array.subarray(offset, offset + bytesRead)); // Put read data back to peek buffer
|
|
return bytesRead;
|
|
}
|
|
/**
|
|
* Read chunk from stream
|
|
* @param buffer - Target Uint8Array (or Buffer) to store data read from stream in
|
|
* @param offset - Offset target
|
|
* @param length - Number of bytes to read
|
|
* @returns Number of bytes read
|
|
*/
|
|
async read(buffer, offset, length) {
|
|
if (length === 0) {
|
|
return 0;
|
|
}
|
|
if (this.peekQueue.length === 0 && this.endOfStream) {
|
|
throw new EndOfFileStream_1.EndOfStreamError();
|
|
}
|
|
let remaining = length;
|
|
let bytesRead = 0;
|
|
// consume peeked data first
|
|
while (this.peekQueue.length > 0 && remaining > 0) {
|
|
const peekData = this.peekQueue.pop(); // Front of queue
|
|
if (!peekData)
|
|
throw new Error('peekData should be defined');
|
|
const lenCopy = Math.min(peekData.length, remaining);
|
|
buffer.set(peekData.subarray(0, lenCopy), offset + bytesRead);
|
|
bytesRead += lenCopy;
|
|
remaining -= lenCopy;
|
|
if (lenCopy < peekData.length) {
|
|
// remainder back to queue
|
|
this.peekQueue.push(peekData.subarray(lenCopy));
|
|
}
|
|
}
|
|
// continue reading from stream if required
|
|
while (remaining > 0 && !this.endOfStream) {
|
|
const reqLen = Math.min(remaining, maxStreamReadSize);
|
|
const chunkLen = await this.readFromStream(buffer, offset + bytesRead, reqLen);
|
|
bytesRead += chunkLen;
|
|
if (chunkLen < reqLen)
|
|
break;
|
|
remaining -= chunkLen;
|
|
}
|
|
return bytesRead;
|
|
}
|
|
/**
|
|
* Read chunk from stream
|
|
* @param buffer Target Uint8Array (or Buffer) to store data read from stream in
|
|
* @param offset Offset target
|
|
* @param length Number of bytes to read
|
|
* @returns Number of bytes read
|
|
*/
|
|
async readFromStream(buffer, offset, length) {
|
|
const readBuffer = this.s.read(length);
|
|
if (readBuffer) {
|
|
buffer.set(readBuffer, offset);
|
|
return readBuffer.length;
|
|
}
|
|
else {
|
|
const request = {
|
|
buffer,
|
|
offset,
|
|
length,
|
|
deferred: new Deferred_1.Deferred()
|
|
};
|
|
this.deferred = request.deferred;
|
|
this.s.once('readable', () => {
|
|
this.readDeferred(request);
|
|
});
|
|
return request.deferred.promise;
|
|
}
|
|
}
|
|
/**
|
|
* Process deferred read request
|
|
* @param request Deferred read request
|
|
*/
|
|
readDeferred(request) {
|
|
const readBuffer = this.s.read(request.length);
|
|
if (readBuffer) {
|
|
request.buffer.set(readBuffer, request.offset);
|
|
request.deferred.resolve(readBuffer.length);
|
|
this.deferred = null;
|
|
}
|
|
else {
|
|
this.s.once('readable', () => {
|
|
this.readDeferred(request);
|
|
});
|
|
}
|
|
}
|
|
reject(err) {
|
|
this.endOfStream = true;
|
|
if (this.deferred) {
|
|
this.deferred.reject(err);
|
|
this.deferred = null;
|
|
}
|
|
}
|
|
}
|
|
exports.StreamReader = StreamReader;
|
|
} (StreamReader));
|
|
|
|
(function (exports) {
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.StreamReader = exports.EndOfStreamError = void 0;
|
|
var EndOfFileStream_1 = EndOfFileStream;
|
|
Object.defineProperty(exports, "EndOfStreamError", { enumerable: true, get: function () { return EndOfFileStream_1.EndOfStreamError; } });
|
|
var StreamReader_1 = StreamReader;
|
|
Object.defineProperty(exports, "StreamReader", { enumerable: true, get: function () { return StreamReader_1.StreamReader; } });
|
|
} (lib$2));
|
|
|
|
Object.defineProperty(AbstractTokenizer$1, "__esModule", { value: true });
|
|
AbstractTokenizer$1.AbstractTokenizer = void 0;
|
|
const peek_readable_1$3 = lib$2;
|
|
/**
|
|
* Core tokenizer
|
|
*/
|
|
class AbstractTokenizer {
|
|
constructor(fileInfo) {
|
|
/**
|
|
* Tokenizer-stream position
|
|
*/
|
|
this.position = 0;
|
|
this.numBuffer = new Uint8Array(8);
|
|
this.fileInfo = fileInfo ? fileInfo : {};
|
|
}
|
|
/**
|
|
* Read a token from the tokenizer-stream
|
|
* @param token - The token to read
|
|
* @param position - If provided, the desired position in the tokenizer-stream
|
|
* @returns Promise with token data
|
|
*/
|
|
async readToken(token, position = this.position) {
|
|
const uint8Array = Buffer.alloc(token.len);
|
|
const len = await this.readBuffer(uint8Array, { position });
|
|
if (len < token.len)
|
|
throw new peek_readable_1$3.EndOfStreamError();
|
|
return token.get(uint8Array, 0);
|
|
}
|
|
/**
|
|
* Peek a token from the tokenizer-stream.
|
|
* @param token - Token to peek from the tokenizer-stream.
|
|
* @param position - Offset where to begin reading within the file. If position is null, data will be read from the current file position.
|
|
* @returns Promise with token data
|
|
*/
|
|
async peekToken(token, position = this.position) {
|
|
const uint8Array = Buffer.alloc(token.len);
|
|
const len = await this.peekBuffer(uint8Array, { position });
|
|
if (len < token.len)
|
|
throw new peek_readable_1$3.EndOfStreamError();
|
|
return token.get(uint8Array, 0);
|
|
}
|
|
/**
|
|
* Read a numeric token from the stream
|
|
* @param token - Numeric token
|
|
* @returns Promise with number
|
|
*/
|
|
async readNumber(token) {
|
|
const len = await this.readBuffer(this.numBuffer, { length: token.len });
|
|
if (len < token.len)
|
|
throw new peek_readable_1$3.EndOfStreamError();
|
|
return token.get(this.numBuffer, 0);
|
|
}
|
|
/**
|
|
* Read a numeric token from the stream
|
|
* @param token - Numeric token
|
|
* @returns Promise with number
|
|
*/
|
|
async peekNumber(token) {
|
|
const len = await this.peekBuffer(this.numBuffer, { length: token.len });
|
|
if (len < token.len)
|
|
throw new peek_readable_1$3.EndOfStreamError();
|
|
return token.get(this.numBuffer, 0);
|
|
}
|
|
/**
|
|
* Ignore number of bytes, advances the pointer in under tokenizer-stream.
|
|
* @param length - Number of bytes to ignore
|
|
* @return resolves the number of bytes ignored, equals length if this available, otherwise the number of bytes available
|
|
*/
|
|
async ignore(length) {
|
|
if (this.fileInfo.size !== undefined) {
|
|
const bytesLeft = this.fileInfo.size - this.position;
|
|
if (length > bytesLeft) {
|
|
this.position += bytesLeft;
|
|
return bytesLeft;
|
|
}
|
|
}
|
|
this.position += length;
|
|
return length;
|
|
}
|
|
async close() {
|
|
// empty
|
|
}
|
|
normalizeOptions(uint8Array, options) {
|
|
if (options && options.position !== undefined && options.position < this.position) {
|
|
throw new Error('`options.position` must be equal or greater than `tokenizer.position`');
|
|
}
|
|
if (options) {
|
|
return {
|
|
mayBeLess: options.mayBeLess === true,
|
|
offset: options.offset ? options.offset : 0,
|
|
length: options.length ? options.length : (uint8Array.length - (options.offset ? options.offset : 0)),
|
|
position: options.position ? options.position : this.position
|
|
};
|
|
}
|
|
return {
|
|
mayBeLess: false,
|
|
offset: 0,
|
|
length: uint8Array.length,
|
|
position: this.position
|
|
};
|
|
}
|
|
}
|
|
AbstractTokenizer$1.AbstractTokenizer = AbstractTokenizer;
|
|
|
|
Object.defineProperty(ReadStreamTokenizer$1, "__esModule", { value: true });
|
|
ReadStreamTokenizer$1.ReadStreamTokenizer = void 0;
|
|
const AbstractTokenizer_1$2 = AbstractTokenizer$1;
|
|
const peek_readable_1$2 = lib$2;
|
|
const maxBufferSize = 256000;
|
|
class ReadStreamTokenizer extends AbstractTokenizer_1$2.AbstractTokenizer {
|
|
constructor(stream, fileInfo) {
|
|
super(fileInfo);
|
|
this.streamReader = new peek_readable_1$2.StreamReader(stream);
|
|
}
|
|
/**
|
|
* Get file information, an HTTP-client may implement this doing a HEAD request
|
|
* @return Promise with file information
|
|
*/
|
|
async getFileInfo() {
|
|
return this.fileInfo;
|
|
}
|
|
/**
|
|
* Read buffer from tokenizer
|
|
* @param uint8Array - Target Uint8Array to fill with data read from the tokenizer-stream
|
|
* @param options - Read behaviour options
|
|
* @returns Promise with number of bytes read
|
|
*/
|
|
async readBuffer(uint8Array, options) {
|
|
const normOptions = this.normalizeOptions(uint8Array, options);
|
|
const skipBytes = normOptions.position - this.position;
|
|
if (skipBytes > 0) {
|
|
await this.ignore(skipBytes);
|
|
return this.readBuffer(uint8Array, options);
|
|
}
|
|
else if (skipBytes < 0) {
|
|
throw new Error('`options.position` must be equal or greater than `tokenizer.position`');
|
|
}
|
|
if (normOptions.length === 0) {
|
|
return 0;
|
|
}
|
|
const bytesRead = await this.streamReader.read(uint8Array, normOptions.offset, normOptions.length);
|
|
this.position += bytesRead;
|
|
if ((!options || !options.mayBeLess) && bytesRead < normOptions.length) {
|
|
throw new peek_readable_1$2.EndOfStreamError();
|
|
}
|
|
return bytesRead;
|
|
}
|
|
/**
|
|
* Peek (read ahead) buffer from tokenizer
|
|
* @param uint8Array - Uint8Array (or Buffer) to write data to
|
|
* @param options - Read behaviour options
|
|
* @returns Promise with number of bytes peeked
|
|
*/
|
|
async peekBuffer(uint8Array, options) {
|
|
const normOptions = this.normalizeOptions(uint8Array, options);
|
|
let bytesRead = 0;
|
|
if (normOptions.position) {
|
|
const skipBytes = normOptions.position - this.position;
|
|
if (skipBytes > 0) {
|
|
const skipBuffer = new Uint8Array(normOptions.length + skipBytes);
|
|
bytesRead = await this.peekBuffer(skipBuffer, { mayBeLess: normOptions.mayBeLess });
|
|
uint8Array.set(skipBuffer.subarray(skipBytes), normOptions.offset);
|
|
return bytesRead - skipBytes;
|
|
}
|
|
else if (skipBytes < 0) {
|
|
throw new Error('Cannot peek from a negative offset in a stream');
|
|
}
|
|
}
|
|
if (normOptions.length > 0) {
|
|
try {
|
|
bytesRead = await this.streamReader.peek(uint8Array, normOptions.offset, normOptions.length);
|
|
}
|
|
catch (err) {
|
|
if (options && options.mayBeLess && err instanceof peek_readable_1$2.EndOfStreamError) {
|
|
return 0;
|
|
}
|
|
throw err;
|
|
}
|
|
if ((!normOptions.mayBeLess) && bytesRead < normOptions.length) {
|
|
throw new peek_readable_1$2.EndOfStreamError();
|
|
}
|
|
}
|
|
return bytesRead;
|
|
}
|
|
async ignore(length) {
|
|
// debug(`ignore ${this.position}...${this.position + length - 1}`);
|
|
const bufSize = Math.min(maxBufferSize, length);
|
|
const buf = new Uint8Array(bufSize);
|
|
let totBytesRead = 0;
|
|
while (totBytesRead < length) {
|
|
const remaining = length - totBytesRead;
|
|
const bytesRead = await this.readBuffer(buf, { length: Math.min(bufSize, remaining) });
|
|
if (bytesRead < 0) {
|
|
return bytesRead;
|
|
}
|
|
totBytesRead += bytesRead;
|
|
}
|
|
return totBytesRead;
|
|
}
|
|
}
|
|
ReadStreamTokenizer$1.ReadStreamTokenizer = ReadStreamTokenizer;
|
|
|
|
var BufferTokenizer$1 = {};
|
|
|
|
Object.defineProperty(BufferTokenizer$1, "__esModule", { value: true });
|
|
BufferTokenizer$1.BufferTokenizer = void 0;
|
|
const peek_readable_1$1 = lib$2;
|
|
const AbstractTokenizer_1$1 = AbstractTokenizer$1;
|
|
class BufferTokenizer extends AbstractTokenizer_1$1.AbstractTokenizer {
|
|
/**
|
|
* Construct BufferTokenizer
|
|
* @param uint8Array - Uint8Array to tokenize
|
|
* @param fileInfo - Pass additional file information to the tokenizer
|
|
*/
|
|
constructor(uint8Array, fileInfo) {
|
|
super(fileInfo);
|
|
this.uint8Array = uint8Array;
|
|
this.fileInfo.size = this.fileInfo.size ? this.fileInfo.size : uint8Array.length;
|
|
}
|
|
/**
|
|
* Read buffer from tokenizer
|
|
* @param uint8Array - Uint8Array to tokenize
|
|
* @param options - Read behaviour options
|
|
* @returns {Promise<number>}
|
|
*/
|
|
async readBuffer(uint8Array, options) {
|
|
if (options && options.position) {
|
|
if (options.position < this.position) {
|
|
throw new Error('`options.position` must be equal or greater than `tokenizer.position`');
|
|
}
|
|
this.position = options.position;
|
|
}
|
|
const bytesRead = await this.peekBuffer(uint8Array, options);
|
|
this.position += bytesRead;
|
|
return bytesRead;
|
|
}
|
|
/**
|
|
* Peek (read ahead) buffer from tokenizer
|
|
* @param uint8Array
|
|
* @param options - Read behaviour options
|
|
* @returns {Promise<number>}
|
|
*/
|
|
async peekBuffer(uint8Array, options) {
|
|
const normOptions = this.normalizeOptions(uint8Array, options);
|
|
const bytes2read = Math.min(this.uint8Array.length - normOptions.position, normOptions.length);
|
|
if ((!normOptions.mayBeLess) && bytes2read < normOptions.length) {
|
|
throw new peek_readable_1$1.EndOfStreamError();
|
|
}
|
|
else {
|
|
uint8Array.set(this.uint8Array.subarray(normOptions.position, normOptions.position + bytes2read), normOptions.offset);
|
|
return bytes2read;
|
|
}
|
|
}
|
|
async close() {
|
|
// empty
|
|
}
|
|
}
|
|
BufferTokenizer$1.BufferTokenizer = BufferTokenizer;
|
|
|
|
(function (exports) {
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.fromBuffer = exports.fromStream = exports.EndOfStreamError = void 0;
|
|
const ReadStreamTokenizer_1 = ReadStreamTokenizer$1;
|
|
const BufferTokenizer_1 = BufferTokenizer$1;
|
|
var peek_readable_1 = lib$2;
|
|
Object.defineProperty(exports, "EndOfStreamError", { enumerable: true, get: function () { return peek_readable_1.EndOfStreamError; } });
|
|
/**
|
|
* Construct ReadStreamTokenizer from given Stream.
|
|
* Will set fileSize, if provided given Stream has set the .path property/
|
|
* @param stream - Read from Node.js Stream.Readable
|
|
* @param fileInfo - Pass the file information, like size and MIME-type of the corresponding stream.
|
|
* @returns ReadStreamTokenizer
|
|
*/
|
|
function fromStream(stream, fileInfo) {
|
|
fileInfo = fileInfo ? fileInfo : {};
|
|
return new ReadStreamTokenizer_1.ReadStreamTokenizer(stream, fileInfo);
|
|
}
|
|
exports.fromStream = fromStream;
|
|
/**
|
|
* Construct ReadStreamTokenizer from given Buffer.
|
|
* @param uint8Array - Uint8Array to tokenize
|
|
* @param fileInfo - Pass additional file information to the tokenizer
|
|
* @returns BufferTokenizer
|
|
*/
|
|
function fromBuffer(uint8Array, fileInfo) {
|
|
return new BufferTokenizer_1.BufferTokenizer(uint8Array, fileInfo);
|
|
}
|
|
exports.fromBuffer = fromBuffer;
|
|
} (core$3));
|
|
|
|
var FileTokenizer$1 = {};
|
|
|
|
Object.defineProperty(FileTokenizer$1, "__esModule", { value: true });
|
|
FileTokenizer$1.fromFile = FileTokenizer$1.FileTokenizer = void 0;
|
|
const AbstractTokenizer_1 = AbstractTokenizer$1;
|
|
const peek_readable_1 = lib$2;
|
|
const fs = FsPromise;
|
|
class FileTokenizer extends AbstractTokenizer_1.AbstractTokenizer {
|
|
constructor(fd, fileInfo) {
|
|
super(fileInfo);
|
|
this.fd = fd;
|
|
}
|
|
/**
|
|
* Read buffer from file
|
|
* @param uint8Array - Uint8Array to write result to
|
|
* @param options - Read behaviour options
|
|
* @returns Promise number of bytes read
|
|
*/
|
|
async readBuffer(uint8Array, options) {
|
|
const normOptions = this.normalizeOptions(uint8Array, options);
|
|
this.position = normOptions.position;
|
|
const res = await fs.read(this.fd, uint8Array, normOptions.offset, normOptions.length, normOptions.position);
|
|
this.position += res.bytesRead;
|
|
if (res.bytesRead < normOptions.length && (!options || !options.mayBeLess)) {
|
|
throw new peek_readable_1.EndOfStreamError();
|
|
}
|
|
return res.bytesRead;
|
|
}
|
|
/**
|
|
* Peek buffer from file
|
|
* @param uint8Array - Uint8Array (or Buffer) to write data to
|
|
* @param options - Read behaviour options
|
|
* @returns Promise number of bytes read
|
|
*/
|
|
async peekBuffer(uint8Array, options) {
|
|
const normOptions = this.normalizeOptions(uint8Array, options);
|
|
const res = await fs.read(this.fd, uint8Array, normOptions.offset, normOptions.length, normOptions.position);
|
|
if ((!normOptions.mayBeLess) && res.bytesRead < normOptions.length) {
|
|
throw new peek_readable_1.EndOfStreamError();
|
|
}
|
|
return res.bytesRead;
|
|
}
|
|
async close() {
|
|
return fs.close(this.fd);
|
|
}
|
|
}
|
|
FileTokenizer$1.FileTokenizer = FileTokenizer;
|
|
async function fromFile$1(sourceFilePath) {
|
|
const stat = await fs.stat(sourceFilePath);
|
|
if (!stat.isFile) {
|
|
throw new Error(`File not a file: ${sourceFilePath}`);
|
|
}
|
|
const fd = await fs.open(sourceFilePath, 'r');
|
|
return new FileTokenizer(fd, { path: sourceFilePath, size: stat.size });
|
|
}
|
|
FileTokenizer$1.fromFile = fromFile$1;
|
|
|
|
(function (exports) {
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.fromStream = exports.fromBuffer = exports.EndOfStreamError = exports.fromFile = void 0;
|
|
const fs = FsPromise;
|
|
const core = core$3;
|
|
var FileTokenizer_1 = FileTokenizer$1;
|
|
Object.defineProperty(exports, "fromFile", { enumerable: true, get: function () { return FileTokenizer_1.fromFile; } });
|
|
var core_1 = core$3;
|
|
Object.defineProperty(exports, "EndOfStreamError", { enumerable: true, get: function () { return core_1.EndOfStreamError; } });
|
|
Object.defineProperty(exports, "fromBuffer", { enumerable: true, get: function () { return core_1.fromBuffer; } });
|
|
/**
|
|
* Construct ReadStreamTokenizer from given Stream.
|
|
* Will set fileSize, if provided given Stream has set the .path property.
|
|
* @param stream - Node.js Stream.Readable
|
|
* @param fileInfo - Pass additional file information to the tokenizer
|
|
* @returns Tokenizer
|
|
*/
|
|
async function fromStream(stream, fileInfo) {
|
|
fileInfo = fileInfo ? fileInfo : {};
|
|
if (stream.path) {
|
|
const stat = await fs.stat(stream.path);
|
|
fileInfo.path = stream.path;
|
|
fileInfo.size = stat.size;
|
|
}
|
|
return core.fromStream(stream, fileInfo);
|
|
}
|
|
exports.fromStream = fromStream;
|
|
} (lib$3));
|
|
|
|
var lib$1 = {};
|
|
|
|
var ieee754 = {};
|
|
|
|
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
|
|
ieee754.read = function (buffer, offset, isLE, mLen, nBytes) {
|
|
var e, m;
|
|
var eLen = (nBytes * 8) - mLen - 1;
|
|
var eMax = (1 << eLen) - 1;
|
|
var eBias = eMax >> 1;
|
|
var nBits = -7;
|
|
var i = isLE ? (nBytes - 1) : 0;
|
|
var d = isLE ? -1 : 1;
|
|
var s = buffer[offset + i];
|
|
|
|
i += d;
|
|
|
|
e = s & ((1 << (-nBits)) - 1);
|
|
s >>= (-nBits);
|
|
nBits += eLen;
|
|
for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
|
|
|
|
m = e & ((1 << (-nBits)) - 1);
|
|
e >>= (-nBits);
|
|
nBits += mLen;
|
|
for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
|
|
|
|
if (e === 0) {
|
|
e = 1 - eBias;
|
|
} else if (e === eMax) {
|
|
return m ? NaN : ((s ? -1 : 1) * Infinity)
|
|
} else {
|
|
m = m + Math.pow(2, mLen);
|
|
e = e - eBias;
|
|
}
|
|
return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
|
|
};
|
|
|
|
ieee754.write = function (buffer, value, offset, isLE, mLen, nBytes) {
|
|
var e, m, c;
|
|
var eLen = (nBytes * 8) - mLen - 1;
|
|
var eMax = (1 << eLen) - 1;
|
|
var eBias = eMax >> 1;
|
|
var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
|
|
var i = isLE ? 0 : (nBytes - 1);
|
|
var d = isLE ? 1 : -1;
|
|
var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
|
|
|
|
value = Math.abs(value);
|
|
|
|
if (isNaN(value) || value === Infinity) {
|
|
m = isNaN(value) ? 1 : 0;
|
|
e = eMax;
|
|
} else {
|
|
e = Math.floor(Math.log(value) / Math.LN2);
|
|
if (value * (c = Math.pow(2, -e)) < 1) {
|
|
e--;
|
|
c *= 2;
|
|
}
|
|
if (e + eBias >= 1) {
|
|
value += rt / c;
|
|
} else {
|
|
value += rt * Math.pow(2, 1 - eBias);
|
|
}
|
|
if (value * c >= 2) {
|
|
e++;
|
|
c /= 2;
|
|
}
|
|
|
|
if (e + eBias >= eMax) {
|
|
m = 0;
|
|
e = eMax;
|
|
} else if (e + eBias >= 1) {
|
|
m = ((value * c) - 1) * Math.pow(2, mLen);
|
|
e = e + eBias;
|
|
} else {
|
|
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
|
|
e = 0;
|
|
}
|
|
}
|
|
|
|
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
|
|
|
|
e = (e << mLen) | m;
|
|
eLen += mLen;
|
|
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
|
|
|
|
buffer[offset + i - d] |= s * 128;
|
|
};
|
|
|
|
(function (exports) {
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.AnsiStringType = exports.StringType = exports.BufferType = exports.Uint8ArrayType = exports.IgnoreType = exports.Float80_LE = exports.Float80_BE = exports.Float64_LE = exports.Float64_BE = exports.Float32_LE = exports.Float32_BE = exports.Float16_LE = exports.Float16_BE = exports.INT64_BE = exports.UINT64_BE = exports.INT64_LE = exports.UINT64_LE = exports.INT32_LE = exports.INT32_BE = exports.INT24_BE = exports.INT24_LE = exports.INT16_LE = exports.INT16_BE = exports.INT8 = exports.UINT32_BE = exports.UINT32_LE = exports.UINT24_BE = exports.UINT24_LE = exports.UINT16_BE = exports.UINT16_LE = exports.UINT8 = void 0;
|
|
const ieee754$1 = ieee754;
|
|
// Primitive types
|
|
function dv(array) {
|
|
return new DataView(array.buffer, array.byteOffset);
|
|
}
|
|
/**
|
|
* 8-bit unsigned integer
|
|
*/
|
|
exports.UINT8 = {
|
|
len: 1,
|
|
get(array, offset) {
|
|
return dv(array).getUint8(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setUint8(offset, value);
|
|
return offset + 1;
|
|
}
|
|
};
|
|
/**
|
|
* 16-bit unsigned integer, Little Endian byte order
|
|
*/
|
|
exports.UINT16_LE = {
|
|
len: 2,
|
|
get(array, offset) {
|
|
return dv(array).getUint16(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setUint16(offset, value, true);
|
|
return offset + 2;
|
|
}
|
|
};
|
|
/**
|
|
* 16-bit unsigned integer, Big Endian byte order
|
|
*/
|
|
exports.UINT16_BE = {
|
|
len: 2,
|
|
get(array, offset) {
|
|
return dv(array).getUint16(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setUint16(offset, value);
|
|
return offset + 2;
|
|
}
|
|
};
|
|
/**
|
|
* 24-bit unsigned integer, Little Endian byte order
|
|
*/
|
|
exports.UINT24_LE = {
|
|
len: 3,
|
|
get(array, offset) {
|
|
const dataView = dv(array);
|
|
return dataView.getUint8(offset) + (dataView.getUint16(offset + 1, true) << 8);
|
|
},
|
|
put(array, offset, value) {
|
|
const dataView = dv(array);
|
|
dataView.setUint8(offset, value & 0xff);
|
|
dataView.setUint16(offset + 1, value >> 8, true);
|
|
return offset + 3;
|
|
}
|
|
};
|
|
/**
|
|
* 24-bit unsigned integer, Big Endian byte order
|
|
*/
|
|
exports.UINT24_BE = {
|
|
len: 3,
|
|
get(array, offset) {
|
|
const dataView = dv(array);
|
|
return (dataView.getUint16(offset) << 8) + dataView.getUint8(offset + 2);
|
|
},
|
|
put(array, offset, value) {
|
|
const dataView = dv(array);
|
|
dataView.setUint16(offset, value >> 8);
|
|
dataView.setUint8(offset + 2, value & 0xff);
|
|
return offset + 3;
|
|
}
|
|
};
|
|
/**
|
|
* 32-bit unsigned integer, Little Endian byte order
|
|
*/
|
|
exports.UINT32_LE = {
|
|
len: 4,
|
|
get(array, offset) {
|
|
return dv(array).getUint32(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setUint32(offset, value, true);
|
|
return offset + 4;
|
|
}
|
|
};
|
|
/**
|
|
* 32-bit unsigned integer, Big Endian byte order
|
|
*/
|
|
exports.UINT32_BE = {
|
|
len: 4,
|
|
get(array, offset) {
|
|
return dv(array).getUint32(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setUint32(offset, value);
|
|
return offset + 4;
|
|
}
|
|
};
|
|
/**
|
|
* 8-bit signed integer
|
|
*/
|
|
exports.INT8 = {
|
|
len: 1,
|
|
get(array, offset) {
|
|
return dv(array).getInt8(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setInt8(offset, value);
|
|
return offset + 1;
|
|
}
|
|
};
|
|
/**
|
|
* 16-bit signed integer, Big Endian byte order
|
|
*/
|
|
exports.INT16_BE = {
|
|
len: 2,
|
|
get(array, offset) {
|
|
return dv(array).getInt16(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setInt16(offset, value);
|
|
return offset + 2;
|
|
}
|
|
};
|
|
/**
|
|
* 16-bit signed integer, Little Endian byte order
|
|
*/
|
|
exports.INT16_LE = {
|
|
len: 2,
|
|
get(array, offset) {
|
|
return dv(array).getInt16(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setInt16(offset, value, true);
|
|
return offset + 2;
|
|
}
|
|
};
|
|
/**
|
|
* 24-bit signed integer, Little Endian byte order
|
|
*/
|
|
exports.INT24_LE = {
|
|
len: 3,
|
|
get(array, offset) {
|
|
const unsigned = exports.UINT24_LE.get(array, offset);
|
|
return unsigned > 0x7fffff ? unsigned - 0x1000000 : unsigned;
|
|
},
|
|
put(array, offset, value) {
|
|
const dataView = dv(array);
|
|
dataView.setUint8(offset, value & 0xff);
|
|
dataView.setUint16(offset + 1, value >> 8, true);
|
|
return offset + 3;
|
|
}
|
|
};
|
|
/**
|
|
* 24-bit signed integer, Big Endian byte order
|
|
*/
|
|
exports.INT24_BE = {
|
|
len: 3,
|
|
get(array, offset) {
|
|
const unsigned = exports.UINT24_BE.get(array, offset);
|
|
return unsigned > 0x7fffff ? unsigned - 0x1000000 : unsigned;
|
|
},
|
|
put(array, offset, value) {
|
|
const dataView = dv(array);
|
|
dataView.setUint16(offset, value >> 8);
|
|
dataView.setUint8(offset + 2, value & 0xff);
|
|
return offset + 3;
|
|
}
|
|
};
|
|
/**
|
|
* 32-bit signed integer, Big Endian byte order
|
|
*/
|
|
exports.INT32_BE = {
|
|
len: 4,
|
|
get(array, offset) {
|
|
return dv(array).getInt32(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setInt32(offset, value);
|
|
return offset + 4;
|
|
}
|
|
};
|
|
/**
|
|
* 32-bit signed integer, Big Endian byte order
|
|
*/
|
|
exports.INT32_LE = {
|
|
len: 4,
|
|
get(array, offset) {
|
|
return dv(array).getInt32(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setInt32(offset, value, true);
|
|
return offset + 4;
|
|
}
|
|
};
|
|
/**
|
|
* 64-bit unsigned integer, Little Endian byte order
|
|
*/
|
|
exports.UINT64_LE = {
|
|
len: 8,
|
|
get(array, offset) {
|
|
return dv(array).getBigUint64(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setBigUint64(offset, value, true);
|
|
return offset + 8;
|
|
}
|
|
};
|
|
/**
|
|
* 64-bit signed integer, Little Endian byte order
|
|
*/
|
|
exports.INT64_LE = {
|
|
len: 8,
|
|
get(array, offset) {
|
|
return dv(array).getBigInt64(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setBigInt64(offset, value, true);
|
|
return offset + 8;
|
|
}
|
|
};
|
|
/**
|
|
* 64-bit unsigned integer, Big Endian byte order
|
|
*/
|
|
exports.UINT64_BE = {
|
|
len: 8,
|
|
get(array, offset) {
|
|
return dv(array).getBigUint64(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setBigUint64(offset, value);
|
|
return offset + 8;
|
|
}
|
|
};
|
|
/**
|
|
* 64-bit signed integer, Big Endian byte order
|
|
*/
|
|
exports.INT64_BE = {
|
|
len: 8,
|
|
get(array, offset) {
|
|
return dv(array).getBigInt64(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setBigInt64(offset, value);
|
|
return offset + 8;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 16-bit (half precision) float, big endian
|
|
*/
|
|
exports.Float16_BE = {
|
|
len: 2,
|
|
get(dataView, offset) {
|
|
return ieee754$1.read(dataView, offset, false, 10, this.len);
|
|
},
|
|
put(dataView, offset, value) {
|
|
ieee754$1.write(dataView, value, offset, false, 10, this.len);
|
|
return offset + this.len;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 16-bit (half precision) float, little endian
|
|
*/
|
|
exports.Float16_LE = {
|
|
len: 2,
|
|
get(array, offset) {
|
|
return ieee754$1.read(array, offset, true, 10, this.len);
|
|
},
|
|
put(array, offset, value) {
|
|
ieee754$1.write(array, value, offset, true, 10, this.len);
|
|
return offset + this.len;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 32-bit (single precision) float, big endian
|
|
*/
|
|
exports.Float32_BE = {
|
|
len: 4,
|
|
get(array, offset) {
|
|
return dv(array).getFloat32(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setFloat32(offset, value);
|
|
return offset + 4;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 32-bit (single precision) float, little endian
|
|
*/
|
|
exports.Float32_LE = {
|
|
len: 4,
|
|
get(array, offset) {
|
|
return dv(array).getFloat32(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setFloat32(offset, value, true);
|
|
return offset + 4;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 64-bit (double precision) float, big endian
|
|
*/
|
|
exports.Float64_BE = {
|
|
len: 8,
|
|
get(array, offset) {
|
|
return dv(array).getFloat64(offset);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setFloat64(offset, value);
|
|
return offset + 8;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 64-bit (double precision) float, little endian
|
|
*/
|
|
exports.Float64_LE = {
|
|
len: 8,
|
|
get(array, offset) {
|
|
return dv(array).getFloat64(offset, true);
|
|
},
|
|
put(array, offset, value) {
|
|
dv(array).setFloat64(offset, value, true);
|
|
return offset + 8;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 80-bit (extended precision) float, big endian
|
|
*/
|
|
exports.Float80_BE = {
|
|
len: 10,
|
|
get(array, offset) {
|
|
return ieee754$1.read(array, offset, false, 63, this.len);
|
|
},
|
|
put(array, offset, value) {
|
|
ieee754$1.write(array, value, offset, false, 63, this.len);
|
|
return offset + this.len;
|
|
}
|
|
};
|
|
/**
|
|
* IEEE 754 80-bit (extended precision) float, little endian
|
|
*/
|
|
exports.Float80_LE = {
|
|
len: 10,
|
|
get(array, offset) {
|
|
return ieee754$1.read(array, offset, true, 63, this.len);
|
|
},
|
|
put(array, offset, value) {
|
|
ieee754$1.write(array, value, offset, true, 63, this.len);
|
|
return offset + this.len;
|
|
}
|
|
};
|
|
/**
|
|
* Ignore a given number of bytes
|
|
*/
|
|
class IgnoreType {
|
|
/**
|
|
* @param len number of bytes to ignore
|
|
*/
|
|
constructor(len) {
|
|
this.len = len;
|
|
}
|
|
// ToDo: don't read, but skip data
|
|
get(array, off) {
|
|
}
|
|
}
|
|
exports.IgnoreType = IgnoreType;
|
|
class Uint8ArrayType {
|
|
constructor(len) {
|
|
this.len = len;
|
|
}
|
|
get(array, offset) {
|
|
return array.subarray(offset, offset + this.len);
|
|
}
|
|
}
|
|
exports.Uint8ArrayType = Uint8ArrayType;
|
|
class BufferType {
|
|
constructor(len) {
|
|
this.len = len;
|
|
}
|
|
get(uint8Array, off) {
|
|
return Buffer.from(uint8Array.subarray(off, off + this.len));
|
|
}
|
|
}
|
|
exports.BufferType = BufferType;
|
|
/**
|
|
* Consume a fixed number of bytes from the stream and return a string with a specified encoding.
|
|
*/
|
|
class StringType {
|
|
constructor(len, encoding) {
|
|
this.len = len;
|
|
this.encoding = encoding;
|
|
}
|
|
get(uint8Array, offset) {
|
|
return Buffer.from(uint8Array).toString(this.encoding, offset, offset + this.len);
|
|
}
|
|
}
|
|
exports.StringType = StringType;
|
|
/**
|
|
* ANSI Latin 1 String
|
|
* Using windows-1252 / ISO 8859-1 decoding
|
|
*/
|
|
class AnsiStringType {
|
|
constructor(len) {
|
|
this.len = len;
|
|
}
|
|
static decode(buffer, offset, until) {
|
|
let str = '';
|
|
for (let i = offset; i < until; ++i) {
|
|
str += AnsiStringType.codePointToString(AnsiStringType.singleByteDecoder(buffer[i]));
|
|
}
|
|
return str;
|
|
}
|
|
static inRange(a, min, max) {
|
|
return min <= a && a <= max;
|
|
}
|
|
static codePointToString(cp) {
|
|
if (cp <= 0xFFFF) {
|
|
return String.fromCharCode(cp);
|
|
}
|
|
else {
|
|
cp -= 0x10000;
|
|
return String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
|
|
}
|
|
}
|
|
static singleByteDecoder(bite) {
|
|
if (AnsiStringType.inRange(bite, 0x00, 0x7F)) {
|
|
return bite;
|
|
}
|
|
const codePoint = AnsiStringType.windows1252[bite - 0x80];
|
|
if (codePoint === null) {
|
|
throw Error('invaliding encoding');
|
|
}
|
|
return codePoint;
|
|
}
|
|
get(buffer, offset = 0) {
|
|
return AnsiStringType.decode(buffer, offset, offset + this.len);
|
|
}
|
|
}
|
|
exports.AnsiStringType = AnsiStringType;
|
|
AnsiStringType.windows1252 = [8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352,
|
|
8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732,
|
|
8482, 353, 8250, 339, 157, 382, 376, 160, 161, 162, 163, 164, 165, 166, 167, 168,
|
|
169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
|
|
185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
|
|
201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216,
|
|
217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
|
|
233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
|
|
248, 249, 250, 251, 252, 253, 254, 255];
|
|
} (lib$1));
|
|
|
|
var util$4 = {};
|
|
|
|
util$4.stringToBytes = string => [...string].map(character => character.charCodeAt(0));
|
|
|
|
/**
|
|
Checks whether the TAR checksum is valid.
|
|
|
|
@param {Buffer} buffer - The TAR header `[offset ... offset + 512]`.
|
|
@param {number} offset - TAR header offset.
|
|
@returns {boolean} `true` if the TAR checksum is valid, otherwise `false`.
|
|
*/
|
|
util$4.tarHeaderChecksumMatches = (buffer, offset = 0) => {
|
|
const readSum = parseInt(buffer.toString('utf8', 148, 154).replace(/\0.*$/, '').trim(), 8); // Read sum in header
|
|
if (isNaN(readSum)) {
|
|
return false;
|
|
}
|
|
|
|
let sum = 8 * 0x20; // Initialize signed bit sum
|
|
|
|
for (let i = offset; i < offset + 148; i++) {
|
|
sum += buffer[i];
|
|
}
|
|
|
|
for (let i = offset + 156; i < offset + 512; i++) {
|
|
sum += buffer[i];
|
|
}
|
|
|
|
return readSum === sum;
|
|
};
|
|
|
|
/**
|
|
ID3 UINT32 sync-safe tokenizer token.
|
|
28 bits (representing up to 256MB) integer, the msb is 0 to avoid "false syncsignals".
|
|
*/
|
|
util$4.uint32SyncSafeToken = {
|
|
get: (buffer, offset) => {
|
|
return (buffer[offset + 3] & 0x7F) | ((buffer[offset + 2]) << 7) | ((buffer[offset + 1]) << 14) | ((buffer[offset]) << 21);
|
|
},
|
|
len: 4
|
|
};
|
|
|
|
var supported$1 = {
|
|
extensions: [
|
|
'jpg',
|
|
'png',
|
|
'apng',
|
|
'gif',
|
|
'webp',
|
|
'flif',
|
|
'xcf',
|
|
'cr2',
|
|
'cr3',
|
|
'orf',
|
|
'arw',
|
|
'dng',
|
|
'nef',
|
|
'rw2',
|
|
'raf',
|
|
'tif',
|
|
'bmp',
|
|
'icns',
|
|
'jxr',
|
|
'psd',
|
|
'indd',
|
|
'zip',
|
|
'tar',
|
|
'rar',
|
|
'gz',
|
|
'bz2',
|
|
'7z',
|
|
'dmg',
|
|
'mp4',
|
|
'mid',
|
|
'mkv',
|
|
'webm',
|
|
'mov',
|
|
'avi',
|
|
'mpg',
|
|
'mp2',
|
|
'mp3',
|
|
'm4a',
|
|
'oga',
|
|
'ogg',
|
|
'ogv',
|
|
'opus',
|
|
'flac',
|
|
'wav',
|
|
'spx',
|
|
'amr',
|
|
'pdf',
|
|
'epub',
|
|
'exe',
|
|
'swf',
|
|
'rtf',
|
|
'wasm',
|
|
'woff',
|
|
'woff2',
|
|
'eot',
|
|
'ttf',
|
|
'otf',
|
|
'ico',
|
|
'flv',
|
|
'ps',
|
|
'xz',
|
|
'sqlite',
|
|
'nes',
|
|
'crx',
|
|
'xpi',
|
|
'cab',
|
|
'deb',
|
|
'ar',
|
|
'rpm',
|
|
'Z',
|
|
'lz',
|
|
'cfb',
|
|
'mxf',
|
|
'mts',
|
|
'blend',
|
|
'bpg',
|
|
'docx',
|
|
'pptx',
|
|
'xlsx',
|
|
'3gp',
|
|
'3g2',
|
|
'jp2',
|
|
'jpm',
|
|
'jpx',
|
|
'mj2',
|
|
'aif',
|
|
'qcp',
|
|
'odt',
|
|
'ods',
|
|
'odp',
|
|
'xml',
|
|
'mobi',
|
|
'heic',
|
|
'cur',
|
|
'ktx',
|
|
'ape',
|
|
'wv',
|
|
'dcm',
|
|
'ics',
|
|
'glb',
|
|
'pcap',
|
|
'dsf',
|
|
'lnk',
|
|
'alias',
|
|
'voc',
|
|
'ac3',
|
|
'm4v',
|
|
'm4p',
|
|
'm4b',
|
|
'f4v',
|
|
'f4p',
|
|
'f4b',
|
|
'f4a',
|
|
'mie',
|
|
'asf',
|
|
'ogm',
|
|
'ogx',
|
|
'mpc',
|
|
'arrow',
|
|
'shp',
|
|
'aac',
|
|
'mp1',
|
|
'it',
|
|
's3m',
|
|
'xm',
|
|
'ai',
|
|
'skp',
|
|
'avif',
|
|
'eps',
|
|
'lzh',
|
|
'pgp',
|
|
'asar',
|
|
'stl',
|
|
'chm',
|
|
'3mf',
|
|
'zst',
|
|
'jxl',
|
|
'vcf'
|
|
],
|
|
mimeTypes: [
|
|
'image/jpeg',
|
|
'image/png',
|
|
'image/gif',
|
|
'image/webp',
|
|
'image/flif',
|
|
'image/x-xcf',
|
|
'image/x-canon-cr2',
|
|
'image/x-canon-cr3',
|
|
'image/tiff',
|
|
'image/bmp',
|
|
'image/vnd.ms-photo',
|
|
'image/vnd.adobe.photoshop',
|
|
'application/x-indesign',
|
|
'application/epub+zip',
|
|
'application/x-xpinstall',
|
|
'application/vnd.oasis.opendocument.text',
|
|
'application/vnd.oasis.opendocument.spreadsheet',
|
|
'application/vnd.oasis.opendocument.presentation',
|
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
'application/zip',
|
|
'application/x-tar',
|
|
'application/x-rar-compressed',
|
|
'application/gzip',
|
|
'application/x-bzip2',
|
|
'application/x-7z-compressed',
|
|
'application/x-apple-diskimage',
|
|
'application/x-apache-arrow',
|
|
'video/mp4',
|
|
'audio/midi',
|
|
'video/x-matroska',
|
|
'video/webm',
|
|
'video/quicktime',
|
|
'video/vnd.avi',
|
|
'audio/vnd.wave',
|
|
'audio/qcelp',
|
|
'audio/x-ms-asf',
|
|
'video/x-ms-asf',
|
|
'application/vnd.ms-asf',
|
|
'video/mpeg',
|
|
'video/3gpp',
|
|
'audio/mpeg',
|
|
'audio/mp4', // RFC 4337
|
|
'audio/opus',
|
|
'video/ogg',
|
|
'audio/ogg',
|
|
'application/ogg',
|
|
'audio/x-flac',
|
|
'audio/ape',
|
|
'audio/wavpack',
|
|
'audio/amr',
|
|
'application/pdf',
|
|
'application/x-msdownload',
|
|
'application/x-shockwave-flash',
|
|
'application/rtf',
|
|
'application/wasm',
|
|
'font/woff',
|
|
'font/woff2',
|
|
'application/vnd.ms-fontobject',
|
|
'font/ttf',
|
|
'font/otf',
|
|
'image/x-icon',
|
|
'video/x-flv',
|
|
'application/postscript',
|
|
'application/eps',
|
|
'application/x-xz',
|
|
'application/x-sqlite3',
|
|
'application/x-nintendo-nes-rom',
|
|
'application/x-google-chrome-extension',
|
|
'application/vnd.ms-cab-compressed',
|
|
'application/x-deb',
|
|
'application/x-unix-archive',
|
|
'application/x-rpm',
|
|
'application/x-compress',
|
|
'application/x-lzip',
|
|
'application/x-cfb',
|
|
'application/x-mie',
|
|
'application/mxf',
|
|
'video/mp2t',
|
|
'application/x-blender',
|
|
'image/bpg',
|
|
'image/jp2',
|
|
'image/jpx',
|
|
'image/jpm',
|
|
'image/mj2',
|
|
'audio/aiff',
|
|
'application/xml',
|
|
'application/x-mobipocket-ebook',
|
|
'image/heif',
|
|
'image/heif-sequence',
|
|
'image/heic',
|
|
'image/heic-sequence',
|
|
'image/icns',
|
|
'image/ktx',
|
|
'application/dicom',
|
|
'audio/x-musepack',
|
|
'text/calendar',
|
|
'text/vcard',
|
|
'model/gltf-binary',
|
|
'application/vnd.tcpdump.pcap',
|
|
'audio/x-dsf', // Non-standard
|
|
'application/x.ms.shortcut', // Invented by us
|
|
'application/x.apple.alias', // Invented by us
|
|
'audio/x-voc',
|
|
'audio/vnd.dolby.dd-raw',
|
|
'audio/x-m4a',
|
|
'image/apng',
|
|
'image/x-olympus-orf',
|
|
'image/x-sony-arw',
|
|
'image/x-adobe-dng',
|
|
'image/x-nikon-nef',
|
|
'image/x-panasonic-rw2',
|
|
'image/x-fujifilm-raf',
|
|
'video/x-m4v',
|
|
'video/3gpp2',
|
|
'application/x-esri-shape',
|
|
'audio/aac',
|
|
'audio/x-it',
|
|
'audio/x-s3m',
|
|
'audio/x-xm',
|
|
'video/MP1S',
|
|
'video/MP2P',
|
|
'application/vnd.sketchup.skp',
|
|
'image/avif',
|
|
'application/x-lzh-compressed',
|
|
'application/pgp-encrypted',
|
|
'application/x-asar',
|
|
'model/stl',
|
|
'application/vnd.ms-htmlhelp',
|
|
'model/3mf',
|
|
'image/jxl',
|
|
'application/zstd'
|
|
]
|
|
};
|
|
|
|
const Token = lib$1;
|
|
const strtok3$1 = core$3;
|
|
const {
|
|
stringToBytes,
|
|
tarHeaderChecksumMatches,
|
|
uint32SyncSafeToken
|
|
} = util$4;
|
|
const supported = supported$1;
|
|
|
|
const minimumBytes = 4100; // A fair amount of file-types are detectable within this range
|
|
|
|
async function fromStream(stream) {
|
|
const tokenizer = await strtok3$1.fromStream(stream);
|
|
try {
|
|
return await fromTokenizer(tokenizer);
|
|
} finally {
|
|
await tokenizer.close();
|
|
}
|
|
}
|
|
|
|
async function fromBuffer(input) {
|
|
if (!(input instanceof Uint8Array || input instanceof ArrayBuffer || Buffer.isBuffer(input))) {
|
|
throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof input}\``);
|
|
}
|
|
|
|
const buffer = input instanceof Buffer ? input : Buffer.from(input);
|
|
|
|
if (!(buffer && buffer.length > 1)) {
|
|
return;
|
|
}
|
|
|
|
const tokenizer = strtok3$1.fromBuffer(buffer);
|
|
return fromTokenizer(tokenizer);
|
|
}
|
|
|
|
function _check(buffer, headers, options) {
|
|
options = {
|
|
offset: 0,
|
|
...options
|
|
};
|
|
|
|
for (const [index, header] of headers.entries()) {
|
|
// If a bitmask is set
|
|
if (options.mask) {
|
|
// If header doesn't equal `buf` with bits masked off
|
|
if (header !== (options.mask[index] & buffer[index + options.offset])) {
|
|
return false;
|
|
}
|
|
} else if (header !== buffer[index + options.offset]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
async function fromTokenizer(tokenizer) {
|
|
try {
|
|
return _fromTokenizer(tokenizer);
|
|
} catch (error) {
|
|
if (!(error instanceof strtok3$1.EndOfStreamError)) {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
async function _fromTokenizer(tokenizer) {
|
|
let buffer = Buffer.alloc(minimumBytes);
|
|
const bytesRead = 12;
|
|
const check = (header, options) => _check(buffer, header, options);
|
|
const checkString = (header, options) => check(stringToBytes(header), options);
|
|
|
|
// Keep reading until EOF if the file size is unknown.
|
|
if (!tokenizer.fileInfo.size) {
|
|
tokenizer.fileInfo.size = Number.MAX_SAFE_INTEGER;
|
|
}
|
|
|
|
await tokenizer.peekBuffer(buffer, {length: bytesRead, mayBeLess: true});
|
|
|
|
// -- 2-byte signatures --
|
|
|
|
if (check([0x42, 0x4D])) {
|
|
return {
|
|
ext: 'bmp',
|
|
mime: 'image/bmp'
|
|
};
|
|
}
|
|
|
|
if (check([0x0B, 0x77])) {
|
|
return {
|
|
ext: 'ac3',
|
|
mime: 'audio/vnd.dolby.dd-raw'
|
|
};
|
|
}
|
|
|
|
if (check([0x78, 0x01])) {
|
|
return {
|
|
ext: 'dmg',
|
|
mime: 'application/x-apple-diskimage'
|
|
};
|
|
}
|
|
|
|
if (check([0x4D, 0x5A])) {
|
|
return {
|
|
ext: 'exe',
|
|
mime: 'application/x-msdownload'
|
|
};
|
|
}
|
|
|
|
if (check([0x25, 0x21])) {
|
|
await tokenizer.peekBuffer(buffer, {length: 24, mayBeLess: true});
|
|
|
|
if (checkString('PS-Adobe-', {offset: 2}) &&
|
|
checkString(' EPSF-', {offset: 14})) {
|
|
return {
|
|
ext: 'eps',
|
|
mime: 'application/eps'
|
|
};
|
|
}
|
|
|
|
return {
|
|
ext: 'ps',
|
|
mime: 'application/postscript'
|
|
};
|
|
}
|
|
|
|
if (
|
|
check([0x1F, 0xA0]) ||
|
|
check([0x1F, 0x9D])
|
|
) {
|
|
return {
|
|
ext: 'Z',
|
|
mime: 'application/x-compress'
|
|
};
|
|
}
|
|
|
|
// -- 3-byte signatures --
|
|
|
|
if (check([0xFF, 0xD8, 0xFF])) {
|
|
return {
|
|
ext: 'jpg',
|
|
mime: 'image/jpeg'
|
|
};
|
|
}
|
|
|
|
if (check([0x49, 0x49, 0xBC])) {
|
|
return {
|
|
ext: 'jxr',
|
|
mime: 'image/vnd.ms-photo'
|
|
};
|
|
}
|
|
|
|
if (check([0x1F, 0x8B, 0x8])) {
|
|
return {
|
|
ext: 'gz',
|
|
mime: 'application/gzip'
|
|
};
|
|
}
|
|
|
|
if (check([0x42, 0x5A, 0x68])) {
|
|
return {
|
|
ext: 'bz2',
|
|
mime: 'application/x-bzip2'
|
|
};
|
|
}
|
|
|
|
if (checkString('ID3')) {
|
|
await tokenizer.ignore(6); // Skip ID3 header until the header size
|
|
const id3HeaderLen = await tokenizer.readToken(uint32SyncSafeToken);
|
|
if (tokenizer.position + id3HeaderLen > tokenizer.fileInfo.size) {
|
|
// Guess file type based on ID3 header for backward compatibility
|
|
return {
|
|
ext: 'mp3',
|
|
mime: 'audio/mpeg'
|
|
};
|
|
}
|
|
|
|
await tokenizer.ignore(id3HeaderLen);
|
|
return fromTokenizer(tokenizer); // Skip ID3 header, recursion
|
|
}
|
|
|
|
// Musepack, SV7
|
|
if (checkString('MP+')) {
|
|
return {
|
|
ext: 'mpc',
|
|
mime: 'audio/x-musepack'
|
|
};
|
|
}
|
|
|
|
if (
|
|
(buffer[0] === 0x43 || buffer[0] === 0x46) &&
|
|
check([0x57, 0x53], {offset: 1})
|
|
) {
|
|
return {
|
|
ext: 'swf',
|
|
mime: 'application/x-shockwave-flash'
|
|
};
|
|
}
|
|
|
|
// -- 4-byte signatures --
|
|
|
|
if (check([0x47, 0x49, 0x46])) {
|
|
return {
|
|
ext: 'gif',
|
|
mime: 'image/gif'
|
|
};
|
|
}
|
|
|
|
if (checkString('FLIF')) {
|
|
return {
|
|
ext: 'flif',
|
|
mime: 'image/flif'
|
|
};
|
|
}
|
|
|
|
if (checkString('8BPS')) {
|
|
return {
|
|
ext: 'psd',
|
|
mime: 'image/vnd.adobe.photoshop'
|
|
};
|
|
}
|
|
|
|
if (checkString('WEBP', {offset: 8})) {
|
|
return {
|
|
ext: 'webp',
|
|
mime: 'image/webp'
|
|
};
|
|
}
|
|
|
|
// Musepack, SV8
|
|
if (checkString('MPCK')) {
|
|
return {
|
|
ext: 'mpc',
|
|
mime: 'audio/x-musepack'
|
|
};
|
|
}
|
|
|
|
if (checkString('FORM')) {
|
|
return {
|
|
ext: 'aif',
|
|
mime: 'audio/aiff'
|
|
};
|
|
}
|
|
|
|
if (checkString('icns', {offset: 0})) {
|
|
return {
|
|
ext: 'icns',
|
|
mime: 'image/icns'
|
|
};
|
|
}
|
|
|
|
// Zip-based file formats
|
|
// Need to be before the `zip` check
|
|
if (check([0x50, 0x4B, 0x3, 0x4])) { // Local file header signature
|
|
try {
|
|
while (tokenizer.position + 30 < tokenizer.fileInfo.size) {
|
|
await tokenizer.readBuffer(buffer, {length: 30});
|
|
|
|
// https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
|
|
const zipHeader = {
|
|
compressedSize: buffer.readUInt32LE(18),
|
|
uncompressedSize: buffer.readUInt32LE(22),
|
|
filenameLength: buffer.readUInt16LE(26),
|
|
extraFieldLength: buffer.readUInt16LE(28)
|
|
};
|
|
|
|
zipHeader.filename = await tokenizer.readToken(new Token.StringType(zipHeader.filenameLength, 'utf-8'));
|
|
await tokenizer.ignore(zipHeader.extraFieldLength);
|
|
|
|
// Assumes signed `.xpi` from addons.mozilla.org
|
|
if (zipHeader.filename === 'META-INF/mozilla.rsa') {
|
|
return {
|
|
ext: 'xpi',
|
|
mime: 'application/x-xpinstall'
|
|
};
|
|
}
|
|
|
|
if (zipHeader.filename.endsWith('.rels') || zipHeader.filename.endsWith('.xml')) {
|
|
const type = zipHeader.filename.split('/')[0];
|
|
switch (type) {
|
|
case '_rels':
|
|
break;
|
|
case 'word':
|
|
return {
|
|
ext: 'docx',
|
|
mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
};
|
|
case 'ppt':
|
|
return {
|
|
ext: 'pptx',
|
|
mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
|
};
|
|
case 'xl':
|
|
return {
|
|
ext: 'xlsx',
|
|
mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
};
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (zipHeader.filename.startsWith('xl/')) {
|
|
return {
|
|
ext: 'xlsx',
|
|
mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
};
|
|
}
|
|
|
|
if (zipHeader.filename.startsWith('3D/') && zipHeader.filename.endsWith('.model')) {
|
|
return {
|
|
ext: '3mf',
|
|
mime: 'model/3mf'
|
|
};
|
|
}
|
|
|
|
// The docx, xlsx and pptx file types extend the Office Open XML file format:
|
|
// https://en.wikipedia.org/wiki/Office_Open_XML_file_formats
|
|
// We look for:
|
|
// - one entry named '[Content_Types].xml' or '_rels/.rels',
|
|
// - one entry indicating specific type of file.
|
|
// MS Office, OpenOffice and LibreOffice may put the parts in different order, so the check should not rely on it.
|
|
if (zipHeader.filename === 'mimetype' && zipHeader.compressedSize === zipHeader.uncompressedSize) {
|
|
const mimeType = await tokenizer.readToken(new Token.StringType(zipHeader.compressedSize, 'utf-8'));
|
|
|
|
switch (mimeType) {
|
|
case 'application/epub+zip':
|
|
return {
|
|
ext: 'epub',
|
|
mime: 'application/epub+zip'
|
|
};
|
|
case 'application/vnd.oasis.opendocument.text':
|
|
return {
|
|
ext: 'odt',
|
|
mime: 'application/vnd.oasis.opendocument.text'
|
|
};
|
|
case 'application/vnd.oasis.opendocument.spreadsheet':
|
|
return {
|
|
ext: 'ods',
|
|
mime: 'application/vnd.oasis.opendocument.spreadsheet'
|
|
};
|
|
case 'application/vnd.oasis.opendocument.presentation':
|
|
return {
|
|
ext: 'odp',
|
|
mime: 'application/vnd.oasis.opendocument.presentation'
|
|
};
|
|
default:
|
|
}
|
|
}
|
|
|
|
// Try to find next header manually when current one is corrupted
|
|
if (zipHeader.compressedSize === 0) {
|
|
let nextHeaderIndex = -1;
|
|
|
|
while (nextHeaderIndex < 0 && (tokenizer.position < tokenizer.fileInfo.size)) {
|
|
await tokenizer.peekBuffer(buffer, {mayBeLess: true});
|
|
|
|
nextHeaderIndex = buffer.indexOf('504B0304', 0, 'hex');
|
|
// Move position to the next header if found, skip the whole buffer otherwise
|
|
await tokenizer.ignore(nextHeaderIndex >= 0 ? nextHeaderIndex : buffer.length);
|
|
}
|
|
} else {
|
|
await tokenizer.ignore(zipHeader.compressedSize);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
if (!(error instanceof strtok3$1.EndOfStreamError)) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
return {
|
|
ext: 'zip',
|
|
mime: 'application/zip'
|
|
};
|
|
}
|
|
|
|
if (checkString('OggS')) {
|
|
// This is an OGG container
|
|
await tokenizer.ignore(28);
|
|
const type = Buffer.alloc(8);
|
|
await tokenizer.readBuffer(type);
|
|
|
|
// Needs to be before `ogg` check
|
|
if (_check(type, [0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64])) {
|
|
return {
|
|
ext: 'opus',
|
|
mime: 'audio/opus'
|
|
};
|
|
}
|
|
|
|
// If ' theora' in header.
|
|
if (_check(type, [0x80, 0x74, 0x68, 0x65, 0x6F, 0x72, 0x61])) {
|
|
return {
|
|
ext: 'ogv',
|
|
mime: 'video/ogg'
|
|
};
|
|
}
|
|
|
|
// If '\x01video' in header.
|
|
if (_check(type, [0x01, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x00])) {
|
|
return {
|
|
ext: 'ogm',
|
|
mime: 'video/ogg'
|
|
};
|
|
}
|
|
|
|
// If ' FLAC' in header https://xiph.org/flac/faq.html
|
|
if (_check(type, [0x7F, 0x46, 0x4C, 0x41, 0x43])) {
|
|
return {
|
|
ext: 'oga',
|
|
mime: 'audio/ogg'
|
|
};
|
|
}
|
|
|
|
// 'Speex ' in header https://en.wikipedia.org/wiki/Speex
|
|
if (_check(type, [0x53, 0x70, 0x65, 0x65, 0x78, 0x20, 0x20])) {
|
|
return {
|
|
ext: 'spx',
|
|
mime: 'audio/ogg'
|
|
};
|
|
}
|
|
|
|
// If '\x01vorbis' in header
|
|
if (_check(type, [0x01, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73])) {
|
|
return {
|
|
ext: 'ogg',
|
|
mime: 'audio/ogg'
|
|
};
|
|
}
|
|
|
|
// Default OGG container https://www.iana.org/assignments/media-types/application/ogg
|
|
return {
|
|
ext: 'ogx',
|
|
mime: 'application/ogg'
|
|
};
|
|
}
|
|
|
|
if (
|
|
check([0x50, 0x4B]) &&
|
|
(buffer[2] === 0x3 || buffer[2] === 0x5 || buffer[2] === 0x7) &&
|
|
(buffer[3] === 0x4 || buffer[3] === 0x6 || buffer[3] === 0x8)
|
|
) {
|
|
return {
|
|
ext: 'zip',
|
|
mime: 'application/zip'
|
|
};
|
|
}
|
|
|
|
//
|
|
|
|
// File Type Box (https://en.wikipedia.org/wiki/ISO_base_media_file_format)
|
|
// It's not required to be first, but it's recommended to be. Almost all ISO base media files start with `ftyp` box.
|
|
// `ftyp` box must contain a brand major identifier, which must consist of ISO 8859-1 printable characters.
|
|
// Here we check for 8859-1 printable characters (for simplicity, it's a mask which also catches one non-printable character).
|
|
if (
|
|
checkString('ftyp', {offset: 4}) &&
|
|
(buffer[8] & 0x60) !== 0x00 // Brand major, first character ASCII?
|
|
) {
|
|
// They all can have MIME `video/mp4` except `application/mp4` special-case which is hard to detect.
|
|
// For some cases, we're specific, everything else falls to `video/mp4` with `mp4` extension.
|
|
const brandMajor = buffer.toString('binary', 8, 12).replace('\0', ' ').trim();
|
|
switch (brandMajor) {
|
|
case 'avif':
|
|
return {ext: 'avif', mime: 'image/avif'};
|
|
case 'mif1':
|
|
return {ext: 'heic', mime: 'image/heif'};
|
|
case 'msf1':
|
|
return {ext: 'heic', mime: 'image/heif-sequence'};
|
|
case 'heic':
|
|
case 'heix':
|
|
return {ext: 'heic', mime: 'image/heic'};
|
|
case 'hevc':
|
|
case 'hevx':
|
|
return {ext: 'heic', mime: 'image/heic-sequence'};
|
|
case 'qt':
|
|
return {ext: 'mov', mime: 'video/quicktime'};
|
|
case 'M4V':
|
|
case 'M4VH':
|
|
case 'M4VP':
|
|
return {ext: 'm4v', mime: 'video/x-m4v'};
|
|
case 'M4P':
|
|
return {ext: 'm4p', mime: 'video/mp4'};
|
|
case 'M4B':
|
|
return {ext: 'm4b', mime: 'audio/mp4'};
|
|
case 'M4A':
|
|
return {ext: 'm4a', mime: 'audio/x-m4a'};
|
|
case 'F4V':
|
|
return {ext: 'f4v', mime: 'video/mp4'};
|
|
case 'F4P':
|
|
return {ext: 'f4p', mime: 'video/mp4'};
|
|
case 'F4A':
|
|
return {ext: 'f4a', mime: 'audio/mp4'};
|
|
case 'F4B':
|
|
return {ext: 'f4b', mime: 'audio/mp4'};
|
|
case 'crx':
|
|
return {ext: 'cr3', mime: 'image/x-canon-cr3'};
|
|
default:
|
|
if (brandMajor.startsWith('3g')) {
|
|
if (brandMajor.startsWith('3g2')) {
|
|
return {ext: '3g2', mime: 'video/3gpp2'};
|
|
}
|
|
|
|
return {ext: '3gp', mime: 'video/3gpp'};
|
|
}
|
|
|
|
return {ext: 'mp4', mime: 'video/mp4'};
|
|
}
|
|
}
|
|
|
|
if (checkString('MThd')) {
|
|
return {
|
|
ext: 'mid',
|
|
mime: 'audio/midi'
|
|
};
|
|
}
|
|
|
|
if (
|
|
checkString('wOFF') &&
|
|
(
|
|
check([0x00, 0x01, 0x00, 0x00], {offset: 4}) ||
|
|
checkString('OTTO', {offset: 4})
|
|
)
|
|
) {
|
|
return {
|
|
ext: 'woff',
|
|
mime: 'font/woff'
|
|
};
|
|
}
|
|
|
|
if (
|
|
checkString('wOF2') &&
|
|
(
|
|
check([0x00, 0x01, 0x00, 0x00], {offset: 4}) ||
|
|
checkString('OTTO', {offset: 4})
|
|
)
|
|
) {
|
|
return {
|
|
ext: 'woff2',
|
|
mime: 'font/woff2'
|
|
};
|
|
}
|
|
|
|
if (check([0xD4, 0xC3, 0xB2, 0xA1]) || check([0xA1, 0xB2, 0xC3, 0xD4])) {
|
|
return {
|
|
ext: 'pcap',
|
|
mime: 'application/vnd.tcpdump.pcap'
|
|
};
|
|
}
|
|
|
|
// Sony DSD Stream File (DSF)
|
|
if (checkString('DSD ')) {
|
|
return {
|
|
ext: 'dsf',
|
|
mime: 'audio/x-dsf' // Non-standard
|
|
};
|
|
}
|
|
|
|
if (checkString('LZIP')) {
|
|
return {
|
|
ext: 'lz',
|
|
mime: 'application/x-lzip'
|
|
};
|
|
}
|
|
|
|
if (checkString('fLaC')) {
|
|
return {
|
|
ext: 'flac',
|
|
mime: 'audio/x-flac'
|
|
};
|
|
}
|
|
|
|
if (check([0x42, 0x50, 0x47, 0xFB])) {
|
|
return {
|
|
ext: 'bpg',
|
|
mime: 'image/bpg'
|
|
};
|
|
}
|
|
|
|
if (checkString('wvpk')) {
|
|
return {
|
|
ext: 'wv',
|
|
mime: 'audio/wavpack'
|
|
};
|
|
}
|
|
|
|
if (checkString('%PDF')) {
|
|
await tokenizer.ignore(1350);
|
|
const maxBufferSize = 10 * 1024 * 1024;
|
|
const buffer = Buffer.alloc(Math.min(maxBufferSize, tokenizer.fileInfo.size));
|
|
await tokenizer.readBuffer(buffer, {mayBeLess: true});
|
|
|
|
// Check if this is an Adobe Illustrator file
|
|
if (buffer.includes(Buffer.from('AIPrivateData'))) {
|
|
return {
|
|
ext: 'ai',
|
|
mime: 'application/postscript'
|
|
};
|
|
}
|
|
|
|
// Assume this is just a normal PDF
|
|
return {
|
|
ext: 'pdf',
|
|
mime: 'application/pdf'
|
|
};
|
|
}
|
|
|
|
if (check([0x00, 0x61, 0x73, 0x6D])) {
|
|
return {
|
|
ext: 'wasm',
|
|
mime: 'application/wasm'
|
|
};
|
|
}
|
|
|
|
// TIFF, little-endian type
|
|
if (check([0x49, 0x49, 0x2A, 0x0])) {
|
|
if (checkString('CR', {offset: 8})) {
|
|
return {
|
|
ext: 'cr2',
|
|
mime: 'image/x-canon-cr2'
|
|
};
|
|
}
|
|
|
|
if (check([0x1C, 0x00, 0xFE, 0x00], {offset: 8}) || check([0x1F, 0x00, 0x0B, 0x00], {offset: 8})) {
|
|
return {
|
|
ext: 'nef',
|
|
mime: 'image/x-nikon-nef'
|
|
};
|
|
}
|
|
|
|
if (
|
|
check([0x08, 0x00, 0x00, 0x00], {offset: 4}) &&
|
|
(check([0x2D, 0x00, 0xFE, 0x00], {offset: 8}) ||
|
|
check([0x27, 0x00, 0xFE, 0x00], {offset: 8}))
|
|
) {
|
|
return {
|
|
ext: 'dng',
|
|
mime: 'image/x-adobe-dng'
|
|
};
|
|
}
|
|
|
|
buffer = Buffer.alloc(24);
|
|
await tokenizer.peekBuffer(buffer);
|
|
if (
|
|
(check([0x10, 0xFB, 0x86, 0x01], {offset: 4}) || check([0x08, 0x00, 0x00, 0x00], {offset: 4})) &&
|
|
// This pattern differentiates ARW from other TIFF-ish file types:
|
|
check([0x00, 0xFE, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x01], {offset: 9})
|
|
) {
|
|
return {
|
|
ext: 'arw',
|
|
mime: 'image/x-sony-arw'
|
|
};
|
|
}
|
|
|
|
return {
|
|
ext: 'tif',
|
|
mime: 'image/tiff'
|
|
};
|
|
}
|
|
|
|
// TIFF, big-endian type
|
|
if (check([0x4D, 0x4D, 0x0, 0x2A])) {
|
|
return {
|
|
ext: 'tif',
|
|
mime: 'image/tiff'
|
|
};
|
|
}
|
|
|
|
if (checkString('MAC ')) {
|
|
return {
|
|
ext: 'ape',
|
|
mime: 'audio/ape'
|
|
};
|
|
}
|
|
|
|
// https://github.com/threatstack/libmagic/blob/master/magic/Magdir/matroska
|
|
if (check([0x1A, 0x45, 0xDF, 0xA3])) { // Root element: EBML
|
|
async function readField() {
|
|
const msb = await tokenizer.peekNumber(Token.UINT8);
|
|
let mask = 0x80;
|
|
let ic = 0; // 0 = A, 1 = B, 2 = C, 3 = D
|
|
|
|
while ((msb & mask) === 0 && mask !== 0) {
|
|
++ic;
|
|
mask >>= 1;
|
|
}
|
|
|
|
const id = Buffer.alloc(ic + 1);
|
|
await tokenizer.readBuffer(id);
|
|
return id;
|
|
}
|
|
|
|
async function readElement() {
|
|
const id = await readField();
|
|
const lenField = await readField();
|
|
lenField[0] ^= 0x80 >> (lenField.length - 1);
|
|
const nrLen = Math.min(6, lenField.length); // JavaScript can max read 6 bytes integer
|
|
return {
|
|
id: id.readUIntBE(0, id.length),
|
|
len: lenField.readUIntBE(lenField.length - nrLen, nrLen)
|
|
};
|
|
}
|
|
|
|
async function readChildren(level, children) {
|
|
while (children > 0) {
|
|
const e = await readElement();
|
|
if (e.id === 0x4282) {
|
|
return tokenizer.readToken(new Token.StringType(e.len, 'utf-8')); // Return DocType
|
|
}
|
|
|
|
await tokenizer.ignore(e.len); // ignore payload
|
|
--children;
|
|
}
|
|
}
|
|
|
|
const re = await readElement();
|
|
const docType = await readChildren(1, re.len);
|
|
|
|
switch (docType) {
|
|
case 'webm':
|
|
return {
|
|
ext: 'webm',
|
|
mime: 'video/webm'
|
|
};
|
|
|
|
case 'matroska':
|
|
return {
|
|
ext: 'mkv',
|
|
mime: 'video/x-matroska'
|
|
};
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
// RIFF file format which might be AVI, WAV, QCP, etc
|
|
if (check([0x52, 0x49, 0x46, 0x46])) {
|
|
if (check([0x41, 0x56, 0x49], {offset: 8})) {
|
|
return {
|
|
ext: 'avi',
|
|
mime: 'video/vnd.avi'
|
|
};
|
|
}
|
|
|
|
if (check([0x57, 0x41, 0x56, 0x45], {offset: 8})) {
|
|
return {
|
|
ext: 'wav',
|
|
mime: 'audio/vnd.wave'
|
|
};
|
|
}
|
|
|
|
// QLCM, QCP file
|
|
if (check([0x51, 0x4C, 0x43, 0x4D], {offset: 8})) {
|
|
return {
|
|
ext: 'qcp',
|
|
mime: 'audio/qcelp'
|
|
};
|
|
}
|
|
}
|
|
|
|
if (checkString('SQLi')) {
|
|
return {
|
|
ext: 'sqlite',
|
|
mime: 'application/x-sqlite3'
|
|
};
|
|
}
|
|
|
|
if (check([0x4E, 0x45, 0x53, 0x1A])) {
|
|
return {
|
|
ext: 'nes',
|
|
mime: 'application/x-nintendo-nes-rom'
|
|
};
|
|
}
|
|
|
|
if (checkString('Cr24')) {
|
|
return {
|
|
ext: 'crx',
|
|
mime: 'application/x-google-chrome-extension'
|
|
};
|
|
}
|
|
|
|
if (
|
|
checkString('MSCF') ||
|
|
checkString('ISc(')
|
|
) {
|
|
return {
|
|
ext: 'cab',
|
|
mime: 'application/vnd.ms-cab-compressed'
|
|
};
|
|
}
|
|
|
|
if (check([0xED, 0xAB, 0xEE, 0xDB])) {
|
|
return {
|
|
ext: 'rpm',
|
|
mime: 'application/x-rpm'
|
|
};
|
|
}
|
|
|
|
if (check([0xC5, 0xD0, 0xD3, 0xC6])) {
|
|
return {
|
|
ext: 'eps',
|
|
mime: 'application/eps'
|
|
};
|
|
}
|
|
|
|
if (check([0x28, 0xB5, 0x2F, 0xFD])) {
|
|
return {
|
|
ext: 'zst',
|
|
mime: 'application/zstd'
|
|
};
|
|
}
|
|
|
|
// -- 5-byte signatures --
|
|
|
|
if (check([0x4F, 0x54, 0x54, 0x4F, 0x00])) {
|
|
return {
|
|
ext: 'otf',
|
|
mime: 'font/otf'
|
|
};
|
|
}
|
|
|
|
if (checkString('#!AMR')) {
|
|
return {
|
|
ext: 'amr',
|
|
mime: 'audio/amr'
|
|
};
|
|
}
|
|
|
|
if (checkString('{\\rtf')) {
|
|
return {
|
|
ext: 'rtf',
|
|
mime: 'application/rtf'
|
|
};
|
|
}
|
|
|
|
if (check([0x46, 0x4C, 0x56, 0x01])) {
|
|
return {
|
|
ext: 'flv',
|
|
mime: 'video/x-flv'
|
|
};
|
|
}
|
|
|
|
if (checkString('IMPM')) {
|
|
return {
|
|
ext: 'it',
|
|
mime: 'audio/x-it'
|
|
};
|
|
}
|
|
|
|
if (
|
|
checkString('-lh0-', {offset: 2}) ||
|
|
checkString('-lh1-', {offset: 2}) ||
|
|
checkString('-lh2-', {offset: 2}) ||
|
|
checkString('-lh3-', {offset: 2}) ||
|
|
checkString('-lh4-', {offset: 2}) ||
|
|
checkString('-lh5-', {offset: 2}) ||
|
|
checkString('-lh6-', {offset: 2}) ||
|
|
checkString('-lh7-', {offset: 2}) ||
|
|
checkString('-lzs-', {offset: 2}) ||
|
|
checkString('-lz4-', {offset: 2}) ||
|
|
checkString('-lz5-', {offset: 2}) ||
|
|
checkString('-lhd-', {offset: 2})
|
|
) {
|
|
return {
|
|
ext: 'lzh',
|
|
mime: 'application/x-lzh-compressed'
|
|
};
|
|
}
|
|
|
|
// MPEG program stream (PS or MPEG-PS)
|
|
if (check([0x00, 0x00, 0x01, 0xBA])) {
|
|
// MPEG-PS, MPEG-1 Part 1
|
|
if (check([0x21], {offset: 4, mask: [0xF1]})) {
|
|
return {
|
|
ext: 'mpg', // May also be .ps, .mpeg
|
|
mime: 'video/MP1S'
|
|
};
|
|
}
|
|
|
|
// MPEG-PS, MPEG-2 Part 1
|
|
if (check([0x44], {offset: 4, mask: [0xC4]})) {
|
|
return {
|
|
ext: 'mpg', // May also be .mpg, .m2p, .vob or .sub
|
|
mime: 'video/MP2P'
|
|
};
|
|
}
|
|
}
|
|
|
|
if (checkString('ITSF')) {
|
|
return {
|
|
ext: 'chm',
|
|
mime: 'application/vnd.ms-htmlhelp'
|
|
};
|
|
}
|
|
|
|
// -- 6-byte signatures --
|
|
|
|
if (check([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00])) {
|
|
return {
|
|
ext: 'xz',
|
|
mime: 'application/x-xz'
|
|
};
|
|
}
|
|
|
|
if (checkString('<?xml ')) {
|
|
return {
|
|
ext: 'xml',
|
|
mime: 'application/xml'
|
|
};
|
|
}
|
|
|
|
if (check([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C])) {
|
|
return {
|
|
ext: '7z',
|
|
mime: 'application/x-7z-compressed'
|
|
};
|
|
}
|
|
|
|
if (
|
|
check([0x52, 0x61, 0x72, 0x21, 0x1A, 0x7]) &&
|
|
(buffer[6] === 0x0 || buffer[6] === 0x1)
|
|
) {
|
|
return {
|
|
ext: 'rar',
|
|
mime: 'application/x-rar-compressed'
|
|
};
|
|
}
|
|
|
|
if (checkString('solid ')) {
|
|
return {
|
|
ext: 'stl',
|
|
mime: 'model/stl'
|
|
};
|
|
}
|
|
|
|
// -- 7-byte signatures --
|
|
|
|
if (checkString('BLENDER')) {
|
|
return {
|
|
ext: 'blend',
|
|
mime: 'application/x-blender'
|
|
};
|
|
}
|
|
|
|
if (checkString('!<arch>')) {
|
|
await tokenizer.ignore(8);
|
|
const str = await tokenizer.readToken(new Token.StringType(13, 'ascii'));
|
|
if (str === 'debian-binary') {
|
|
return {
|
|
ext: 'deb',
|
|
mime: 'application/x-deb'
|
|
};
|
|
}
|
|
|
|
return {
|
|
ext: 'ar',
|
|
mime: 'application/x-unix-archive'
|
|
};
|
|
}
|
|
|
|
// -- 8-byte signatures --
|
|
|
|
if (check([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])) {
|
|
// APNG format (https://wiki.mozilla.org/APNG_Specification)
|
|
// 1. Find the first IDAT (image data) chunk (49 44 41 54)
|
|
// 2. Check if there is an "acTL" chunk before the IDAT one (61 63 54 4C)
|
|
|
|
// Offset calculated as follows:
|
|
// - 8 bytes: PNG signature
|
|
// - 4 (length) + 4 (chunk type) + 13 (chunk data) + 4 (CRC): IHDR chunk
|
|
|
|
await tokenizer.ignore(8); // ignore PNG signature
|
|
|
|
async function readChunkHeader() {
|
|
return {
|
|
length: await tokenizer.readToken(Token.INT32_BE),
|
|
type: await tokenizer.readToken(new Token.StringType(4, 'binary'))
|
|
};
|
|
}
|
|
|
|
do {
|
|
const chunk = await readChunkHeader();
|
|
if (chunk.length < 0) {
|
|
return; // Invalid chunk length
|
|
}
|
|
|
|
switch (chunk.type) {
|
|
case 'IDAT':
|
|
return {
|
|
ext: 'png',
|
|
mime: 'image/png'
|
|
};
|
|
case 'acTL':
|
|
return {
|
|
ext: 'apng',
|
|
mime: 'image/apng'
|
|
};
|
|
default:
|
|
await tokenizer.ignore(chunk.length + 4); // Ignore chunk-data + CRC
|
|
}
|
|
} while (tokenizer.position + 8 < tokenizer.fileInfo.size);
|
|
|
|
return {
|
|
ext: 'png',
|
|
mime: 'image/png'
|
|
};
|
|
}
|
|
|
|
if (check([0x41, 0x52, 0x52, 0x4F, 0x57, 0x31, 0x00, 0x00])) {
|
|
return {
|
|
ext: 'arrow',
|
|
mime: 'application/x-apache-arrow'
|
|
};
|
|
}
|
|
|
|
if (check([0x67, 0x6C, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00])) {
|
|
return {
|
|
ext: 'glb',
|
|
mime: 'model/gltf-binary'
|
|
};
|
|
}
|
|
|
|
// `mov` format variants
|
|
if (
|
|
check([0x66, 0x72, 0x65, 0x65], {offset: 4}) || // `free`
|
|
check([0x6D, 0x64, 0x61, 0x74], {offset: 4}) || // `mdat` MJPEG
|
|
check([0x6D, 0x6F, 0x6F, 0x76], {offset: 4}) || // `moov`
|
|
check([0x77, 0x69, 0x64, 0x65], {offset: 4}) // `wide`
|
|
) {
|
|
return {
|
|
ext: 'mov',
|
|
mime: 'video/quicktime'
|
|
};
|
|
}
|
|
|
|
// -- 9-byte signatures --
|
|
|
|
if (check([0x49, 0x49, 0x52, 0x4F, 0x08, 0x00, 0x00, 0x00, 0x18])) {
|
|
return {
|
|
ext: 'orf',
|
|
mime: 'image/x-olympus-orf'
|
|
};
|
|
}
|
|
|
|
if (checkString('gimp xcf ')) {
|
|
return {
|
|
ext: 'xcf',
|
|
mime: 'image/x-xcf'
|
|
};
|
|
}
|
|
|
|
// -- 12-byte signatures --
|
|
|
|
if (check([0x49, 0x49, 0x55, 0x00, 0x18, 0x00, 0x00, 0x00, 0x88, 0xE7, 0x74, 0xD8])) {
|
|
return {
|
|
ext: 'rw2',
|
|
mime: 'image/x-panasonic-rw2'
|
|
};
|
|
}
|
|
|
|
// ASF_Header_Object first 80 bytes
|
|
if (check([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9])) {
|
|
async function readHeader() {
|
|
const guid = Buffer.alloc(16);
|
|
await tokenizer.readBuffer(guid);
|
|
return {
|
|
id: guid,
|
|
size: Number(await tokenizer.readToken(Token.UINT64_LE))
|
|
};
|
|
}
|
|
|
|
await tokenizer.ignore(30);
|
|
// Search for header should be in first 1KB of file.
|
|
while (tokenizer.position + 24 < tokenizer.fileInfo.size) {
|
|
const header = await readHeader();
|
|
let payload = header.size - 24;
|
|
if (_check(header.id, [0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65])) {
|
|
// Sync on Stream-Properties-Object (B7DC0791-A9B7-11CF-8EE6-00C00C205365)
|
|
const typeId = Buffer.alloc(16);
|
|
payload -= await tokenizer.readBuffer(typeId);
|
|
|
|
if (_check(typeId, [0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B])) {
|
|
// Found audio:
|
|
return {
|
|
ext: 'asf',
|
|
mime: 'audio/x-ms-asf'
|
|
};
|
|
}
|
|
|
|
if (_check(typeId, [0xC0, 0xEF, 0x19, 0xBC, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B])) {
|
|
// Found video:
|
|
return {
|
|
ext: 'asf',
|
|
mime: 'video/x-ms-asf'
|
|
};
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
await tokenizer.ignore(payload);
|
|
}
|
|
|
|
// Default to ASF generic extension
|
|
return {
|
|
ext: 'asf',
|
|
mime: 'application/vnd.ms-asf'
|
|
};
|
|
}
|
|
|
|
if (check([0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A])) {
|
|
return {
|
|
ext: 'ktx',
|
|
mime: 'image/ktx'
|
|
};
|
|
}
|
|
|
|
if ((check([0x7E, 0x10, 0x04]) || check([0x7E, 0x18, 0x04])) && check([0x30, 0x4D, 0x49, 0x45], {offset: 4})) {
|
|
return {
|
|
ext: 'mie',
|
|
mime: 'application/x-mie'
|
|
};
|
|
}
|
|
|
|
if (check([0x27, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], {offset: 2})) {
|
|
return {
|
|
ext: 'shp',
|
|
mime: 'application/x-esri-shape'
|
|
};
|
|
}
|
|
|
|
if (check([0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A])) {
|
|
// JPEG-2000 family
|
|
|
|
await tokenizer.ignore(20);
|
|
const type = await tokenizer.readToken(new Token.StringType(4, 'ascii'));
|
|
switch (type) {
|
|
case 'jp2 ':
|
|
return {
|
|
ext: 'jp2',
|
|
mime: 'image/jp2'
|
|
};
|
|
case 'jpx ':
|
|
return {
|
|
ext: 'jpx',
|
|
mime: 'image/jpx'
|
|
};
|
|
case 'jpm ':
|
|
return {
|
|
ext: 'jpm',
|
|
mime: 'image/jpm'
|
|
};
|
|
case 'mjp2':
|
|
return {
|
|
ext: 'mj2',
|
|
mime: 'image/mj2'
|
|
};
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (
|
|
check([0xFF, 0x0A]) ||
|
|
check([0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x4C, 0x20, 0x0D, 0x0A, 0x87, 0x0A])
|
|
) {
|
|
return {
|
|
ext: 'jxl',
|
|
mime: 'image/jxl'
|
|
};
|
|
}
|
|
|
|
// -- Unsafe signatures --
|
|
|
|
if (
|
|
check([0x0, 0x0, 0x1, 0xBA]) ||
|
|
check([0x0, 0x0, 0x1, 0xB3])
|
|
) {
|
|
return {
|
|
ext: 'mpg',
|
|
mime: 'video/mpeg'
|
|
};
|
|
}
|
|
|
|
if (check([0x00, 0x01, 0x00, 0x00, 0x00])) {
|
|
return {
|
|
ext: 'ttf',
|
|
mime: 'font/ttf'
|
|
};
|
|
}
|
|
|
|
if (check([0x00, 0x00, 0x01, 0x00])) {
|
|
return {
|
|
ext: 'ico',
|
|
mime: 'image/x-icon'
|
|
};
|
|
}
|
|
|
|
if (check([0x00, 0x00, 0x02, 0x00])) {
|
|
return {
|
|
ext: 'cur',
|
|
mime: 'image/x-icon'
|
|
};
|
|
}
|
|
|
|
if (check([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1])) {
|
|
// Detected Microsoft Compound File Binary File (MS-CFB) Format.
|
|
return {
|
|
ext: 'cfb',
|
|
mime: 'application/x-cfb'
|
|
};
|
|
}
|
|
|
|
// Increase sample size from 12 to 256.
|
|
await tokenizer.peekBuffer(buffer, {length: Math.min(256, tokenizer.fileInfo.size), mayBeLess: true});
|
|
|
|
// -- 15-byte signatures --
|
|
|
|
if (checkString('BEGIN:')) {
|
|
if (checkString('VCARD', {offset: 6})) {
|
|
return {
|
|
ext: 'vcf',
|
|
mime: 'text/vcard'
|
|
};
|
|
}
|
|
|
|
if (checkString('VCALENDAR', {offset: 6})) {
|
|
return {
|
|
ext: 'ics',
|
|
mime: 'text/calendar'
|
|
};
|
|
}
|
|
}
|
|
|
|
// `raf` is here just to keep all the raw image detectors together.
|
|
if (checkString('FUJIFILMCCD-RAW')) {
|
|
return {
|
|
ext: 'raf',
|
|
mime: 'image/x-fujifilm-raf'
|
|
};
|
|
}
|
|
|
|
if (checkString('Extended Module:')) {
|
|
return {
|
|
ext: 'xm',
|
|
mime: 'audio/x-xm'
|
|
};
|
|
}
|
|
|
|
if (checkString('Creative Voice File')) {
|
|
return {
|
|
ext: 'voc',
|
|
mime: 'audio/x-voc'
|
|
};
|
|
}
|
|
|
|
if (check([0x04, 0x00, 0x00, 0x00]) && buffer.length >= 16) { // Rough & quick check Pickle/ASAR
|
|
const jsonSize = buffer.readUInt32LE(12);
|
|
if (jsonSize > 12 && buffer.length >= jsonSize + 16) {
|
|
try {
|
|
const header = buffer.slice(16, jsonSize + 16).toString();
|
|
const json = JSON.parse(header);
|
|
// Check if Pickle is ASAR
|
|
if (json.files) { // Final check, assuring Pickle/ASAR format
|
|
return {
|
|
ext: 'asar',
|
|
mime: 'application/x-asar'
|
|
};
|
|
}
|
|
} catch (_) {
|
|
}
|
|
}
|
|
}
|
|
|
|
if (check([0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0x02])) {
|
|
return {
|
|
ext: 'mxf',
|
|
mime: 'application/mxf'
|
|
};
|
|
}
|
|
|
|
if (checkString('SCRM', {offset: 44})) {
|
|
return {
|
|
ext: 's3m',
|
|
mime: 'audio/x-s3m'
|
|
};
|
|
}
|
|
|
|
if (check([0x47], {offset: 4}) && (check([0x47], {offset: 192}) || check([0x47], {offset: 196}))) {
|
|
return {
|
|
ext: 'mts',
|
|
mime: 'video/mp2t'
|
|
};
|
|
}
|
|
|
|
if (check([0x42, 0x4F, 0x4F, 0x4B, 0x4D, 0x4F, 0x42, 0x49], {offset: 60})) {
|
|
return {
|
|
ext: 'mobi',
|
|
mime: 'application/x-mobipocket-ebook'
|
|
};
|
|
}
|
|
|
|
if (check([0x44, 0x49, 0x43, 0x4D], {offset: 128})) {
|
|
return {
|
|
ext: 'dcm',
|
|
mime: 'application/dicom'
|
|
};
|
|
}
|
|
|
|
if (check([0x4C, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46])) {
|
|
return {
|
|
ext: 'lnk',
|
|
mime: 'application/x.ms.shortcut' // Invented by us
|
|
};
|
|
}
|
|
|
|
if (check([0x62, 0x6F, 0x6F, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x72, 0x6B, 0x00, 0x00, 0x00, 0x00])) {
|
|
return {
|
|
ext: 'alias',
|
|
mime: 'application/x.apple.alias' // Invented by us
|
|
};
|
|
}
|
|
|
|
if (
|
|
check([0x4C, 0x50], {offset: 34}) &&
|
|
(
|
|
check([0x00, 0x00, 0x01], {offset: 8}) ||
|
|
check([0x01, 0x00, 0x02], {offset: 8}) ||
|
|
check([0x02, 0x00, 0x02], {offset: 8})
|
|
)
|
|
) {
|
|
return {
|
|
ext: 'eot',
|
|
mime: 'application/vnd.ms-fontobject'
|
|
};
|
|
}
|
|
|
|
if (check([0x06, 0x06, 0xED, 0xF5, 0xD8, 0x1D, 0x46, 0xE5, 0xBD, 0x31, 0xEF, 0xE7, 0xFE, 0x74, 0xB7, 0x1D])) {
|
|
return {
|
|
ext: 'indd',
|
|
mime: 'application/x-indesign'
|
|
};
|
|
}
|
|
|
|
// Increase sample size from 256 to 512
|
|
await tokenizer.peekBuffer(buffer, {length: Math.min(512, tokenizer.fileInfo.size), mayBeLess: true});
|
|
|
|
// Requires a buffer size of 512 bytes
|
|
if (tarHeaderChecksumMatches(buffer)) {
|
|
return {
|
|
ext: 'tar',
|
|
mime: 'application/x-tar'
|
|
};
|
|
}
|
|
|
|
if (check([0xFF, 0xFE, 0xFF, 0x0E, 0x53, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x74, 0x00, 0x63, 0x00, 0x68, 0x00, 0x55, 0x00, 0x70, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6C, 0x00])) {
|
|
return {
|
|
ext: 'skp',
|
|
mime: 'application/vnd.sketchup.skp'
|
|
};
|
|
}
|
|
|
|
if (checkString('-----BEGIN PGP MESSAGE-----')) {
|
|
return {
|
|
ext: 'pgp',
|
|
mime: 'application/pgp-encrypted'
|
|
};
|
|
}
|
|
|
|
// Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE)
|
|
if (buffer.length >= 2 && check([0xFF, 0xE0], {offset: 0, mask: [0xFF, 0xE0]})) {
|
|
if (check([0x10], {offset: 1, mask: [0x16]})) {
|
|
// Check for (ADTS) MPEG-2
|
|
if (check([0x08], {offset: 1, mask: [0x08]})) {
|
|
return {
|
|
ext: 'aac',
|
|
mime: 'audio/aac'
|
|
};
|
|
}
|
|
|
|
// Must be (ADTS) MPEG-4
|
|
return {
|
|
ext: 'aac',
|
|
mime: 'audio/aac'
|
|
};
|
|
}
|
|
|
|
// MPEG 1 or 2 Layer 3 header
|
|
// Check for MPEG layer 3
|
|
if (check([0x02], {offset: 1, mask: [0x06]})) {
|
|
return {
|
|
ext: 'mp3',
|
|
mime: 'audio/mpeg'
|
|
};
|
|
}
|
|
|
|
// Check for MPEG layer 2
|
|
if (check([0x04], {offset: 1, mask: [0x06]})) {
|
|
return {
|
|
ext: 'mp2',
|
|
mime: 'audio/mpeg'
|
|
};
|
|
}
|
|
|
|
// Check for MPEG layer 1
|
|
if (check([0x06], {offset: 1, mask: [0x06]})) {
|
|
return {
|
|
ext: 'mp1',
|
|
mime: 'audio/mpeg'
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
const stream = readableStream => new Promise((resolve, reject) => {
|
|
// Using `eval` to work around issues when bundling with Webpack
|
|
const stream = eval('require')('stream'); // eslint-disable-line no-eval
|
|
|
|
readableStream.on('error', reject);
|
|
readableStream.once('readable', async () => {
|
|
// Set up output stream
|
|
const pass = new stream.PassThrough();
|
|
let outputStream;
|
|
if (stream.pipeline) {
|
|
outputStream = stream.pipeline(readableStream, pass, () => {
|
|
});
|
|
} else {
|
|
outputStream = readableStream.pipe(pass);
|
|
}
|
|
|
|
// Read the input stream and detect the filetype
|
|
const chunk = readableStream.read(minimumBytes) || readableStream.read() || Buffer.alloc(0);
|
|
try {
|
|
const fileType = await fromBuffer(chunk);
|
|
pass.fileType = fileType;
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
|
|
resolve(outputStream);
|
|
});
|
|
});
|
|
|
|
const fileType$1 = {
|
|
fromStream,
|
|
fromTokenizer,
|
|
fromBuffer,
|
|
stream
|
|
};
|
|
|
|
Object.defineProperty(fileType$1, 'extensions', {
|
|
get() {
|
|
return new Set(supported.extensions);
|
|
}
|
|
});
|
|
|
|
Object.defineProperty(fileType$1, 'mimeTypes', {
|
|
get() {
|
|
return new Set(supported.mimeTypes);
|
|
}
|
|
});
|
|
|
|
var core$2 = fileType$1;
|
|
|
|
const strtok3 = lib$3;
|
|
const core$1 = core$2;
|
|
|
|
async function fromFile(path) {
|
|
const tokenizer = await strtok3.fromFile(path);
|
|
try {
|
|
return await core$1.fromTokenizer(tokenizer);
|
|
} finally {
|
|
await tokenizer.close();
|
|
}
|
|
}
|
|
|
|
const fileType = {
|
|
fromFile
|
|
};
|
|
|
|
Object.assign(fileType, core$1);
|
|
|
|
Object.defineProperty(fileType, 'extensions', {
|
|
get() {
|
|
return core$1.extensions;
|
|
}
|
|
});
|
|
|
|
Object.defineProperty(fileType, 'mimeTypes', {
|
|
get() {
|
|
return core$1.mimeTypes;
|
|
}
|
|
});
|
|
|
|
var fileType_1 = fileType;
|
|
|
|
var isSvg$2 = {exports: {}};
|
|
|
|
var validator$2 = {};
|
|
|
|
var util$3 = {};
|
|
|
|
(function (exports) {
|
|
|
|
const nameStartChar = ':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
|
|
const nameChar = nameStartChar + '\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
|
|
const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*';
|
|
const regexName = new RegExp('^' + nameRegexp + '$');
|
|
|
|
const getAllMatches = function(string, regex) {
|
|
const matches = [];
|
|
let match = regex.exec(string);
|
|
while (match) {
|
|
const allmatches = [];
|
|
allmatches.startIndex = regex.lastIndex - match[0].length;
|
|
const len = match.length;
|
|
for (let index = 0; index < len; index++) {
|
|
allmatches.push(match[index]);
|
|
}
|
|
matches.push(allmatches);
|
|
match = regex.exec(string);
|
|
}
|
|
return matches;
|
|
};
|
|
|
|
const isName = function(string) {
|
|
const match = regexName.exec(string);
|
|
return !(match === null || typeof match === 'undefined');
|
|
};
|
|
|
|
exports.isExist = function(v) {
|
|
return typeof v !== 'undefined';
|
|
};
|
|
|
|
exports.isEmptyObject = function(obj) {
|
|
return Object.keys(obj).length === 0;
|
|
};
|
|
|
|
/**
|
|
* Copy all the properties of a into b.
|
|
* @param {*} target
|
|
* @param {*} a
|
|
*/
|
|
exports.merge = function(target, a, arrayMode) {
|
|
if (a) {
|
|
const keys = Object.keys(a); // will return an array of own properties
|
|
const len = keys.length; //don't make it inline
|
|
for (let i = 0; i < len; i++) {
|
|
if (arrayMode === 'strict') {
|
|
target[keys[i]] = [ a[keys[i]] ];
|
|
} else {
|
|
target[keys[i]] = a[keys[i]];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* exports.merge =function (b,a){
|
|
return Object.assign(b,a);
|
|
} */
|
|
|
|
exports.getValue = function(v) {
|
|
if (exports.isExist(v)) {
|
|
return v;
|
|
} else {
|
|
return '';
|
|
}
|
|
};
|
|
|
|
// const fakeCall = function(a) {return a;};
|
|
// const fakeCallNoReturn = function() {};
|
|
|
|
exports.isName = isName;
|
|
exports.getAllMatches = getAllMatches;
|
|
exports.nameRegexp = nameRegexp;
|
|
} (util$3));
|
|
|
|
const util$2 = util$3;
|
|
|
|
const defaultOptions$2 = {
|
|
allowBooleanAttributes: false, //A tag can have attributes without any value
|
|
unpairedTags: []
|
|
};
|
|
|
|
//const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
|
|
validator$2.validate = function (xmlData, options) {
|
|
options = Object.assign({}, defaultOptions$2, options);
|
|
|
|
//xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
|
|
//xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
|
|
//xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
|
|
const tags = [];
|
|
let tagFound = false;
|
|
|
|
//indicates that the root tag has been closed (aka. depth 0 has been reached)
|
|
let reachedRoot = false;
|
|
|
|
if (xmlData[0] === '\ufeff') {
|
|
// check for byte order mark (BOM)
|
|
xmlData = xmlData.substr(1);
|
|
}
|
|
|
|
for (let i = 0; i < xmlData.length; i++) {
|
|
|
|
if (xmlData[i] === '<' && xmlData[i+1] === '?') {
|
|
i+=2;
|
|
i = readPI(xmlData,i);
|
|
if (i.err) return i;
|
|
}else if (xmlData[i] === '<') {
|
|
//starting of tag
|
|
//read until you reach to '>' avoiding any '>' in attribute value
|
|
let tagStartPos = i;
|
|
i++;
|
|
|
|
if (xmlData[i] === '!') {
|
|
i = readCommentAndCDATA(xmlData, i);
|
|
continue;
|
|
} else {
|
|
let closingTag = false;
|
|
if (xmlData[i] === '/') {
|
|
//closing tag
|
|
closingTag = true;
|
|
i++;
|
|
}
|
|
//read tagname
|
|
let tagName = '';
|
|
for (; i < xmlData.length &&
|
|
xmlData[i] !== '>' &&
|
|
xmlData[i] !== ' ' &&
|
|
xmlData[i] !== '\t' &&
|
|
xmlData[i] !== '\n' &&
|
|
xmlData[i] !== '\r'; i++
|
|
) {
|
|
tagName += xmlData[i];
|
|
}
|
|
tagName = tagName.trim();
|
|
//console.log(tagName);
|
|
|
|
if (tagName[tagName.length - 1] === '/') {
|
|
//self closing tag without attributes
|
|
tagName = tagName.substring(0, tagName.length - 1);
|
|
//continue;
|
|
i--;
|
|
}
|
|
if (!validateTagName(tagName)) {
|
|
let msg;
|
|
if (tagName.trim().length === 0) {
|
|
msg = "Invalid space after '<'.";
|
|
} else {
|
|
msg = "Tag '"+tagName+"' is an invalid name.";
|
|
}
|
|
return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));
|
|
}
|
|
|
|
const result = readAttributeStr(xmlData, i);
|
|
if (result === false) {
|
|
return getErrorObject('InvalidAttr', "Attributes for '"+tagName+"' have open quote.", getLineNumberForPosition(xmlData, i));
|
|
}
|
|
let attrStr = result.value;
|
|
i = result.index;
|
|
|
|
if (attrStr[attrStr.length - 1] === '/') {
|
|
//self closing tag
|
|
const attrStrStart = i - attrStr.length;
|
|
attrStr = attrStr.substring(0, attrStr.length - 1);
|
|
const isValid = validateAttributeString(attrStr, options);
|
|
if (isValid === true) {
|
|
tagFound = true;
|
|
//continue; //text may presents after self closing tag
|
|
} else {
|
|
//the result from the nested function returns the position of the error within the attribute
|
|
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
|
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
|
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
|
|
}
|
|
} else if (closingTag) {
|
|
if (!result.tagClosed) {
|
|
return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
|
|
} else if (attrStr.trim().length > 0) {
|
|
return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
} else if (tags.length === 0) {
|
|
return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
} else {
|
|
const otg = tags.pop();
|
|
if (tagName !== otg.tagName) {
|
|
let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
|
|
return getErrorObject('InvalidTag',
|
|
"Expected closing tag '"+otg.tagName+"' (opened in line "+openPos.line+", col "+openPos.col+") instead of closing tag '"+tagName+"'.",
|
|
getLineNumberForPosition(xmlData, tagStartPos));
|
|
}
|
|
|
|
//when there are no more tags, we reached the root level.
|
|
if (tags.length == 0) {
|
|
reachedRoot = true;
|
|
}
|
|
}
|
|
} else {
|
|
const isValid = validateAttributeString(attrStr, options);
|
|
if (isValid !== true) {
|
|
//the result from the nested function returns the position of the error within the attribute
|
|
//in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
|
|
//this gives us the absolute index in the entire xml, which we can use to find the line at last
|
|
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
|
|
}
|
|
|
|
//if the root level has been reached before ...
|
|
if (reachedRoot === true) {
|
|
return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
|
|
} else if(options.unpairedTags.indexOf(tagName) !== -1); else {
|
|
tags.push({tagName, tagStartPos});
|
|
}
|
|
tagFound = true;
|
|
}
|
|
|
|
//skip tag text value
|
|
//It may include comments and CDATA value
|
|
for (i++; i < xmlData.length; i++) {
|
|
if (xmlData[i] === '<') {
|
|
if (xmlData[i + 1] === '!') {
|
|
//comment or CADATA
|
|
i++;
|
|
i = readCommentAndCDATA(xmlData, i);
|
|
continue;
|
|
} else if (xmlData[i+1] === '?') {
|
|
i = readPI(xmlData, ++i);
|
|
if (i.err) return i;
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (xmlData[i] === '&') {
|
|
const afterAmp = validateAmpersand(xmlData, i);
|
|
if (afterAmp == -1)
|
|
return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
i = afterAmp;
|
|
}else {
|
|
if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
|
|
return getErrorObject('InvalidXml', "Extra text at the end", getLineNumberForPosition(xmlData, i));
|
|
}
|
|
}
|
|
} //end of reading tag text value
|
|
if (xmlData[i] === '<') {
|
|
i--;
|
|
}
|
|
}
|
|
} else {
|
|
if ( isWhiteSpace(xmlData[i])) {
|
|
continue;
|
|
}
|
|
return getErrorObject('InvalidChar', "char '"+xmlData[i]+"' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
}
|
|
}
|
|
|
|
if (!tagFound) {
|
|
return getErrorObject('InvalidXml', 'Start tag expected.', 1);
|
|
}else if (tags.length == 1) {
|
|
return getErrorObject('InvalidTag', "Unclosed tag '"+tags[0].tagName+"'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
|
|
}else if (tags.length > 0) {
|
|
return getErrorObject('InvalidXml', "Invalid '"+
|
|
JSON.stringify(tags.map(t => t.tagName), null, 4).replace(/\r?\n/g, '')+
|
|
"' found.", {line: 1, col: 1});
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
function isWhiteSpace(char){
|
|
return char === ' ' || char === '\t' || char === '\n' || char === '\r';
|
|
}
|
|
/**
|
|
* Read Processing insstructions and skip
|
|
* @param {*} xmlData
|
|
* @param {*} i
|
|
*/
|
|
function readPI(xmlData, i) {
|
|
const start = i;
|
|
for (; i < xmlData.length; i++) {
|
|
if (xmlData[i] == '?' || xmlData[i] == ' ') {
|
|
//tagname
|
|
const tagname = xmlData.substr(start, i - start);
|
|
if (i > 5 && tagname === 'xml') {
|
|
return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));
|
|
} else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {
|
|
//check if valid attribut string
|
|
i++;
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
function readCommentAndCDATA(xmlData, i) {
|
|
if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {
|
|
//comment
|
|
for (i += 3; i < xmlData.length; i++) {
|
|
if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {
|
|
i += 2;
|
|
break;
|
|
}
|
|
}
|
|
} else if (
|
|
xmlData.length > i + 8 &&
|
|
xmlData[i + 1] === 'D' &&
|
|
xmlData[i + 2] === 'O' &&
|
|
xmlData[i + 3] === 'C' &&
|
|
xmlData[i + 4] === 'T' &&
|
|
xmlData[i + 5] === 'Y' &&
|
|
xmlData[i + 6] === 'P' &&
|
|
xmlData[i + 7] === 'E'
|
|
) {
|
|
let angleBracketsCount = 1;
|
|
for (i += 8; i < xmlData.length; i++) {
|
|
if (xmlData[i] === '<') {
|
|
angleBracketsCount++;
|
|
} else if (xmlData[i] === '>') {
|
|
angleBracketsCount--;
|
|
if (angleBracketsCount === 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (
|
|
xmlData.length > i + 9 &&
|
|
xmlData[i + 1] === '[' &&
|
|
xmlData[i + 2] === 'C' &&
|
|
xmlData[i + 3] === 'D' &&
|
|
xmlData[i + 4] === 'A' &&
|
|
xmlData[i + 5] === 'T' &&
|
|
xmlData[i + 6] === 'A' &&
|
|
xmlData[i + 7] === '['
|
|
) {
|
|
for (i += 8; i < xmlData.length; i++) {
|
|
if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
|
|
i += 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
const doubleQuote = '"';
|
|
const singleQuote = "'";
|
|
|
|
/**
|
|
* Keep reading xmlData until '<' is found outside the attribute value.
|
|
* @param {string} xmlData
|
|
* @param {number} i
|
|
*/
|
|
function readAttributeStr(xmlData, i) {
|
|
let attrStr = '';
|
|
let startChar = '';
|
|
let tagClosed = false;
|
|
for (; i < xmlData.length; i++) {
|
|
if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
|
|
if (startChar === '') {
|
|
startChar = xmlData[i];
|
|
} else if (startChar !== xmlData[i]) ; else {
|
|
startChar = '';
|
|
}
|
|
} else if (xmlData[i] === '>') {
|
|
if (startChar === '') {
|
|
tagClosed = true;
|
|
break;
|
|
}
|
|
}
|
|
attrStr += xmlData[i];
|
|
}
|
|
if (startChar !== '') {
|
|
return false;
|
|
}
|
|
|
|
return {
|
|
value: attrStr,
|
|
index: i,
|
|
tagClosed: tagClosed
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Select all the attributes whether valid or invalid.
|
|
*/
|
|
const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g');
|
|
|
|
//attr, ="sd", a="amit's", a="sd"b="saf", ab cd=""
|
|
|
|
function validateAttributeString(attrStr, options) {
|
|
//console.log("start:"+attrStr+":end");
|
|
|
|
//if(attrStr.trim().length === 0) return true; //empty string
|
|
|
|
const matches = util$2.getAllMatches(attrStr, validAttrStrRegxp);
|
|
const attrNames = {};
|
|
|
|
for (let i = 0; i < matches.length; i++) {
|
|
if (matches[i][1].length === 0) {
|
|
//nospace before attribute name: a="sd"b="saf"
|
|
return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' has no space in starting.", getPositionFromMatch(matches[i]))
|
|
} else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
|
|
return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' is without value.", getPositionFromMatch(matches[i]));
|
|
} else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
|
|
//independent attribute: ab
|
|
return getErrorObject('InvalidAttr', "boolean attribute '"+matches[i][2]+"' is not allowed.", getPositionFromMatch(matches[i]));
|
|
}
|
|
/* else if(matches[i][6] === undefined){//attribute without value: ab=
|
|
return { err: { code:"InvalidAttr",msg:"attribute " + matches[i][2] + " has no value assigned."}};
|
|
} */
|
|
const attrName = matches[i][2];
|
|
if (!validateAttrName(attrName)) {
|
|
return getErrorObject('InvalidAttr', "Attribute '"+attrName+"' is an invalid name.", getPositionFromMatch(matches[i]));
|
|
}
|
|
if (!attrNames.hasOwnProperty(attrName)) {
|
|
//check for duplicate attribute.
|
|
attrNames[attrName] = 1;
|
|
} else {
|
|
return getErrorObject('InvalidAttr', "Attribute '"+attrName+"' is repeated.", getPositionFromMatch(matches[i]));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function validateNumberAmpersand(xmlData, i) {
|
|
let re = /\d/;
|
|
if (xmlData[i] === 'x') {
|
|
i++;
|
|
re = /[\da-fA-F]/;
|
|
}
|
|
for (; i < xmlData.length; i++) {
|
|
if (xmlData[i] === ';')
|
|
return i;
|
|
if (!xmlData[i].match(re))
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
function validateAmpersand(xmlData, i) {
|
|
// https://www.w3.org/TR/xml/#dt-charref
|
|
i++;
|
|
if (xmlData[i] === ';')
|
|
return -1;
|
|
if (xmlData[i] === '#') {
|
|
i++;
|
|
return validateNumberAmpersand(xmlData, i);
|
|
}
|
|
let count = 0;
|
|
for (; i < xmlData.length; i++, count++) {
|
|
if (xmlData[i].match(/\w/) && count < 20)
|
|
continue;
|
|
if (xmlData[i] === ';')
|
|
break;
|
|
return -1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
function getErrorObject(code, message, lineNumber) {
|
|
return {
|
|
err: {
|
|
code: code,
|
|
msg: message,
|
|
line: lineNumber.line || lineNumber,
|
|
col: lineNumber.col,
|
|
},
|
|
};
|
|
}
|
|
|
|
function validateAttrName(attrName) {
|
|
return util$2.isName(attrName);
|
|
}
|
|
|
|
// const startsWithXML = /^xml/i;
|
|
|
|
function validateTagName(tagname) {
|
|
return util$2.isName(tagname) /* && !tagname.match(startsWithXML) */;
|
|
}
|
|
|
|
//this function returns the line number for the character at the given index
|
|
function getLineNumberForPosition(xmlData, index) {
|
|
const lines = xmlData.substring(0, index).split(/\r?\n/);
|
|
return {
|
|
line: lines.length,
|
|
|
|
// column number is last line's length + 1, because column numbering starts at 1:
|
|
col: lines[lines.length - 1].length + 1
|
|
};
|
|
}
|
|
|
|
//this function returns the position of the first character of match within attrStr
|
|
function getPositionFromMatch(match) {
|
|
return match.startIndex + match[1].length;
|
|
}
|
|
|
|
var OptionsBuilder = {};
|
|
|
|
const defaultOptions$1 = {
|
|
preserveOrder: false,
|
|
attributeNamePrefix: '@_',
|
|
attributesGroupName: false,
|
|
textNodeName: '#text',
|
|
ignoreAttributes: true,
|
|
removeNSPrefix: false, // remove NS from tag name or attribute name if true
|
|
allowBooleanAttributes: false, //a tag can have attributes without any value
|
|
//ignoreRootElement : false,
|
|
parseTagValue: true,
|
|
parseAttributeValue: false,
|
|
trimValues: true, //Trim string values of tag and attributes
|
|
cdataPropName: false,
|
|
numberParseOptions: {
|
|
hex: true,
|
|
leadingZeros: true,
|
|
eNotation: true
|
|
},
|
|
tagValueProcessor: function(tagName, val) {
|
|
return val;
|
|
},
|
|
attributeValueProcessor: function(attrName, val) {
|
|
return val;
|
|
},
|
|
stopNodes: [], //nested tags will not be parsed even for errors
|
|
alwaysCreateTextNode: false,
|
|
isArray: () => false,
|
|
commentPropName: false,
|
|
unpairedTags: [],
|
|
processEntities: true,
|
|
htmlEntities: false,
|
|
ignoreDeclaration: false,
|
|
ignorePiTags: false,
|
|
transformTagName: false,
|
|
transformAttributeName: false,
|
|
updateTag: function(tagName, jPath, attrs){
|
|
return tagName
|
|
},
|
|
// skipEmptyListItem: false
|
|
};
|
|
|
|
const buildOptions$1 = function(options) {
|
|
return Object.assign({}, defaultOptions$1, options);
|
|
};
|
|
|
|
OptionsBuilder.buildOptions = buildOptions$1;
|
|
OptionsBuilder.defaultOptions = defaultOptions$1;
|
|
|
|
class XmlNode{
|
|
constructor(tagname) {
|
|
this.tagname = tagname;
|
|
this.child = []; //nested tags, text, cdata, comments in order
|
|
this[":@"] = {}; //attributes map
|
|
}
|
|
add(key,val){
|
|
// this.child.push( {name : key, val: val, isCdata: isCdata });
|
|
if(key === "__proto__") key = "#__proto__";
|
|
this.child.push( {[key]: val });
|
|
}
|
|
addChild(node) {
|
|
if(node.tagname === "__proto__") node.tagname = "#__proto__";
|
|
if(node[":@"] && Object.keys(node[":@"]).length > 0){
|
|
this.child.push( { [node.tagname]: node.child, [":@"]: node[":@"] });
|
|
}else {
|
|
this.child.push( { [node.tagname]: node.child });
|
|
}
|
|
};
|
|
}
|
|
|
|
var xmlNode$1 = XmlNode;
|
|
|
|
const util$1 = util$3;
|
|
|
|
//TODO: handle comments
|
|
function readDocType$1(xmlData, i){
|
|
|
|
const entities = {};
|
|
if( xmlData[i + 3] === 'O' &&
|
|
xmlData[i + 4] === 'C' &&
|
|
xmlData[i + 5] === 'T' &&
|
|
xmlData[i + 6] === 'Y' &&
|
|
xmlData[i + 7] === 'P' &&
|
|
xmlData[i + 8] === 'E')
|
|
{
|
|
i = i+9;
|
|
let angleBracketsCount = 1;
|
|
let hasBody = false, comment = false;
|
|
let exp = "";
|
|
for(;i<xmlData.length;i++){
|
|
if (xmlData[i] === '<' && !comment) { //Determine the tag type
|
|
if( hasBody && isEntity(xmlData, i)){
|
|
i += 7;
|
|
let entityName, val;
|
|
[entityName, val,i] = readEntityExp(xmlData,i+1);
|
|
if(val.indexOf("&") === -1) //Parameter entities are not supported
|
|
entities[ validateEntityName(entityName) ] = {
|
|
regx : RegExp( `&${entityName};`,"g"),
|
|
val: val
|
|
};
|
|
}
|
|
else if( hasBody && isElement(xmlData, i)) i += 8;//Not supported
|
|
else if( hasBody && isAttlist(xmlData, i)) i += 8;//Not supported
|
|
else if( hasBody && isNotation(xmlData, i)) i += 9;//Not supported
|
|
else if( isComment) comment = true;
|
|
else throw new Error("Invalid DOCTYPE");
|
|
|
|
angleBracketsCount++;
|
|
exp = "";
|
|
} else if (xmlData[i] === '>') { //Read tag content
|
|
if(comment){
|
|
if( xmlData[i - 1] === "-" && xmlData[i - 2] === "-"){
|
|
comment = false;
|
|
angleBracketsCount--;
|
|
}
|
|
}else {
|
|
angleBracketsCount--;
|
|
}
|
|
if (angleBracketsCount === 0) {
|
|
break;
|
|
}
|
|
}else if( xmlData[i] === '['){
|
|
hasBody = true;
|
|
}else {
|
|
exp += xmlData[i];
|
|
}
|
|
}
|
|
if(angleBracketsCount !== 0){
|
|
throw new Error(`Unclosed DOCTYPE`);
|
|
}
|
|
}else {
|
|
throw new Error(`Invalid Tag instead of DOCTYPE`);
|
|
}
|
|
return {entities, i};
|
|
}
|
|
|
|
function readEntityExp(xmlData,i){
|
|
//External entities are not supported
|
|
// <!ENTITY ext SYSTEM "http://normal-website.com" >
|
|
|
|
//Parameter entities are not supported
|
|
// <!ENTITY entityname "&anotherElement;">
|
|
|
|
//Internal entities are supported
|
|
// <!ENTITY entityname "replacement text">
|
|
|
|
//read EntityName
|
|
let entityName = "";
|
|
for (; i < xmlData.length && (xmlData[i] !== "'" && xmlData[i] !== '"' ); i++) {
|
|
// if(xmlData[i] === " ") continue;
|
|
// else
|
|
entityName += xmlData[i];
|
|
}
|
|
entityName = entityName.trim();
|
|
if(entityName.indexOf(" ") !== -1) throw new Error("External entites are not supported");
|
|
|
|
//read Entity Value
|
|
const startChar = xmlData[i++];
|
|
let val = "";
|
|
for (; i < xmlData.length && xmlData[i] !== startChar ; i++) {
|
|
val += xmlData[i];
|
|
}
|
|
return [entityName, val, i];
|
|
}
|
|
|
|
function isComment(xmlData, i){
|
|
if(xmlData[i+1] === '!' &&
|
|
xmlData[i+2] === '-' &&
|
|
xmlData[i+3] === '-') return true
|
|
return false
|
|
}
|
|
function isEntity(xmlData, i){
|
|
if(xmlData[i+1] === '!' &&
|
|
xmlData[i+2] === 'E' &&
|
|
xmlData[i+3] === 'N' &&
|
|
xmlData[i+4] === 'T' &&
|
|
xmlData[i+5] === 'I' &&
|
|
xmlData[i+6] === 'T' &&
|
|
xmlData[i+7] === 'Y') return true
|
|
return false
|
|
}
|
|
function isElement(xmlData, i){
|
|
if(xmlData[i+1] === '!' &&
|
|
xmlData[i+2] === 'E' &&
|
|
xmlData[i+3] === 'L' &&
|
|
xmlData[i+4] === 'E' &&
|
|
xmlData[i+5] === 'M' &&
|
|
xmlData[i+6] === 'E' &&
|
|
xmlData[i+7] === 'N' &&
|
|
xmlData[i+8] === 'T') return true
|
|
return false
|
|
}
|
|
|
|
function isAttlist(xmlData, i){
|
|
if(xmlData[i+1] === '!' &&
|
|
xmlData[i+2] === 'A' &&
|
|
xmlData[i+3] === 'T' &&
|
|
xmlData[i+4] === 'T' &&
|
|
xmlData[i+5] === 'L' &&
|
|
xmlData[i+6] === 'I' &&
|
|
xmlData[i+7] === 'S' &&
|
|
xmlData[i+8] === 'T') return true
|
|
return false
|
|
}
|
|
function isNotation(xmlData, i){
|
|
if(xmlData[i+1] === '!' &&
|
|
xmlData[i+2] === 'N' &&
|
|
xmlData[i+3] === 'O' &&
|
|
xmlData[i+4] === 'T' &&
|
|
xmlData[i+5] === 'A' &&
|
|
xmlData[i+6] === 'T' &&
|
|
xmlData[i+7] === 'I' &&
|
|
xmlData[i+8] === 'O' &&
|
|
xmlData[i+9] === 'N') return true
|
|
return false
|
|
}
|
|
|
|
function validateEntityName(name){
|
|
if (util$1.isName(name))
|
|
return name;
|
|
else
|
|
throw new Error(`Invalid entity name ${name}`);
|
|
}
|
|
|
|
var DocTypeReader = readDocType$1;
|
|
|
|
const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;
|
|
const numRegex = /^([\-\+])?(0*)(\.[0-9]+([eE]\-?[0-9]+)?|[0-9]+(\.[0-9]+([eE]\-?[0-9]+)?)?)$/;
|
|
// const octRegex = /0x[a-z0-9]+/;
|
|
// const binRegex = /0x[a-z0-9]+/;
|
|
|
|
|
|
//polyfill
|
|
if (!Number.parseInt && window.parseInt) {
|
|
Number.parseInt = window.parseInt;
|
|
}
|
|
if (!Number.parseFloat && window.parseFloat) {
|
|
Number.parseFloat = window.parseFloat;
|
|
}
|
|
|
|
|
|
const consider = {
|
|
hex : true,
|
|
leadingZeros: true,
|
|
decimalPoint: "\.",
|
|
eNotation: true
|
|
//skipLike: /regex/
|
|
};
|
|
|
|
function toNumber$1(str, options = {}){
|
|
// const options = Object.assign({}, consider);
|
|
// if(opt.leadingZeros === false){
|
|
// options.leadingZeros = false;
|
|
// }else if(opt.hex === false){
|
|
// options.hex = false;
|
|
// }
|
|
|
|
options = Object.assign({}, consider, options );
|
|
if(!str || typeof str !== "string" ) return str;
|
|
|
|
let trimmedStr = str.trim();
|
|
// if(trimmedStr === "0.0") return 0;
|
|
// else if(trimmedStr === "+0.0") return 0;
|
|
// else if(trimmedStr === "-0.0") return -0;
|
|
|
|
if(options.skipLike !== undefined && options.skipLike.test(trimmedStr)) return str;
|
|
else if (options.hex && hexRegex.test(trimmedStr)) {
|
|
return Number.parseInt(trimmedStr, 16);
|
|
// } else if (options.parseOct && octRegex.test(str)) {
|
|
// return Number.parseInt(val, 8);
|
|
// }else if (options.parseBin && binRegex.test(str)) {
|
|
// return Number.parseInt(val, 2);
|
|
}else {
|
|
//separate negative sign, leading zeros, and rest number
|
|
const match = numRegex.exec(trimmedStr);
|
|
if(match){
|
|
const sign = match[1];
|
|
const leadingZeros = match[2];
|
|
let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros
|
|
//trim ending zeros for floating number
|
|
|
|
const eNotation = match[4] || match[6];
|
|
if(!options.leadingZeros && leadingZeros.length > 0 && sign && trimmedStr[2] !== ".") return str; //-0123
|
|
else if(!options.leadingZeros && leadingZeros.length > 0 && !sign && trimmedStr[1] !== ".") return str; //0123
|
|
else {//no leading zeros or leading zeros are allowed
|
|
const num = Number(trimmedStr);
|
|
const numStr = "" + num;
|
|
if(numStr.search(/[eE]/) !== -1){ //given number is long and parsed to eNotation
|
|
if(options.eNotation) return num;
|
|
else return str;
|
|
}else if(eNotation){ //given number has enotation
|
|
if(options.eNotation) return num;
|
|
else return str;
|
|
}else if(trimmedStr.indexOf(".") !== -1){ //floating number
|
|
// const decimalPart = match[5].substr(1);
|
|
// const intPart = trimmedStr.substr(0,trimmedStr.indexOf("."));
|
|
|
|
|
|
// const p = numStr.indexOf(".");
|
|
// const givenIntPart = numStr.substr(0,p);
|
|
// const givenDecPart = numStr.substr(p+1);
|
|
if(numStr === "0" && (numTrimmedByZeros === "") ) return num; //0.0
|
|
else if(numStr === numTrimmedByZeros) return num; //0.456. 0.79000
|
|
else if( sign && numStr === "-"+numTrimmedByZeros) return num;
|
|
else return str;
|
|
}
|
|
|
|
if(leadingZeros){
|
|
// if(numTrimmedByZeros === numStr){
|
|
// if(options.leadingZeros) return num;
|
|
// else return str;
|
|
// }else return str;
|
|
if(numTrimmedByZeros === numStr) return num;
|
|
else if(sign+numTrimmedByZeros === numStr) return num;
|
|
else return str;
|
|
}
|
|
|
|
if(trimmedStr === numStr) return num;
|
|
else if(trimmedStr === sign+numStr) return num;
|
|
// else{
|
|
// //number with +/- sign
|
|
// trimmedStr.test(/[-+][0-9]);
|
|
|
|
// }
|
|
return str;
|
|
}
|
|
// else if(!eNotation && trimmedStr && trimmedStr !== Number(trimmedStr) ) return str;
|
|
|
|
}else { //non-numeric string
|
|
return str;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} numStr without leading zeros
|
|
* @returns
|
|
*/
|
|
function trimZeros(numStr){
|
|
if(numStr && numStr.indexOf(".") !== -1){//float
|
|
numStr = numStr.replace(/0+$/, ""); //remove ending zeros
|
|
if(numStr === ".") numStr = "0";
|
|
else if(numStr[0] === ".") numStr = "0"+numStr;
|
|
else if(numStr[numStr.length-1] === ".") numStr = numStr.substr(0,numStr.length-1);
|
|
return numStr;
|
|
}
|
|
return numStr;
|
|
}
|
|
var strnum = toNumber$1;
|
|
|
|
function getIgnoreAttributesFn$2(ignoreAttributes) {
|
|
if (typeof ignoreAttributes === 'function') {
|
|
return ignoreAttributes
|
|
}
|
|
if (Array.isArray(ignoreAttributes)) {
|
|
return (attrName) => {
|
|
for (const pattern of ignoreAttributes) {
|
|
if (typeof pattern === 'string' && attrName === pattern) {
|
|
return true
|
|
}
|
|
if (pattern instanceof RegExp && pattern.test(attrName)) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return () => false
|
|
}
|
|
|
|
var ignoreAttributes = getIgnoreAttributesFn$2;
|
|
|
|
///@ts-check
|
|
|
|
const util = util$3;
|
|
const xmlNode = xmlNode$1;
|
|
const readDocType = DocTypeReader;
|
|
const toNumber = strnum;
|
|
const getIgnoreAttributesFn$1 = ignoreAttributes;
|
|
|
|
// const regx =
|
|
// '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
|
|
// .replace(/NAME/g, util.nameRegexp);
|
|
|
|
//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
|
|
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
|
|
|
|
class OrderedObjParser$1{
|
|
constructor(options){
|
|
this.options = options;
|
|
this.currentNode = null;
|
|
this.tagsNodeStack = [];
|
|
this.docTypeEntities = {};
|
|
this.lastEntities = {
|
|
"apos" : { regex: /&(apos|#39|#x27);/g, val : "'"},
|
|
"gt" : { regex: /&(gt|#62|#x3E);/g, val : ">"},
|
|
"lt" : { regex: /&(lt|#60|#x3C);/g, val : "<"},
|
|
"quot" : { regex: /&(quot|#34|#x22);/g, val : "\""},
|
|
};
|
|
this.ampEntity = { regex: /&(amp|#38|#x26);/g, val : "&"};
|
|
this.htmlEntities = {
|
|
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
|
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
|
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
|
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
|
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
|
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
|
"cent" : { regex: /&(cent|#162);/g, val: "¢" },
|
|
"pound" : { regex: /&(pound|#163);/g, val: "£" },
|
|
"yen" : { regex: /&(yen|#165);/g, val: "¥" },
|
|
"euro" : { regex: /&(euro|#8364);/g, val: "€" },
|
|
"copyright" : { regex: /&(copy|#169);/g, val: "©" },
|
|
"reg" : { regex: /&(reg|#174);/g, val: "®" },
|
|
"inr" : { regex: /&(inr|#8377);/g, val: "₹" },
|
|
"num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 10)) },
|
|
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 16)) },
|
|
};
|
|
this.addExternalEntities = addExternalEntities;
|
|
this.parseXml = parseXml;
|
|
this.parseTextData = parseTextData;
|
|
this.resolveNameSpace = resolveNameSpace;
|
|
this.buildAttributesMap = buildAttributesMap;
|
|
this.isItStopNode = isItStopNode;
|
|
this.replaceEntitiesValue = replaceEntitiesValue$1;
|
|
this.readStopNodeData = readStopNodeData;
|
|
this.saveTextToParentTag = saveTextToParentTag;
|
|
this.addChild = addChild;
|
|
this.ignoreAttributesFn = getIgnoreAttributesFn$1(this.options.ignoreAttributes);
|
|
}
|
|
|
|
}
|
|
|
|
function addExternalEntities(externalEntities){
|
|
const entKeys = Object.keys(externalEntities);
|
|
for (let i = 0; i < entKeys.length; i++) {
|
|
const ent = entKeys[i];
|
|
this.lastEntities[ent] = {
|
|
regex: new RegExp("&"+ent+";","g"),
|
|
val : externalEntities[ent]
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} val
|
|
* @param {string} tagName
|
|
* @param {string} jPath
|
|
* @param {boolean} dontTrim
|
|
* @param {boolean} hasAttributes
|
|
* @param {boolean} isLeafNode
|
|
* @param {boolean} escapeEntities
|
|
*/
|
|
function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
|
|
if (val !== undefined) {
|
|
if (this.options.trimValues && !dontTrim) {
|
|
val = val.trim();
|
|
}
|
|
if(val.length > 0){
|
|
if(!escapeEntities) val = this.replaceEntitiesValue(val);
|
|
|
|
const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
|
|
if(newval === null || newval === undefined){
|
|
//don't parse
|
|
return val;
|
|
}else if(typeof newval !== typeof val || newval !== val){
|
|
//overwrite
|
|
return newval;
|
|
}else if(this.options.trimValues){
|
|
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
|
}else {
|
|
const trimmedVal = val.trim();
|
|
if(trimmedVal === val){
|
|
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
|
}else {
|
|
return val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function resolveNameSpace(tagname) {
|
|
if (this.options.removeNSPrefix) {
|
|
const tags = tagname.split(':');
|
|
const prefix = tagname.charAt(0) === '/' ? '/' : '';
|
|
if (tags[0] === 'xmlns') {
|
|
return '';
|
|
}
|
|
if (tags.length === 2) {
|
|
tagname = prefix + tags[1];
|
|
}
|
|
}
|
|
return tagname;
|
|
}
|
|
|
|
//TODO: change regex to capture NS
|
|
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
|
|
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
|
|
|
|
function buildAttributesMap(attrStr, jPath, tagName) {
|
|
if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') {
|
|
// attrStr = attrStr.replace(/\r?\n/g, ' ');
|
|
//attrStr = attrStr || attrStr.trim();
|
|
|
|
const matches = util.getAllMatches(attrStr, attrsRegx);
|
|
const len = matches.length; //don't make it inline
|
|
const attrs = {};
|
|
for (let i = 0; i < len; i++) {
|
|
const attrName = this.resolveNameSpace(matches[i][1]);
|
|
if (this.ignoreAttributesFn(attrName, jPath)) {
|
|
continue
|
|
}
|
|
let oldVal = matches[i][4];
|
|
let aName = this.options.attributeNamePrefix + attrName;
|
|
if (attrName.length) {
|
|
if (this.options.transformAttributeName) {
|
|
aName = this.options.transformAttributeName(aName);
|
|
}
|
|
if(aName === "__proto__") aName = "#__proto__";
|
|
if (oldVal !== undefined) {
|
|
if (this.options.trimValues) {
|
|
oldVal = oldVal.trim();
|
|
}
|
|
oldVal = this.replaceEntitiesValue(oldVal);
|
|
const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath);
|
|
if(newVal === null || newVal === undefined){
|
|
//don't parse
|
|
attrs[aName] = oldVal;
|
|
}else if(typeof newVal !== typeof oldVal || newVal !== oldVal){
|
|
//overwrite
|
|
attrs[aName] = newVal;
|
|
}else {
|
|
//parse
|
|
attrs[aName] = parseValue(
|
|
oldVal,
|
|
this.options.parseAttributeValue,
|
|
this.options.numberParseOptions
|
|
);
|
|
}
|
|
} else if (this.options.allowBooleanAttributes) {
|
|
attrs[aName] = true;
|
|
}
|
|
}
|
|
}
|
|
if (!Object.keys(attrs).length) {
|
|
return;
|
|
}
|
|
if (this.options.attributesGroupName) {
|
|
const attrCollection = {};
|
|
attrCollection[this.options.attributesGroupName] = attrs;
|
|
return attrCollection;
|
|
}
|
|
return attrs
|
|
}
|
|
}
|
|
|
|
const parseXml = function(xmlData) {
|
|
xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line
|
|
const xmlObj = new xmlNode('!xml');
|
|
let currentNode = xmlObj;
|
|
let textData = "";
|
|
let jPath = "";
|
|
for(let i=0; i< xmlData.length; i++){//for each char in XML data
|
|
const ch = xmlData[i];
|
|
if(ch === '<'){
|
|
// const nextIndex = i+1;
|
|
// const _2ndChar = xmlData[nextIndex];
|
|
if( xmlData[i+1] === '/') {//Closing Tag
|
|
const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.");
|
|
let tagName = xmlData.substring(i+2,closeIndex).trim();
|
|
|
|
if(this.options.removeNSPrefix){
|
|
const colonIndex = tagName.indexOf(":");
|
|
if(colonIndex !== -1){
|
|
tagName = tagName.substr(colonIndex+1);
|
|
}
|
|
}
|
|
|
|
if(this.options.transformTagName) {
|
|
tagName = this.options.transformTagName(tagName);
|
|
}
|
|
|
|
if(currentNode){
|
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
}
|
|
|
|
//check if last tag of nested tag was unpaired tag
|
|
const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1);
|
|
if(tagName && this.options.unpairedTags.indexOf(tagName) !== -1 ){
|
|
throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
|
|
}
|
|
let propIndex = 0;
|
|
if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1 ){
|
|
propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1);
|
|
this.tagsNodeStack.pop();
|
|
}else {
|
|
propIndex = jPath.lastIndexOf(".");
|
|
}
|
|
jPath = jPath.substring(0, propIndex);
|
|
|
|
currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
|
|
textData = "";
|
|
i = closeIndex;
|
|
} else if( xmlData[i+1] === '?') {
|
|
|
|
let tagData = readTagExp(xmlData,i, false, "?>");
|
|
if(!tagData) throw new Error("Pi Tag is not closed.");
|
|
|
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
if( (this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags);else {
|
|
|
|
const childNode = new xmlNode(tagData.tagName);
|
|
childNode.add(this.options.textNodeName, "");
|
|
|
|
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
|
|
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
|
|
}
|
|
this.addChild(currentNode, childNode, jPath);
|
|
|
|
}
|
|
|
|
|
|
i = tagData.closeIndex + 1;
|
|
} else if(xmlData.substr(i + 1, 3) === '!--') {
|
|
const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.");
|
|
if(this.options.commentPropName){
|
|
const comment = xmlData.substring(i + 4, endIndex - 2);
|
|
|
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
|
|
currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]);
|
|
}
|
|
i = endIndex;
|
|
} else if( xmlData.substr(i + 1, 2) === '!D') {
|
|
const result = readDocType(xmlData, i);
|
|
this.docTypeEntities = result.entities;
|
|
i = result.i;
|
|
}else if(xmlData.substr(i + 1, 2) === '![') {
|
|
const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
|
|
const tagExp = xmlData.substring(i + 9,closeIndex);
|
|
|
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
|
|
let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true);
|
|
if(val == undefined) val = "";
|
|
|
|
//cdata should be set even if it is 0 length string
|
|
if(this.options.cdataPropName){
|
|
currentNode.add(this.options.cdataPropName, [ { [this.options.textNodeName] : tagExp } ]);
|
|
}else {
|
|
currentNode.add(this.options.textNodeName, val);
|
|
}
|
|
|
|
i = closeIndex + 2;
|
|
}else {//Opening tag
|
|
let result = readTagExp(xmlData,i, this.options.removeNSPrefix);
|
|
let tagName= result.tagName;
|
|
const rawTagName = result.rawTagName;
|
|
let tagExp = result.tagExp;
|
|
let attrExpPresent = result.attrExpPresent;
|
|
let closeIndex = result.closeIndex;
|
|
|
|
if (this.options.transformTagName) {
|
|
tagName = this.options.transformTagName(tagName);
|
|
}
|
|
|
|
//save text as child node
|
|
if (currentNode && textData) {
|
|
if(currentNode.tagname !== '!xml'){
|
|
//when nested tag is found
|
|
textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
|
|
}
|
|
}
|
|
|
|
//check if last tag was unpaired tag
|
|
const lastTag = currentNode;
|
|
if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){
|
|
currentNode = this.tagsNodeStack.pop();
|
|
jPath = jPath.substring(0, jPath.lastIndexOf("."));
|
|
}
|
|
if(tagName !== xmlObj.tagname){
|
|
jPath += jPath ? "." + tagName : tagName;
|
|
}
|
|
if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) {
|
|
let tagContent = "";
|
|
//self-closing tag
|
|
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
|
|
if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
|
|
tagName = tagName.substr(0, tagName.length - 1);
|
|
jPath = jPath.substr(0, jPath.length - 1);
|
|
tagExp = tagName;
|
|
}else {
|
|
tagExp = tagExp.substr(0, tagExp.length - 1);
|
|
}
|
|
i = result.closeIndex;
|
|
}
|
|
//unpaired tag
|
|
else if(this.options.unpairedTags.indexOf(tagName) !== -1){
|
|
|
|
i = result.closeIndex;
|
|
}
|
|
//normal tag
|
|
else {
|
|
//read until closing tag is found
|
|
const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
|
|
if(!result) throw new Error(`Unexpected end of ${rawTagName}`);
|
|
i = result.i;
|
|
tagContent = result.tagContent;
|
|
}
|
|
|
|
const childNode = new xmlNode(tagName);
|
|
if(tagName !== tagExp && attrExpPresent){
|
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
}
|
|
if(tagContent) {
|
|
tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
|
|
}
|
|
|
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
childNode.add(this.options.textNodeName, tagContent);
|
|
|
|
this.addChild(currentNode, childNode, jPath);
|
|
}else {
|
|
//selfClosing tag
|
|
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
|
|
if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
|
|
tagName = tagName.substr(0, tagName.length - 1);
|
|
jPath = jPath.substr(0, jPath.length - 1);
|
|
tagExp = tagName;
|
|
}else {
|
|
tagExp = tagExp.substr(0, tagExp.length - 1);
|
|
}
|
|
|
|
if(this.options.transformTagName) {
|
|
tagName = this.options.transformTagName(tagName);
|
|
}
|
|
|
|
const childNode = new xmlNode(tagName);
|
|
if(tagName !== tagExp && attrExpPresent){
|
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
}
|
|
this.addChild(currentNode, childNode, jPath);
|
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
}
|
|
//opening tag
|
|
else {
|
|
const childNode = new xmlNode( tagName);
|
|
this.tagsNodeStack.push(currentNode);
|
|
|
|
if(tagName !== tagExp && attrExpPresent){
|
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
}
|
|
this.addChild(currentNode, childNode, jPath);
|
|
currentNode = childNode;
|
|
}
|
|
textData = "";
|
|
i = closeIndex;
|
|
}
|
|
}
|
|
}else {
|
|
textData += xmlData[i];
|
|
}
|
|
}
|
|
return xmlObj.child;
|
|
};
|
|
|
|
function addChild(currentNode, childNode, jPath){
|
|
const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"]);
|
|
if(result === false);else if(typeof result === "string"){
|
|
childNode.tagname = result;
|
|
currentNode.addChild(childNode);
|
|
}else {
|
|
currentNode.addChild(childNode);
|
|
}
|
|
}
|
|
|
|
const replaceEntitiesValue$1 = function(val){
|
|
|
|
if(this.options.processEntities){
|
|
for(let entityName in this.docTypeEntities){
|
|
const entity = this.docTypeEntities[entityName];
|
|
val = val.replace( entity.regx, entity.val);
|
|
}
|
|
for(let entityName in this.lastEntities){
|
|
const entity = this.lastEntities[entityName];
|
|
val = val.replace( entity.regex, entity.val);
|
|
}
|
|
if(this.options.htmlEntities){
|
|
for(let entityName in this.htmlEntities){
|
|
const entity = this.htmlEntities[entityName];
|
|
val = val.replace( entity.regex, entity.val);
|
|
}
|
|
}
|
|
val = val.replace( this.ampEntity.regex, this.ampEntity.val);
|
|
}
|
|
return val;
|
|
};
|
|
function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
|
|
if (textData) { //store previously collected data as textNode
|
|
if(isLeafNode === undefined) isLeafNode = Object.keys(currentNode.child).length === 0;
|
|
|
|
textData = this.parseTextData(textData,
|
|
currentNode.tagname,
|
|
jPath,
|
|
false,
|
|
currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
|
|
isLeafNode);
|
|
|
|
if (textData !== undefined && textData !== "")
|
|
currentNode.add(this.options.textNodeName, textData);
|
|
textData = "";
|
|
}
|
|
return textData;
|
|
}
|
|
|
|
//TODO: use jPath to simplify the logic
|
|
/**
|
|
*
|
|
* @param {string[]} stopNodes
|
|
* @param {string} jPath
|
|
* @param {string} currentTagName
|
|
*/
|
|
function isItStopNode(stopNodes, jPath, currentTagName){
|
|
const allNodesExp = "*." + currentTagName;
|
|
for (const stopNodePath in stopNodes) {
|
|
const stopNodeExp = stopNodes[stopNodePath];
|
|
if( allNodesExp === stopNodeExp || jPath === stopNodeExp ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the tag Expression and where it is ending handling single-double quotes situation
|
|
* @param {string} xmlData
|
|
* @param {number} i starting index
|
|
* @returns
|
|
*/
|
|
function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
|
|
let attrBoundary;
|
|
let tagExp = "";
|
|
for (let index = i; index < xmlData.length; index++) {
|
|
let ch = xmlData[index];
|
|
if (attrBoundary) {
|
|
if (ch === attrBoundary) attrBoundary = "";//reset
|
|
} else if (ch === '"' || ch === "'") {
|
|
attrBoundary = ch;
|
|
} else if (ch === closingChar[0]) {
|
|
if(closingChar[1]){
|
|
if(xmlData[index + 1] === closingChar[1]){
|
|
return {
|
|
data: tagExp,
|
|
index: index
|
|
}
|
|
}
|
|
}else {
|
|
return {
|
|
data: tagExp,
|
|
index: index
|
|
}
|
|
}
|
|
} else if (ch === '\t') {
|
|
ch = " ";
|
|
}
|
|
tagExp += ch;
|
|
}
|
|
}
|
|
|
|
function findClosingIndex(xmlData, str, i, errMsg){
|
|
const closingIndex = xmlData.indexOf(str, i);
|
|
if(closingIndex === -1){
|
|
throw new Error(errMsg)
|
|
}else {
|
|
return closingIndex + str.length - 1;
|
|
}
|
|
}
|
|
|
|
function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
|
|
const result = tagExpWithClosingIndex(xmlData, i+1, closingChar);
|
|
if(!result) return;
|
|
let tagExp = result.data;
|
|
const closeIndex = result.index;
|
|
const separatorIndex = tagExp.search(/\s/);
|
|
let tagName = tagExp;
|
|
let attrExpPresent = true;
|
|
if(separatorIndex !== -1){//separate tag name and attributes expression
|
|
tagName = tagExp.substring(0, separatorIndex);
|
|
tagExp = tagExp.substring(separatorIndex + 1).trimStart();
|
|
}
|
|
|
|
const rawTagName = tagName;
|
|
if(removeNSPrefix){
|
|
const colonIndex = tagName.indexOf(":");
|
|
if(colonIndex !== -1){
|
|
tagName = tagName.substr(colonIndex+1);
|
|
attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
|
|
}
|
|
}
|
|
|
|
return {
|
|
tagName: tagName,
|
|
tagExp: tagExp,
|
|
closeIndex: closeIndex,
|
|
attrExpPresent: attrExpPresent,
|
|
rawTagName: rawTagName,
|
|
}
|
|
}
|
|
/**
|
|
* find paired tag for a stop node
|
|
* @param {string} xmlData
|
|
* @param {string} tagName
|
|
* @param {number} i
|
|
*/
|
|
function readStopNodeData(xmlData, tagName, i){
|
|
const startIndex = i;
|
|
// Starting at 1 since we already have an open tag
|
|
let openTagCount = 1;
|
|
|
|
for (; i < xmlData.length; i++) {
|
|
if( xmlData[i] === "<"){
|
|
if (xmlData[i+1] === "/") {//close tag
|
|
const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
|
|
let closeTagName = xmlData.substring(i+2,closeIndex).trim();
|
|
if(closeTagName === tagName){
|
|
openTagCount--;
|
|
if (openTagCount === 0) {
|
|
return {
|
|
tagContent: xmlData.substring(startIndex, i),
|
|
i : closeIndex
|
|
}
|
|
}
|
|
}
|
|
i=closeIndex;
|
|
} else if(xmlData[i+1] === '?') {
|
|
const closeIndex = findClosingIndex(xmlData, "?>", i+1, "StopNode is not closed.");
|
|
i=closeIndex;
|
|
} else if(xmlData.substr(i + 1, 3) === '!--') {
|
|
const closeIndex = findClosingIndex(xmlData, "-->", i+3, "StopNode is not closed.");
|
|
i=closeIndex;
|
|
} else if(xmlData.substr(i + 1, 2) === '![') {
|
|
const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
|
|
i=closeIndex;
|
|
} else {
|
|
const tagData = readTagExp(xmlData, i, '>');
|
|
|
|
if (tagData) {
|
|
const openTagName = tagData && tagData.tagName;
|
|
if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
|
|
openTagCount++;
|
|
}
|
|
i=tagData.closeIndex;
|
|
}
|
|
}
|
|
}
|
|
}//end for loop
|
|
}
|
|
|
|
function parseValue(val, shouldParse, options) {
|
|
if (shouldParse && typeof val === 'string') {
|
|
//console.log(options)
|
|
const newval = val.trim();
|
|
if(newval === 'true' ) return true;
|
|
else if(newval === 'false' ) return false;
|
|
else return toNumber(val, options);
|
|
} else {
|
|
if (util.isExist(val)) {
|
|
return val;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var OrderedObjParser_1 = OrderedObjParser$1;
|
|
|
|
var node2json = {};
|
|
|
|
/**
|
|
*
|
|
* @param {array} node
|
|
* @param {any} options
|
|
* @returns
|
|
*/
|
|
function prettify$1(node, options){
|
|
return compress( node, options);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {array} arr
|
|
* @param {object} options
|
|
* @param {string} jPath
|
|
* @returns object
|
|
*/
|
|
function compress(arr, options, jPath){
|
|
let text;
|
|
const compressedObj = {};
|
|
for (let i = 0; i < arr.length; i++) {
|
|
const tagObj = arr[i];
|
|
const property = propName$1(tagObj);
|
|
let newJpath = "";
|
|
if(jPath === undefined) newJpath = property;
|
|
else newJpath = jPath + "." + property;
|
|
|
|
if(property === options.textNodeName){
|
|
if(text === undefined) text = tagObj[property];
|
|
else text += "" + tagObj[property];
|
|
}else if(property === undefined){
|
|
continue;
|
|
}else if(tagObj[property]){
|
|
|
|
let val = compress(tagObj[property], options, newJpath);
|
|
const isLeaf = isLeafTag(val, options);
|
|
|
|
if(tagObj[":@"]){
|
|
assignAttributes( val, tagObj[":@"], newJpath, options);
|
|
}else if(Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode){
|
|
val = val[options.textNodeName];
|
|
}else if(Object.keys(val).length === 0){
|
|
if(options.alwaysCreateTextNode) val[options.textNodeName] = "";
|
|
else val = "";
|
|
}
|
|
|
|
if(compressedObj[property] !== undefined && compressedObj.hasOwnProperty(property)) {
|
|
if(!Array.isArray(compressedObj[property])) {
|
|
compressedObj[property] = [ compressedObj[property] ];
|
|
}
|
|
compressedObj[property].push(val);
|
|
}else {
|
|
//TODO: if a node is not an array, then check if it should be an array
|
|
//also determine if it is a leaf node
|
|
if (options.isArray(property, newJpath, isLeaf )) {
|
|
compressedObj[property] = [val];
|
|
}else {
|
|
compressedObj[property] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
// if(text && text.length > 0) compressedObj[options.textNodeName] = text;
|
|
if(typeof text === "string"){
|
|
if(text.length > 0) compressedObj[options.textNodeName] = text;
|
|
}else if(text !== undefined) compressedObj[options.textNodeName] = text;
|
|
return compressedObj;
|
|
}
|
|
|
|
function propName$1(obj){
|
|
const keys = Object.keys(obj);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
if(key !== ":@") return key;
|
|
}
|
|
}
|
|
|
|
function assignAttributes(obj, attrMap, jpath, options){
|
|
if (attrMap) {
|
|
const keys = Object.keys(attrMap);
|
|
const len = keys.length; //don't make it inline
|
|
for (let i = 0; i < len; i++) {
|
|
const atrrName = keys[i];
|
|
if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
|
|
obj[atrrName] = [ attrMap[atrrName] ];
|
|
} else {
|
|
obj[atrrName] = attrMap[atrrName];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function isLeafTag(obj, options){
|
|
const { textNodeName } = options;
|
|
const propCount = Object.keys(obj).length;
|
|
|
|
if (propCount === 0) {
|
|
return true;
|
|
}
|
|
|
|
if (
|
|
propCount === 1 &&
|
|
(obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
node2json.prettify = prettify$1;
|
|
|
|
const { buildOptions} = OptionsBuilder;
|
|
const OrderedObjParser = OrderedObjParser_1;
|
|
const { prettify} = node2json;
|
|
const validator$1 = validator$2;
|
|
|
|
class XMLParser$2{
|
|
|
|
constructor(options){
|
|
this.externalEntities = {};
|
|
this.options = buildOptions(options);
|
|
|
|
}
|
|
/**
|
|
* Parse XML dats to JS object
|
|
* @param {string|Buffer} xmlData
|
|
* @param {boolean|Object} validationOption
|
|
*/
|
|
parse(xmlData,validationOption){
|
|
if(typeof xmlData === "string");else if( xmlData.toString){
|
|
xmlData = xmlData.toString();
|
|
}else {
|
|
throw new Error("XML data is accepted in String or Bytes[] form.")
|
|
}
|
|
if( validationOption){
|
|
if(validationOption === true) validationOption = {}; //validate with default options
|
|
|
|
const result = validator$1.validate(xmlData, validationOption);
|
|
if (result !== true) {
|
|
throw Error( `${result.err.msg}:${result.err.line}:${result.err.col}` )
|
|
}
|
|
}
|
|
const orderedObjParser = new OrderedObjParser(this.options);
|
|
orderedObjParser.addExternalEntities(this.externalEntities);
|
|
const orderedResult = orderedObjParser.parseXml(xmlData);
|
|
if(this.options.preserveOrder || orderedResult === undefined) return orderedResult;
|
|
else return prettify(orderedResult, this.options);
|
|
}
|
|
|
|
/**
|
|
* Add Entity which is not by default supported by this library
|
|
* @param {string} key
|
|
* @param {string} value
|
|
*/
|
|
addEntity(key, value){
|
|
if(value.indexOf("&") !== -1){
|
|
throw new Error("Entity value can't have '&'")
|
|
}else if(key.indexOf("&") !== -1 || key.indexOf(";") !== -1){
|
|
throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'")
|
|
}else if(value === "&"){
|
|
throw new Error("An entity with value '&' is not permitted");
|
|
}else {
|
|
this.externalEntities[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
var XMLParser_1 = XMLParser$2;
|
|
|
|
const EOL = "\n";
|
|
|
|
/**
|
|
*
|
|
* @param {array} jArray
|
|
* @param {any} options
|
|
* @returns
|
|
*/
|
|
function toXml(jArray, options) {
|
|
let indentation = "";
|
|
if (options.format && options.indentBy.length > 0) {
|
|
indentation = EOL;
|
|
}
|
|
return arrToStr(jArray, options, "", indentation);
|
|
}
|
|
|
|
function arrToStr(arr, options, jPath, indentation) {
|
|
let xmlStr = "";
|
|
let isPreviousElementTag = false;
|
|
|
|
for (let i = 0; i < arr.length; i++) {
|
|
const tagObj = arr[i];
|
|
const tagName = propName(tagObj);
|
|
if(tagName === undefined) continue;
|
|
|
|
let newJPath = "";
|
|
if (jPath.length === 0) newJPath = tagName;
|
|
else newJPath = `${jPath}.${tagName}`;
|
|
|
|
if (tagName === options.textNodeName) {
|
|
let tagText = tagObj[tagName];
|
|
if (!isStopNode(newJPath, options)) {
|
|
tagText = options.tagValueProcessor(tagName, tagText);
|
|
tagText = replaceEntitiesValue(tagText, options);
|
|
}
|
|
if (isPreviousElementTag) {
|
|
xmlStr += indentation;
|
|
}
|
|
xmlStr += tagText;
|
|
isPreviousElementTag = false;
|
|
continue;
|
|
} else if (tagName === options.cdataPropName) {
|
|
if (isPreviousElementTag) {
|
|
xmlStr += indentation;
|
|
}
|
|
xmlStr += `<![CDATA[${tagObj[tagName][0][options.textNodeName]}]]>`;
|
|
isPreviousElementTag = false;
|
|
continue;
|
|
} else if (tagName === options.commentPropName) {
|
|
xmlStr += indentation + `<!--${tagObj[tagName][0][options.textNodeName]}-->`;
|
|
isPreviousElementTag = true;
|
|
continue;
|
|
} else if (tagName[0] === "?") {
|
|
const attStr = attr_to_str(tagObj[":@"], options);
|
|
const tempInd = tagName === "?xml" ? "" : indentation;
|
|
let piTextNodeName = tagObj[tagName][0][options.textNodeName];
|
|
piTextNodeName = piTextNodeName.length !== 0 ? " " + piTextNodeName : ""; //remove extra spacing
|
|
xmlStr += tempInd + `<${tagName}${piTextNodeName}${attStr}?>`;
|
|
isPreviousElementTag = true;
|
|
continue;
|
|
}
|
|
let newIdentation = indentation;
|
|
if (newIdentation !== "") {
|
|
newIdentation += options.indentBy;
|
|
}
|
|
const attStr = attr_to_str(tagObj[":@"], options);
|
|
const tagStart = indentation + `<${tagName}${attStr}`;
|
|
const tagValue = arrToStr(tagObj[tagName], options, newJPath, newIdentation);
|
|
if (options.unpairedTags.indexOf(tagName) !== -1) {
|
|
if (options.suppressUnpairedNode) xmlStr += tagStart + ">";
|
|
else xmlStr += tagStart + "/>";
|
|
} else if ((!tagValue || tagValue.length === 0) && options.suppressEmptyNode) {
|
|
xmlStr += tagStart + "/>";
|
|
} else if (tagValue && tagValue.endsWith(">")) {
|
|
xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>`;
|
|
} else {
|
|
xmlStr += tagStart + ">";
|
|
if (tagValue && indentation !== "" && (tagValue.includes("/>") || tagValue.includes("</"))) {
|
|
xmlStr += indentation + options.indentBy + tagValue + indentation;
|
|
} else {
|
|
xmlStr += tagValue;
|
|
}
|
|
xmlStr += `</${tagName}>`;
|
|
}
|
|
isPreviousElementTag = true;
|
|
}
|
|
|
|
return xmlStr;
|
|
}
|
|
|
|
function propName(obj) {
|
|
const keys = Object.keys(obj);
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
if(!obj.hasOwnProperty(key)) continue;
|
|
if (key !== ":@") return key;
|
|
}
|
|
}
|
|
|
|
function attr_to_str(attrMap, options) {
|
|
let attrStr = "";
|
|
if (attrMap && !options.ignoreAttributes) {
|
|
for (let attr in attrMap) {
|
|
if(!attrMap.hasOwnProperty(attr)) continue;
|
|
let attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
|
|
attrVal = replaceEntitiesValue(attrVal, options);
|
|
if (attrVal === true && options.suppressBooleanAttributes) {
|
|
attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}`;
|
|
} else {
|
|
attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}="${attrVal}"`;
|
|
}
|
|
}
|
|
}
|
|
return attrStr;
|
|
}
|
|
|
|
function isStopNode(jPath, options) {
|
|
jPath = jPath.substr(0, jPath.length - options.textNodeName.length - 1);
|
|
let tagName = jPath.substr(jPath.lastIndexOf(".") + 1);
|
|
for (let index in options.stopNodes) {
|
|
if (options.stopNodes[index] === jPath || options.stopNodes[index] === "*." + tagName) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function replaceEntitiesValue(textValue, options) {
|
|
if (textValue && textValue.length > 0 && options.processEntities) {
|
|
for (let i = 0; i < options.entities.length; i++) {
|
|
const entity = options.entities[i];
|
|
textValue = textValue.replace(entity.regex, entity.val);
|
|
}
|
|
}
|
|
return textValue;
|
|
}
|
|
var orderedJs2Xml = toXml;
|
|
|
|
//parse Empty Node as self closing node
|
|
const buildFromOrderedJs = orderedJs2Xml;
|
|
const getIgnoreAttributesFn = ignoreAttributes;
|
|
|
|
const defaultOptions = {
|
|
attributeNamePrefix: '@_',
|
|
attributesGroupName: false,
|
|
textNodeName: '#text',
|
|
ignoreAttributes: true,
|
|
cdataPropName: false,
|
|
format: false,
|
|
indentBy: ' ',
|
|
suppressEmptyNode: false,
|
|
suppressUnpairedNode: true,
|
|
suppressBooleanAttributes: true,
|
|
tagValueProcessor: function(key, a) {
|
|
return a;
|
|
},
|
|
attributeValueProcessor: function(attrName, a) {
|
|
return a;
|
|
},
|
|
preserveOrder: false,
|
|
commentPropName: false,
|
|
unpairedTags: [],
|
|
entities: [
|
|
{ regex: new RegExp("&", "g"), val: "&" },//it must be on top
|
|
{ regex: new RegExp(">", "g"), val: ">" },
|
|
{ regex: new RegExp("<", "g"), val: "<" },
|
|
{ regex: new RegExp("\'", "g"), val: "'" },
|
|
{ regex: new RegExp("\"", "g"), val: """ }
|
|
],
|
|
processEntities: true,
|
|
stopNodes: [],
|
|
// transformTagName: false,
|
|
// transformAttributeName: false,
|
|
oneListGroup: false
|
|
};
|
|
|
|
function Builder(options) {
|
|
this.options = Object.assign({}, defaultOptions, options);
|
|
if (this.options.ignoreAttributes === true || this.options.attributesGroupName) {
|
|
this.isAttribute = function(/*a*/) {
|
|
return false;
|
|
};
|
|
} else {
|
|
this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes);
|
|
this.attrPrefixLen = this.options.attributeNamePrefix.length;
|
|
this.isAttribute = isAttribute;
|
|
}
|
|
|
|
this.processTextOrObjNode = processTextOrObjNode;
|
|
|
|
if (this.options.format) {
|
|
this.indentate = indentate;
|
|
this.tagEndChar = '>\n';
|
|
this.newLine = '\n';
|
|
} else {
|
|
this.indentate = function() {
|
|
return '';
|
|
};
|
|
this.tagEndChar = '>';
|
|
this.newLine = '';
|
|
}
|
|
}
|
|
|
|
Builder.prototype.build = function(jObj) {
|
|
if(this.options.preserveOrder){
|
|
return buildFromOrderedJs(jObj, this.options);
|
|
}else {
|
|
if(Array.isArray(jObj) && this.options.arrayNodeName && this.options.arrayNodeName.length > 1){
|
|
jObj = {
|
|
[this.options.arrayNodeName] : jObj
|
|
};
|
|
}
|
|
return this.j2x(jObj, 0, []).val;
|
|
}
|
|
};
|
|
|
|
Builder.prototype.j2x = function(jObj, level, ajPath) {
|
|
let attrStr = '';
|
|
let val = '';
|
|
const jPath = ajPath.join('.');
|
|
for (let key in jObj) {
|
|
if(!Object.prototype.hasOwnProperty.call(jObj, key)) continue;
|
|
if (typeof jObj[key] === 'undefined') {
|
|
// supress undefined node only if it is not an attribute
|
|
if (this.isAttribute(key)) {
|
|
val += '';
|
|
}
|
|
} else if (jObj[key] === null) {
|
|
// null attribute should be ignored by the attribute list, but should not cause the tag closing
|
|
if (this.isAttribute(key)) {
|
|
val += '';
|
|
} else if (key[0] === '?') {
|
|
val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
|
|
} else {
|
|
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
}
|
|
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
} else if (jObj[key] instanceof Date) {
|
|
val += this.buildTextValNode(jObj[key], key, '', level);
|
|
} else if (typeof jObj[key] !== 'object') {
|
|
//premitive type
|
|
const attr = this.isAttribute(key);
|
|
if (attr && !this.ignoreAttributesFn(attr, jPath)) {
|
|
attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
|
|
} else if (!attr) {
|
|
//tag value
|
|
if (key === this.options.textNodeName) {
|
|
let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
|
|
val += this.replaceEntitiesValue(newval);
|
|
} else {
|
|
val += this.buildTextValNode(jObj[key], key, '', level);
|
|
}
|
|
}
|
|
} else if (Array.isArray(jObj[key])) {
|
|
//repeated nodes
|
|
const arrLen = jObj[key].length;
|
|
let listTagVal = "";
|
|
let listTagAttr = "";
|
|
for (let j = 0; j < arrLen; j++) {
|
|
const item = jObj[key][j];
|
|
if (typeof item === 'undefined') ; else if (item === null) {
|
|
if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
|
|
else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
} else if (typeof item === 'object') {
|
|
if(this.options.oneListGroup){
|
|
const result = this.j2x(item, level + 1, ajPath.concat(key));
|
|
listTagVal += result.val;
|
|
if (this.options.attributesGroupName && item.hasOwnProperty(this.options.attributesGroupName)) {
|
|
listTagAttr += result.attrStr;
|
|
}
|
|
}else {
|
|
listTagVal += this.processTextOrObjNode(item, key, level, ajPath);
|
|
}
|
|
} else {
|
|
if (this.options.oneListGroup) {
|
|
let textValue = this.options.tagValueProcessor(key, item);
|
|
textValue = this.replaceEntitiesValue(textValue);
|
|
listTagVal += textValue;
|
|
} else {
|
|
listTagVal += this.buildTextValNode(item, key, '', level);
|
|
}
|
|
}
|
|
}
|
|
if(this.options.oneListGroup){
|
|
listTagVal = this.buildObjectNode(listTagVal, key, listTagAttr, level);
|
|
}
|
|
val += listTagVal;
|
|
} else {
|
|
//nested node
|
|
if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
|
|
const Ks = Object.keys(jObj[key]);
|
|
const L = Ks.length;
|
|
for (let j = 0; j < L; j++) {
|
|
attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
|
|
}
|
|
} else {
|
|
val += this.processTextOrObjNode(jObj[key], key, level, ajPath);
|
|
}
|
|
}
|
|
}
|
|
return {attrStr: attrStr, val: val};
|
|
};
|
|
|
|
Builder.prototype.buildAttrPairStr = function(attrName, val){
|
|
val = this.options.attributeValueProcessor(attrName, '' + val);
|
|
val = this.replaceEntitiesValue(val);
|
|
if (this.options.suppressBooleanAttributes && val === "true") {
|
|
return ' ' + attrName;
|
|
} else return ' ' + attrName + '="' + val + '"';
|
|
};
|
|
|
|
function processTextOrObjNode (object, key, level, ajPath) {
|
|
const result = this.j2x(object, level + 1, ajPath.concat(key));
|
|
if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
|
|
return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level);
|
|
} else {
|
|
return this.buildObjectNode(result.val, key, result.attrStr, level);
|
|
}
|
|
}
|
|
|
|
Builder.prototype.buildObjectNode = function(val, key, attrStr, level) {
|
|
if(val === ""){
|
|
if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
|
|
else {
|
|
return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
|
|
}
|
|
}else {
|
|
|
|
let tagEndExp = '</' + key + this.tagEndChar;
|
|
let piClosingChar = "";
|
|
|
|
if(key[0] === "?") {
|
|
piClosingChar = "?";
|
|
tagEndExp = "";
|
|
}
|
|
|
|
// attrStr is an empty string in case the attribute came as undefined or null
|
|
if ((attrStr || attrStr === '') && val.indexOf('<') === -1) {
|
|
return ( this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' + val + tagEndExp );
|
|
} else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
|
|
return this.indentate(level) + `<!--${val}-->` + this.newLine;
|
|
}else {
|
|
return (
|
|
this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
|
|
val +
|
|
this.indentate(level) + tagEndExp );
|
|
}
|
|
}
|
|
};
|
|
|
|
Builder.prototype.closeTag = function(key){
|
|
let closeTag = "";
|
|
if(this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
|
|
if(!this.options.suppressUnpairedNode) closeTag = "/";
|
|
}else if(this.options.suppressEmptyNode){ //empty
|
|
closeTag = "/";
|
|
}else {
|
|
closeTag = `></${key}`;
|
|
}
|
|
return closeTag;
|
|
};
|
|
|
|
Builder.prototype.buildTextValNode = function(val, key, attrStr, level) {
|
|
if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
|
|
return this.indentate(level) + `<![CDATA[${val}]]>` + this.newLine;
|
|
}else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
|
|
return this.indentate(level) + `<!--${val}-->` + this.newLine;
|
|
}else if(key[0] === "?") {//PI tag
|
|
return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
|
|
}else {
|
|
let textValue = this.options.tagValueProcessor(key, val);
|
|
textValue = this.replaceEntitiesValue(textValue);
|
|
|
|
if( textValue === ''){
|
|
return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
|
|
}else {
|
|
return this.indentate(level) + '<' + key + attrStr + '>' +
|
|
textValue +
|
|
'</' + key + this.tagEndChar;
|
|
}
|
|
}
|
|
};
|
|
|
|
Builder.prototype.replaceEntitiesValue = function(textValue){
|
|
if(textValue && textValue.length > 0 && this.options.processEntities){
|
|
for (let i=0; i<this.options.entities.length; i++) {
|
|
const entity = this.options.entities[i];
|
|
textValue = textValue.replace(entity.regex, entity.val);
|
|
}
|
|
}
|
|
return textValue;
|
|
};
|
|
|
|
function indentate(level) {
|
|
return this.options.indentBy.repeat(level);
|
|
}
|
|
|
|
function isAttribute(name /*, options*/) {
|
|
if (name.startsWith(this.options.attributeNamePrefix) && name !== this.options.textNodeName) {
|
|
return name.substr(this.attrPrefixLen);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var json2xml = Builder;
|
|
|
|
const validator = validator$2;
|
|
const XMLParser$1 = XMLParser_1;
|
|
const XMLBuilder = json2xml;
|
|
|
|
var fxp = {
|
|
XMLParser: XMLParser$1,
|
|
XMLValidator: validator,
|
|
XMLBuilder: XMLBuilder
|
|
};
|
|
|
|
const {XMLParser, XMLValidator} = fxp;
|
|
|
|
const isSvg = input => {
|
|
if (input === undefined || input === null) {
|
|
return false;
|
|
}
|
|
|
|
input = input.toString().trim();
|
|
|
|
if (input.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
// Has to be `!==` as it can also return an object with error info.
|
|
if (XMLValidator.validate(input) !== true) {
|
|
return false;
|
|
}
|
|
|
|
let jsonObject;
|
|
const parser = new XMLParser();
|
|
|
|
try {
|
|
jsonObject = parser.parse(input);
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
|
|
if (!jsonObject) {
|
|
return false;
|
|
}
|
|
|
|
if (!('svg' in jsonObject)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
isSvg$2.exports = isSvg;
|
|
// TODO: Remove this for the next major release
|
|
isSvg$2.exports.default = isSvg;
|
|
|
|
var isSvgExports = isSvg$2.exports;
|
|
var isSvg$1 = /*@__PURE__*/getDefaultExportFromCjs(isSvgExports);
|
|
|
|
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
|
|
|
|
var escapeStringRegexp$2 = function (str) {
|
|
if (typeof str !== 'string') {
|
|
throw new TypeError('Expected a string');
|
|
}
|
|
|
|
return str.replace(matchOperatorsRe, '\\$&');
|
|
};
|
|
|
|
var escapeStringRegexp$1 = escapeStringRegexp$2;
|
|
|
|
var trimRepeated = function (str, target) {
|
|
if (typeof str !== 'string' || typeof target !== 'string') {
|
|
throw new TypeError('Expected a string');
|
|
}
|
|
|
|
return str.replace(new RegExp('(?:' + escapeStringRegexp$1(target) + '){2,}', 'g'), target);
|
|
};
|
|
|
|
var trimRepeated$1 = /*@__PURE__*/getDefaultExportFromCjs(trimRepeated);
|
|
|
|
var filenameReservedRegex$1 = {exports: {}};
|
|
|
|
/* eslint-disable no-control-regex */
|
|
// TODO: remove parens when Node.js 6 is targeted. Node.js 4 barfs at it.
|
|
filenameReservedRegex$1.exports = () => (/[<>:"\/\\|?*\x00-\x1F]/g);
|
|
filenameReservedRegex$1.exports.windowsNames = () => (/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i);
|
|
|
|
var filenameReservedRegexExports = filenameReservedRegex$1.exports;
|
|
var filenameReservedRegex = /*@__PURE__*/getDefaultExportFromCjs(filenameReservedRegexExports);
|
|
|
|
var escapeStringRegexp = escapeStringRegexp$2;
|
|
|
|
var stripOuter = function (str, sub) {
|
|
if (typeof str !== 'string' || typeof sub !== 'string') {
|
|
throw new TypeError();
|
|
}
|
|
|
|
sub = escapeStringRegexp(sub);
|
|
return str.replace(new RegExp('^' + sub + '|' + sub + '$', 'g'), '');
|
|
};
|
|
|
|
var stripOuter$1 = /*@__PURE__*/getDefaultExportFromCjs(stripOuter);
|
|
|
|
// Doesn't make sense to have longer filenames
|
|
const MAX_FILENAME_LENGTH = 100;
|
|
|
|
const reControlChars = /[\u0000-\u001F\u0080-\u009F]/g; // eslint-disable-line no-control-regex
|
|
const reRelativePath = /^\.+/;
|
|
const reTrailingPeriods = /\.+$/;
|
|
|
|
function filenamify(string, options = {}) {
|
|
if (typeof string !== 'string') {
|
|
throw new TypeError('Expected a string');
|
|
}
|
|
|
|
const replacement = options.replacement === undefined ? '!' : options.replacement;
|
|
|
|
if (filenameReservedRegex().test(replacement) && reControlChars.test(replacement)) {
|
|
throw new Error('Replacement string cannot contain reserved filename characters');
|
|
}
|
|
|
|
string = string.replace(filenameReservedRegex(), replacement);
|
|
string = string.replace(reControlChars, replacement);
|
|
string = string.replace(reRelativePath, replacement);
|
|
string = string.replace(reTrailingPeriods, '');
|
|
|
|
if (replacement.length > 0) {
|
|
string = trimRepeated$1(string, replacement);
|
|
string = string.length > 1 ? stripOuter$1(string, replacement) : string;
|
|
}
|
|
|
|
string = filenameReservedRegex.windowsNames().test(string) ? string + replacement : string;
|
|
string = string.slice(0, typeof options.maxLength === 'number' ? options.maxLength : MAX_FILENAME_LENGTH);
|
|
|
|
return string;
|
|
}
|
|
|
|
var md5$1 = {exports: {}};
|
|
|
|
function commonjsRequire(path) {
|
|
throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
|
|
}
|
|
|
|
var core = {exports: {}};
|
|
|
|
var _nodeResolve_empty = {};
|
|
|
|
var _nodeResolve_empty$1 = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
'default': _nodeResolve_empty
|
|
});
|
|
|
|
var require$$0 = /*@__PURE__*/getAugmentedNamespace(_nodeResolve_empty$1);
|
|
|
|
core.exports;
|
|
|
|
var hasRequiredCore;
|
|
|
|
function requireCore () {
|
|
if (hasRequiredCore) return core.exports;
|
|
hasRequiredCore = 1;
|
|
(function (module, exports) {
|
|
(function (root, factory) {
|
|
{
|
|
// CommonJS
|
|
module.exports = factory();
|
|
}
|
|
}(commonjsGlobal, function () {
|
|
|
|
/*globals window, global, require*/
|
|
|
|
/**
|
|
* CryptoJS core components.
|
|
*/
|
|
var CryptoJS = CryptoJS || (function (Math, undefined$1) {
|
|
|
|
var crypto;
|
|
|
|
// Native crypto from window (Browser)
|
|
if (typeof window !== 'undefined' && window.crypto) {
|
|
crypto = window.crypto;
|
|
}
|
|
|
|
// Native crypto in web worker (Browser)
|
|
if (typeof self !== 'undefined' && self.crypto) {
|
|
crypto = self.crypto;
|
|
}
|
|
|
|
// Native crypto from worker
|
|
if (typeof globalThis !== 'undefined' && globalThis.crypto) {
|
|
crypto = globalThis.crypto;
|
|
}
|
|
|
|
// Native (experimental IE 11) crypto from window (Browser)
|
|
if (!crypto && typeof window !== 'undefined' && window.msCrypto) {
|
|
crypto = window.msCrypto;
|
|
}
|
|
|
|
// Native crypto from global (NodeJS)
|
|
if (!crypto && typeof commonjsGlobal !== 'undefined' && commonjsGlobal.crypto) {
|
|
crypto = commonjsGlobal.crypto;
|
|
}
|
|
|
|
// Native crypto import via require (NodeJS)
|
|
if (!crypto && typeof commonjsRequire === 'function') {
|
|
try {
|
|
crypto = require$$0;
|
|
} catch (err) {}
|
|
}
|
|
|
|
/*
|
|
* Cryptographically secure pseudorandom number generator
|
|
*
|
|
* As Math.random() is cryptographically not safe to use
|
|
*/
|
|
var cryptoSecureRandomInt = function () {
|
|
if (crypto) {
|
|
// Use getRandomValues method (Browser)
|
|
if (typeof crypto.getRandomValues === 'function') {
|
|
try {
|
|
return crypto.getRandomValues(new Uint32Array(1))[0];
|
|
} catch (err) {}
|
|
}
|
|
|
|
// Use randomBytes method (NodeJS)
|
|
if (typeof crypto.randomBytes === 'function') {
|
|
try {
|
|
return crypto.randomBytes(4).readInt32LE();
|
|
} catch (err) {}
|
|
}
|
|
}
|
|
|
|
throw new Error('Native crypto module could not be used to get secure random number.');
|
|
};
|
|
|
|
/*
|
|
* Local polyfill of Object.create
|
|
|
|
*/
|
|
var create = Object.create || (function () {
|
|
function F() {}
|
|
|
|
return function (obj) {
|
|
var subtype;
|
|
|
|
F.prototype = obj;
|
|
|
|
subtype = new F();
|
|
|
|
F.prototype = null;
|
|
|
|
return subtype;
|
|
};
|
|
}());
|
|
|
|
/**
|
|
* CryptoJS namespace.
|
|
*/
|
|
var C = {};
|
|
|
|
/**
|
|
* Library namespace.
|
|
*/
|
|
var C_lib = C.lib = {};
|
|
|
|
/**
|
|
* Base object for prototypal inheritance.
|
|
*/
|
|
var Base = C_lib.Base = (function () {
|
|
|
|
|
|
return {
|
|
/**
|
|
* Creates a new object that inherits from this object.
|
|
*
|
|
* @param {Object} overrides Properties to copy into the new object.
|
|
*
|
|
* @return {Object} The new object.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var MyType = CryptoJS.lib.Base.extend({
|
|
* field: 'value',
|
|
*
|
|
* method: function () {
|
|
* }
|
|
* });
|
|
*/
|
|
extend: function (overrides) {
|
|
// Spawn
|
|
var subtype = create(this);
|
|
|
|
// Augment
|
|
if (overrides) {
|
|
subtype.mixIn(overrides);
|
|
}
|
|
|
|
// Create default initializer
|
|
if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {
|
|
subtype.init = function () {
|
|
subtype.$super.init.apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
// Initializer's prototype is the subtype object
|
|
subtype.init.prototype = subtype;
|
|
|
|
// Reference supertype
|
|
subtype.$super = this;
|
|
|
|
return subtype;
|
|
},
|
|
|
|
/**
|
|
* Extends this object and runs the init method.
|
|
* Arguments to create() will be passed to init().
|
|
*
|
|
* @return {Object} The new object.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var instance = MyType.create();
|
|
*/
|
|
create: function () {
|
|
var instance = this.extend();
|
|
instance.init.apply(instance, arguments);
|
|
|
|
return instance;
|
|
},
|
|
|
|
/**
|
|
* Initializes a newly created object.
|
|
* Override this method to add some logic when your objects are created.
|
|
*
|
|
* @example
|
|
*
|
|
* var MyType = CryptoJS.lib.Base.extend({
|
|
* init: function () {
|
|
* // ...
|
|
* }
|
|
* });
|
|
*/
|
|
init: function () {
|
|
},
|
|
|
|
/**
|
|
* Copies properties into this object.
|
|
*
|
|
* @param {Object} properties The properties to mix in.
|
|
*
|
|
* @example
|
|
*
|
|
* MyType.mixIn({
|
|
* field: 'value'
|
|
* });
|
|
*/
|
|
mixIn: function (properties) {
|
|
for (var propertyName in properties) {
|
|
if (properties.hasOwnProperty(propertyName)) {
|
|
this[propertyName] = properties[propertyName];
|
|
}
|
|
}
|
|
|
|
// IE won't copy toString using the loop above
|
|
if (properties.hasOwnProperty('toString')) {
|
|
this.toString = properties.toString;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Creates a copy of this object.
|
|
*
|
|
* @return {Object} The clone.
|
|
*
|
|
* @example
|
|
*
|
|
* var clone = instance.clone();
|
|
*/
|
|
clone: function () {
|
|
return this.init.prototype.extend(this);
|
|
}
|
|
};
|
|
}());
|
|
|
|
/**
|
|
* An array of 32-bit words.
|
|
*
|
|
* @property {Array} words The array of 32-bit words.
|
|
* @property {number} sigBytes The number of significant bytes in this word array.
|
|
*/
|
|
var WordArray = C_lib.WordArray = Base.extend({
|
|
/**
|
|
* Initializes a newly created word array.
|
|
*
|
|
* @param {Array} words (Optional) An array of 32-bit words.
|
|
* @param {number} sigBytes (Optional) The number of significant bytes in the words.
|
|
*
|
|
* @example
|
|
*
|
|
* var wordArray = CryptoJS.lib.WordArray.create();
|
|
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);
|
|
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);
|
|
*/
|
|
init: function (words, sigBytes) {
|
|
words = this.words = words || [];
|
|
|
|
if (sigBytes != undefined$1) {
|
|
this.sigBytes = sigBytes;
|
|
} else {
|
|
this.sigBytes = words.length * 4;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Converts this word array to a string.
|
|
*
|
|
* @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex
|
|
*
|
|
* @return {string} The stringified word array.
|
|
*
|
|
* @example
|
|
*
|
|
* var string = wordArray + '';
|
|
* var string = wordArray.toString();
|
|
* var string = wordArray.toString(CryptoJS.enc.Utf8);
|
|
*/
|
|
toString: function (encoder) {
|
|
return (encoder || Hex).stringify(this);
|
|
},
|
|
|
|
/**
|
|
* Concatenates a word array to this word array.
|
|
*
|
|
* @param {WordArray} wordArray The word array to append.
|
|
*
|
|
* @return {WordArray} This word array.
|
|
*
|
|
* @example
|
|
*
|
|
* wordArray1.concat(wordArray2);
|
|
*/
|
|
concat: function (wordArray) {
|
|
// Shortcuts
|
|
var thisWords = this.words;
|
|
var thatWords = wordArray.words;
|
|
var thisSigBytes = this.sigBytes;
|
|
var thatSigBytes = wordArray.sigBytes;
|
|
|
|
// Clamp excess bits
|
|
this.clamp();
|
|
|
|
// Concat
|
|
if (thisSigBytes % 4) {
|
|
// Copy one byte at a time
|
|
for (var i = 0; i < thatSigBytes; i++) {
|
|
var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
|
thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);
|
|
}
|
|
} else {
|
|
// Copy one word at a time
|
|
for (var j = 0; j < thatSigBytes; j += 4) {
|
|
thisWords[(thisSigBytes + j) >>> 2] = thatWords[j >>> 2];
|
|
}
|
|
}
|
|
this.sigBytes += thatSigBytes;
|
|
|
|
// Chainable
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Removes insignificant bits.
|
|
*
|
|
* @example
|
|
*
|
|
* wordArray.clamp();
|
|
*/
|
|
clamp: function () {
|
|
// Shortcuts
|
|
var words = this.words;
|
|
var sigBytes = this.sigBytes;
|
|
|
|
// Clamp
|
|
words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);
|
|
words.length = Math.ceil(sigBytes / 4);
|
|
},
|
|
|
|
/**
|
|
* Creates a copy of this word array.
|
|
*
|
|
* @return {WordArray} The clone.
|
|
*
|
|
* @example
|
|
*
|
|
* var clone = wordArray.clone();
|
|
*/
|
|
clone: function () {
|
|
var clone = Base.clone.call(this);
|
|
clone.words = this.words.slice(0);
|
|
|
|
return clone;
|
|
},
|
|
|
|
/**
|
|
* Creates a word array filled with random bytes.
|
|
*
|
|
* @param {number} nBytes The number of random bytes to generate.
|
|
*
|
|
* @return {WordArray} The random word array.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var wordArray = CryptoJS.lib.WordArray.random(16);
|
|
*/
|
|
random: function (nBytes) {
|
|
var words = [];
|
|
|
|
for (var i = 0; i < nBytes; i += 4) {
|
|
words.push(cryptoSecureRandomInt());
|
|
}
|
|
|
|
return new WordArray.init(words, nBytes);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Encoder namespace.
|
|
*/
|
|
var C_enc = C.enc = {};
|
|
|
|
/**
|
|
* Hex encoding strategy.
|
|
*/
|
|
var Hex = C_enc.Hex = {
|
|
/**
|
|
* Converts a word array to a hex string.
|
|
*
|
|
* @param {WordArray} wordArray The word array.
|
|
*
|
|
* @return {string} The hex string.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var hexString = CryptoJS.enc.Hex.stringify(wordArray);
|
|
*/
|
|
stringify: function (wordArray) {
|
|
// Shortcuts
|
|
var words = wordArray.words;
|
|
var sigBytes = wordArray.sigBytes;
|
|
|
|
// Convert
|
|
var hexChars = [];
|
|
for (var i = 0; i < sigBytes; i++) {
|
|
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
|
hexChars.push((bite >>> 4).toString(16));
|
|
hexChars.push((bite & 0x0f).toString(16));
|
|
}
|
|
|
|
return hexChars.join('');
|
|
},
|
|
|
|
/**
|
|
* Converts a hex string to a word array.
|
|
*
|
|
* @param {string} hexStr The hex string.
|
|
*
|
|
* @return {WordArray} The word array.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var wordArray = CryptoJS.enc.Hex.parse(hexString);
|
|
*/
|
|
parse: function (hexStr) {
|
|
// Shortcut
|
|
var hexStrLength = hexStr.length;
|
|
|
|
// Convert
|
|
var words = [];
|
|
for (var i = 0; i < hexStrLength; i += 2) {
|
|
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
|
|
}
|
|
|
|
return new WordArray.init(words, hexStrLength / 2);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Latin1 encoding strategy.
|
|
*/
|
|
var Latin1 = C_enc.Latin1 = {
|
|
/**
|
|
* Converts a word array to a Latin1 string.
|
|
*
|
|
* @param {WordArray} wordArray The word array.
|
|
*
|
|
* @return {string} The Latin1 string.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);
|
|
*/
|
|
stringify: function (wordArray) {
|
|
// Shortcuts
|
|
var words = wordArray.words;
|
|
var sigBytes = wordArray.sigBytes;
|
|
|
|
// Convert
|
|
var latin1Chars = [];
|
|
for (var i = 0; i < sigBytes; i++) {
|
|
var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
|
latin1Chars.push(String.fromCharCode(bite));
|
|
}
|
|
|
|
return latin1Chars.join('');
|
|
},
|
|
|
|
/**
|
|
* Converts a Latin1 string to a word array.
|
|
*
|
|
* @param {string} latin1Str The Latin1 string.
|
|
*
|
|
* @return {WordArray} The word array.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var wordArray = CryptoJS.enc.Latin1.parse(latin1String);
|
|
*/
|
|
parse: function (latin1Str) {
|
|
// Shortcut
|
|
var latin1StrLength = latin1Str.length;
|
|
|
|
// Convert
|
|
var words = [];
|
|
for (var i = 0; i < latin1StrLength; i++) {
|
|
words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);
|
|
}
|
|
|
|
return new WordArray.init(words, latin1StrLength);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* UTF-8 encoding strategy.
|
|
*/
|
|
var Utf8 = C_enc.Utf8 = {
|
|
/**
|
|
* Converts a word array to a UTF-8 string.
|
|
*
|
|
* @param {WordArray} wordArray The word array.
|
|
*
|
|
* @return {string} The UTF-8 string.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);
|
|
*/
|
|
stringify: function (wordArray) {
|
|
try {
|
|
return decodeURIComponent(escape(Latin1.stringify(wordArray)));
|
|
} catch (e) {
|
|
throw new Error('Malformed UTF-8 data');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Converts a UTF-8 string to a word array.
|
|
*
|
|
* @param {string} utf8Str The UTF-8 string.
|
|
*
|
|
* @return {WordArray} The word array.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var wordArray = CryptoJS.enc.Utf8.parse(utf8String);
|
|
*/
|
|
parse: function (utf8Str) {
|
|
return Latin1.parse(unescape(encodeURIComponent(utf8Str)));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Abstract buffered block algorithm template.
|
|
*
|
|
* The property blockSize must be implemented in a concrete subtype.
|
|
*
|
|
* @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0
|
|
*/
|
|
var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({
|
|
/**
|
|
* Resets this block algorithm's data buffer to its initial state.
|
|
*
|
|
* @example
|
|
*
|
|
* bufferedBlockAlgorithm.reset();
|
|
*/
|
|
reset: function () {
|
|
// Initial values
|
|
this._data = new WordArray.init();
|
|
this._nDataBytes = 0;
|
|
},
|
|
|
|
/**
|
|
* Adds new data to this block algorithm's buffer.
|
|
*
|
|
* @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.
|
|
*
|
|
* @example
|
|
*
|
|
* bufferedBlockAlgorithm._append('data');
|
|
* bufferedBlockAlgorithm._append(wordArray);
|
|
*/
|
|
_append: function (data) {
|
|
// Convert string to WordArray, else assume WordArray already
|
|
if (typeof data == 'string') {
|
|
data = Utf8.parse(data);
|
|
}
|
|
|
|
// Append
|
|
this._data.concat(data);
|
|
this._nDataBytes += data.sigBytes;
|
|
},
|
|
|
|
/**
|
|
* Processes available data blocks.
|
|
*
|
|
* This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.
|
|
*
|
|
* @param {boolean} doFlush Whether all blocks and partial blocks should be processed.
|
|
*
|
|
* @return {WordArray} The processed data.
|
|
*
|
|
* @example
|
|
*
|
|
* var processedData = bufferedBlockAlgorithm._process();
|
|
* var processedData = bufferedBlockAlgorithm._process(!!'flush');
|
|
*/
|
|
_process: function (doFlush) {
|
|
var processedWords;
|
|
|
|
// Shortcuts
|
|
var data = this._data;
|
|
var dataWords = data.words;
|
|
var dataSigBytes = data.sigBytes;
|
|
var blockSize = this.blockSize;
|
|
var blockSizeBytes = blockSize * 4;
|
|
|
|
// Count blocks ready
|
|
var nBlocksReady = dataSigBytes / blockSizeBytes;
|
|
if (doFlush) {
|
|
// Round up to include partial blocks
|
|
nBlocksReady = Math.ceil(nBlocksReady);
|
|
} else {
|
|
// Round down to include only full blocks,
|
|
// less the number of blocks that must remain in the buffer
|
|
nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);
|
|
}
|
|
|
|
// Count words ready
|
|
var nWordsReady = nBlocksReady * blockSize;
|
|
|
|
// Count bytes ready
|
|
var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);
|
|
|
|
// Process blocks
|
|
if (nWordsReady) {
|
|
for (var offset = 0; offset < nWordsReady; offset += blockSize) {
|
|
// Perform concrete-algorithm logic
|
|
this._doProcessBlock(dataWords, offset);
|
|
}
|
|
|
|
// Remove processed words
|
|
processedWords = dataWords.splice(0, nWordsReady);
|
|
data.sigBytes -= nBytesReady;
|
|
}
|
|
|
|
// Return processed words
|
|
return new WordArray.init(processedWords, nBytesReady);
|
|
},
|
|
|
|
/**
|
|
* Creates a copy of this object.
|
|
*
|
|
* @return {Object} The clone.
|
|
*
|
|
* @example
|
|
*
|
|
* var clone = bufferedBlockAlgorithm.clone();
|
|
*/
|
|
clone: function () {
|
|
var clone = Base.clone.call(this);
|
|
clone._data = this._data.clone();
|
|
|
|
return clone;
|
|
},
|
|
|
|
_minBufferSize: 0
|
|
});
|
|
|
|
/**
|
|
* Abstract hasher template.
|
|
*
|
|
* @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)
|
|
*/
|
|
C_lib.Hasher = BufferedBlockAlgorithm.extend({
|
|
/**
|
|
* Configuration options.
|
|
*/
|
|
cfg: Base.extend(),
|
|
|
|
/**
|
|
* Initializes a newly created hasher.
|
|
*
|
|
* @param {Object} cfg (Optional) The configuration options to use for this hash computation.
|
|
*
|
|
* @example
|
|
*
|
|
* var hasher = CryptoJS.algo.SHA256.create();
|
|
*/
|
|
init: function (cfg) {
|
|
// Apply config defaults
|
|
this.cfg = this.cfg.extend(cfg);
|
|
|
|
// Set initial values
|
|
this.reset();
|
|
},
|
|
|
|
/**
|
|
* Resets this hasher to its initial state.
|
|
*
|
|
* @example
|
|
*
|
|
* hasher.reset();
|
|
*/
|
|
reset: function () {
|
|
// Reset data buffer
|
|
BufferedBlockAlgorithm.reset.call(this);
|
|
|
|
// Perform concrete-hasher logic
|
|
this._doReset();
|
|
},
|
|
|
|
/**
|
|
* Updates this hasher with a message.
|
|
*
|
|
* @param {WordArray|string} messageUpdate The message to append.
|
|
*
|
|
* @return {Hasher} This hasher.
|
|
*
|
|
* @example
|
|
*
|
|
* hasher.update('message');
|
|
* hasher.update(wordArray);
|
|
*/
|
|
update: function (messageUpdate) {
|
|
// Append
|
|
this._append(messageUpdate);
|
|
|
|
// Update the hash
|
|
this._process();
|
|
|
|
// Chainable
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Finalizes the hash computation.
|
|
* Note that the finalize operation is effectively a destructive, read-once operation.
|
|
*
|
|
* @param {WordArray|string} messageUpdate (Optional) A final message update.
|
|
*
|
|
* @return {WordArray} The hash.
|
|
*
|
|
* @example
|
|
*
|
|
* var hash = hasher.finalize();
|
|
* var hash = hasher.finalize('message');
|
|
* var hash = hasher.finalize(wordArray);
|
|
*/
|
|
finalize: function (messageUpdate) {
|
|
// Final message update
|
|
if (messageUpdate) {
|
|
this._append(messageUpdate);
|
|
}
|
|
|
|
// Perform concrete-hasher logic
|
|
var hash = this._doFinalize();
|
|
|
|
return hash;
|
|
},
|
|
|
|
blockSize: 512/32,
|
|
|
|
/**
|
|
* Creates a shortcut function to a hasher's object interface.
|
|
*
|
|
* @param {Hasher} hasher The hasher to create a helper for.
|
|
*
|
|
* @return {Function} The shortcut function.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);
|
|
*/
|
|
_createHelper: function (hasher) {
|
|
return function (message, cfg) {
|
|
return new hasher.init(cfg).finalize(message);
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Creates a shortcut function to the HMAC's object interface.
|
|
*
|
|
* @param {Hasher} hasher The hasher to use in this HMAC helper.
|
|
*
|
|
* @return {Function} The shortcut function.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);
|
|
*/
|
|
_createHmacHelper: function (hasher) {
|
|
return function (message, key) {
|
|
return new C_algo.HMAC.init(hasher, key).finalize(message);
|
|
};
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Algorithm namespace.
|
|
*/
|
|
var C_algo = C.algo = {};
|
|
|
|
return C;
|
|
}(Math));
|
|
|
|
|
|
return CryptoJS;
|
|
|
|
}));
|
|
} (core, core.exports));
|
|
return core.exports;
|
|
}
|
|
|
|
md5$1.exports;
|
|
|
|
(function (module, exports) {
|
|
(function (root, factory) {
|
|
{
|
|
// CommonJS
|
|
module.exports = factory(requireCore());
|
|
}
|
|
}(commonjsGlobal, function (CryptoJS) {
|
|
|
|
(function (Math) {
|
|
// Shortcuts
|
|
var C = CryptoJS;
|
|
var C_lib = C.lib;
|
|
var WordArray = C_lib.WordArray;
|
|
var Hasher = C_lib.Hasher;
|
|
var C_algo = C.algo;
|
|
|
|
// Constants table
|
|
var T = [];
|
|
|
|
// Compute constants
|
|
(function () {
|
|
for (var i = 0; i < 64; i++) {
|
|
T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;
|
|
}
|
|
}());
|
|
|
|
/**
|
|
* MD5 hash algorithm.
|
|
*/
|
|
var MD5 = C_algo.MD5 = Hasher.extend({
|
|
_doReset: function () {
|
|
this._hash = new WordArray.init([
|
|
0x67452301, 0xefcdab89,
|
|
0x98badcfe, 0x10325476
|
|
]);
|
|
},
|
|
|
|
_doProcessBlock: function (M, offset) {
|
|
// Swap endian
|
|
for (var i = 0; i < 16; i++) {
|
|
// Shortcuts
|
|
var offset_i = offset + i;
|
|
var M_offset_i = M[offset_i];
|
|
|
|
M[offset_i] = (
|
|
(((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
|
|
(((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
|
|
);
|
|
}
|
|
|
|
// Shortcuts
|
|
var H = this._hash.words;
|
|
|
|
var M_offset_0 = M[offset + 0];
|
|
var M_offset_1 = M[offset + 1];
|
|
var M_offset_2 = M[offset + 2];
|
|
var M_offset_3 = M[offset + 3];
|
|
var M_offset_4 = M[offset + 4];
|
|
var M_offset_5 = M[offset + 5];
|
|
var M_offset_6 = M[offset + 6];
|
|
var M_offset_7 = M[offset + 7];
|
|
var M_offset_8 = M[offset + 8];
|
|
var M_offset_9 = M[offset + 9];
|
|
var M_offset_10 = M[offset + 10];
|
|
var M_offset_11 = M[offset + 11];
|
|
var M_offset_12 = M[offset + 12];
|
|
var M_offset_13 = M[offset + 13];
|
|
var M_offset_14 = M[offset + 14];
|
|
var M_offset_15 = M[offset + 15];
|
|
|
|
// Working variables
|
|
var a = H[0];
|
|
var b = H[1];
|
|
var c = H[2];
|
|
var d = H[3];
|
|
|
|
// Computation
|
|
a = FF(a, b, c, d, M_offset_0, 7, T[0]);
|
|
d = FF(d, a, b, c, M_offset_1, 12, T[1]);
|
|
c = FF(c, d, a, b, M_offset_2, 17, T[2]);
|
|
b = FF(b, c, d, a, M_offset_3, 22, T[3]);
|
|
a = FF(a, b, c, d, M_offset_4, 7, T[4]);
|
|
d = FF(d, a, b, c, M_offset_5, 12, T[5]);
|
|
c = FF(c, d, a, b, M_offset_6, 17, T[6]);
|
|
b = FF(b, c, d, a, M_offset_7, 22, T[7]);
|
|
a = FF(a, b, c, d, M_offset_8, 7, T[8]);
|
|
d = FF(d, a, b, c, M_offset_9, 12, T[9]);
|
|
c = FF(c, d, a, b, M_offset_10, 17, T[10]);
|
|
b = FF(b, c, d, a, M_offset_11, 22, T[11]);
|
|
a = FF(a, b, c, d, M_offset_12, 7, T[12]);
|
|
d = FF(d, a, b, c, M_offset_13, 12, T[13]);
|
|
c = FF(c, d, a, b, M_offset_14, 17, T[14]);
|
|
b = FF(b, c, d, a, M_offset_15, 22, T[15]);
|
|
|
|
a = GG(a, b, c, d, M_offset_1, 5, T[16]);
|
|
d = GG(d, a, b, c, M_offset_6, 9, T[17]);
|
|
c = GG(c, d, a, b, M_offset_11, 14, T[18]);
|
|
b = GG(b, c, d, a, M_offset_0, 20, T[19]);
|
|
a = GG(a, b, c, d, M_offset_5, 5, T[20]);
|
|
d = GG(d, a, b, c, M_offset_10, 9, T[21]);
|
|
c = GG(c, d, a, b, M_offset_15, 14, T[22]);
|
|
b = GG(b, c, d, a, M_offset_4, 20, T[23]);
|
|
a = GG(a, b, c, d, M_offset_9, 5, T[24]);
|
|
d = GG(d, a, b, c, M_offset_14, 9, T[25]);
|
|
c = GG(c, d, a, b, M_offset_3, 14, T[26]);
|
|
b = GG(b, c, d, a, M_offset_8, 20, T[27]);
|
|
a = GG(a, b, c, d, M_offset_13, 5, T[28]);
|
|
d = GG(d, a, b, c, M_offset_2, 9, T[29]);
|
|
c = GG(c, d, a, b, M_offset_7, 14, T[30]);
|
|
b = GG(b, c, d, a, M_offset_12, 20, T[31]);
|
|
|
|
a = HH(a, b, c, d, M_offset_5, 4, T[32]);
|
|
d = HH(d, a, b, c, M_offset_8, 11, T[33]);
|
|
c = HH(c, d, a, b, M_offset_11, 16, T[34]);
|
|
b = HH(b, c, d, a, M_offset_14, 23, T[35]);
|
|
a = HH(a, b, c, d, M_offset_1, 4, T[36]);
|
|
d = HH(d, a, b, c, M_offset_4, 11, T[37]);
|
|
c = HH(c, d, a, b, M_offset_7, 16, T[38]);
|
|
b = HH(b, c, d, a, M_offset_10, 23, T[39]);
|
|
a = HH(a, b, c, d, M_offset_13, 4, T[40]);
|
|
d = HH(d, a, b, c, M_offset_0, 11, T[41]);
|
|
c = HH(c, d, a, b, M_offset_3, 16, T[42]);
|
|
b = HH(b, c, d, a, M_offset_6, 23, T[43]);
|
|
a = HH(a, b, c, d, M_offset_9, 4, T[44]);
|
|
d = HH(d, a, b, c, M_offset_12, 11, T[45]);
|
|
c = HH(c, d, a, b, M_offset_15, 16, T[46]);
|
|
b = HH(b, c, d, a, M_offset_2, 23, T[47]);
|
|
|
|
a = II(a, b, c, d, M_offset_0, 6, T[48]);
|
|
d = II(d, a, b, c, M_offset_7, 10, T[49]);
|
|
c = II(c, d, a, b, M_offset_14, 15, T[50]);
|
|
b = II(b, c, d, a, M_offset_5, 21, T[51]);
|
|
a = II(a, b, c, d, M_offset_12, 6, T[52]);
|
|
d = II(d, a, b, c, M_offset_3, 10, T[53]);
|
|
c = II(c, d, a, b, M_offset_10, 15, T[54]);
|
|
b = II(b, c, d, a, M_offset_1, 21, T[55]);
|
|
a = II(a, b, c, d, M_offset_8, 6, T[56]);
|
|
d = II(d, a, b, c, M_offset_15, 10, T[57]);
|
|
c = II(c, d, a, b, M_offset_6, 15, T[58]);
|
|
b = II(b, c, d, a, M_offset_13, 21, T[59]);
|
|
a = II(a, b, c, d, M_offset_4, 6, T[60]);
|
|
d = II(d, a, b, c, M_offset_11, 10, T[61]);
|
|
c = II(c, d, a, b, M_offset_2, 15, T[62]);
|
|
b = II(b, c, d, a, M_offset_9, 21, T[63]);
|
|
|
|
// Intermediate hash value
|
|
H[0] = (H[0] + a) | 0;
|
|
H[1] = (H[1] + b) | 0;
|
|
H[2] = (H[2] + c) | 0;
|
|
H[3] = (H[3] + d) | 0;
|
|
},
|
|
|
|
_doFinalize: function () {
|
|
// Shortcuts
|
|
var data = this._data;
|
|
var dataWords = data.words;
|
|
|
|
var nBitsTotal = this._nDataBytes * 8;
|
|
var nBitsLeft = data.sigBytes * 8;
|
|
|
|
// Add padding
|
|
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
|
|
|
var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);
|
|
var nBitsTotalL = nBitsTotal;
|
|
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = (
|
|
(((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) |
|
|
(((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00)
|
|
);
|
|
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
|
|
(((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) |
|
|
(((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00)
|
|
);
|
|
|
|
data.sigBytes = (dataWords.length + 1) * 4;
|
|
|
|
// Hash final blocks
|
|
this._process();
|
|
|
|
// Shortcuts
|
|
var hash = this._hash;
|
|
var H = hash.words;
|
|
|
|
// Swap endian
|
|
for (var i = 0; i < 4; i++) {
|
|
// Shortcut
|
|
var H_i = H[i];
|
|
|
|
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
|
|
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
|
|
}
|
|
|
|
// Return final computed hash
|
|
return hash;
|
|
},
|
|
|
|
clone: function () {
|
|
var clone = Hasher.clone.call(this);
|
|
clone._hash = this._hash.clone();
|
|
|
|
return clone;
|
|
}
|
|
});
|
|
|
|
function FF(a, b, c, d, x, s, t) {
|
|
var n = a + ((b & c) | (~b & d)) + x + t;
|
|
return ((n << s) | (n >>> (32 - s))) + b;
|
|
}
|
|
|
|
function GG(a, b, c, d, x, s, t) {
|
|
var n = a + ((b & d) | (c & ~d)) + x + t;
|
|
return ((n << s) | (n >>> (32 - s))) + b;
|
|
}
|
|
|
|
function HH(a, b, c, d, x, s, t) {
|
|
var n = a + (b ^ c ^ d) + x + t;
|
|
return ((n << s) | (n >>> (32 - s))) + b;
|
|
}
|
|
|
|
function II(a, b, c, d, x, s, t) {
|
|
var n = a + (c ^ (b | ~d)) + x + t;
|
|
return ((n << s) | (n >>> (32 - s))) + b;
|
|
}
|
|
|
|
/**
|
|
* Shortcut function to the hasher's object interface.
|
|
*
|
|
* @param {WordArray|string} message The message to hash.
|
|
*
|
|
* @return {WordArray} The hash.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var hash = CryptoJS.MD5('message');
|
|
* var hash = CryptoJS.MD5(wordArray);
|
|
*/
|
|
C.MD5 = Hasher._createHelper(MD5);
|
|
|
|
/**
|
|
* Shortcut function to the HMAC's object interface.
|
|
*
|
|
* @param {WordArray|string} message The message to hash.
|
|
* @param {WordArray|string} key The secret key.
|
|
*
|
|
* @return {WordArray} The HMAC.
|
|
*
|
|
* @static
|
|
*
|
|
* @example
|
|
*
|
|
* var hmac = CryptoJS.HmacMD5(message, key);
|
|
*/
|
|
C.HmacMD5 = Hasher._createHmacHelper(MD5);
|
|
}(Math));
|
|
|
|
|
|
return CryptoJS.MD5;
|
|
|
|
}));
|
|
} (md5$1, md5$1.exports));
|
|
|
|
var md5Exports = md5$1.exports;
|
|
var md5 = /*@__PURE__*/getDefaultExportFromCjs(md5Exports);
|
|
|
|
const APP_TITLE = "Local Images Plus 0.16.3";
|
|
//Option to enable debugging
|
|
let VERBOSE = false;
|
|
function setDebug(value = false) {
|
|
VERBOSE = value;
|
|
}
|
|
const SUPPORTED_OS = { "win": "win32", "unix": "linux,darwin,freebsd,openbsd" };
|
|
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36';
|
|
const MD_SEARCH_PATTERN = [
|
|
//file link
|
|
/\!\[(?<anchor>(.{0}|(?!^file\:\/)+?))\]\((?<link>((file\:\/)[^\!]+?(\.{1}.{3,4}\) {0,1}|\)$|\)\n|\)])))/gm,
|
|
//hypertext link
|
|
///\!\[(?<anchor>(.{0}|[^\[]+?))\]\((?<link>((http(s){0,1}).+?(\) |\..{3,4}\)|\)$|\)\n|\)\]|\)\[)))/gm,
|
|
/\!\[(?<anchor>([^\]]*))\]\((?<link>((http(s){0,1}).+?(\) |\..{3,4}\)|\)$|\)\n|\)\]|\)\[)))/gm,
|
|
//Base64 encoded data
|
|
/\!\[[^\[](?<anchor>(.{0}|[^\[]+?))\]\((?<link>((data\:.+?base64\,).+?(\) |\..{3,4}\)|\)$|\)\n|\)\]|\)\[)))/gm,
|
|
/\!\[(?<anchor>(.{0}|[^\[]+?))\]\((?<link>((http(s){0,1}|(data\:.+?base64\,)).+?\)))/gm
|
|
];
|
|
const MD_LINK = /\http(s){0,1}.+?( {1}|\)\n)/g;
|
|
const ATT_SIZE_ACHOR = /(^(?<attdesc>.{1,})\|(?<attsize>[0-9]{2,4})$)|(?<attsize2>^[0-9]{2,4}$)/gm;
|
|
// Looks like timeouts in Obsidian API are set in milliseconds
|
|
const NOTICE_TIMEOUT = 5 * 1000;
|
|
const TIMEOUT_LIKE_INFINITY = 24 * 60 * 60 * 1000;
|
|
const FORBIDDEN_SYMBOLS_FILENAME_PATTERN = /\s+/g;
|
|
const DEFAULT_SETTINGS = {
|
|
processCreated: true,
|
|
ignoredExt: "cnt|php|htm|html",
|
|
processAll: true,
|
|
useCaptions: true,
|
|
pathInTags: "fullDirPath",
|
|
downUnknown: false,
|
|
saveAttE: "obsFolder",
|
|
realTimeUpdate: true,
|
|
filesizeLimit: 0,
|
|
tryCount: 2,
|
|
realTimeUpdateInterval: 5,
|
|
addNameOfFile: true,
|
|
showNotifications: true,
|
|
includeps: "md|canvas",
|
|
includepattern: "(?<md>.*\\.md)|(?<canvas>.*\\.canvas)",
|
|
mediaRootDir: "_resources/${notename}",
|
|
disAddCom: false,
|
|
useMD5ForNewAtt: true,
|
|
removeMediaFolder: true,
|
|
removeOrphansCompl: false,
|
|
PngToJpeg: false,
|
|
PngToJpegLocal: true,
|
|
JpegQuality: 80,
|
|
DoNotCreateObsFolder: false,
|
|
DateFormat: "YYYY MM DD"
|
|
};
|
|
|
|
const fs2 = require('fs').promises;
|
|
//import { TIMEOUT } from "dns";
|
|
//import fs from "fs";
|
|
/*
|
|
https://stackoverflow.com/a/48032528/1020973
|
|
It will be better to do it type-correct.
|
|
*/
|
|
function showBalloon(str, show = true, timeout = NOTICE_TIMEOUT) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (show) {
|
|
new obsidian.Notice(APP_TITLE + "\r\n" + str, timeout);
|
|
}
|
|
});
|
|
}
|
|
function displayError(error, file) {
|
|
if (file) {
|
|
showBalloon(`LocalImagesPlus: Error while handling file ${file.name}, ${error.toString()}`);
|
|
}
|
|
else {
|
|
showBalloon(error.toString());
|
|
}
|
|
logError(`LocalImagesPlus: error: ${error}`, false);
|
|
}
|
|
function logError(str, isObj = false) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (VERBOSE) {
|
|
console.log(APP_TITLE + ": ");
|
|
if (isObj) {
|
|
console.table(str);
|
|
}
|
|
else {
|
|
console.log(str);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function md5Sig(contentData = undefined) {
|
|
try {
|
|
var dec = new TextDecoder("utf-8");
|
|
const arrMid = Math.round(contentData.byteLength / 2);
|
|
const chunk = 15000;
|
|
const signature = md5([
|
|
contentData.slice(0, chunk),
|
|
contentData.slice(arrMid, arrMid + chunk),
|
|
contentData.slice(-chunk)
|
|
].map(x => dec.decode(x)).join()).toString();
|
|
return signature + "_MD5";
|
|
}
|
|
catch (e) {
|
|
logError("Cannot generate md5: " + e, false);
|
|
return null;
|
|
}
|
|
}
|
|
function replaceAsync(str, regex, asyncFn) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
logError("replaceAsync: \r\nstr: " + str + "\r\nregex: ");
|
|
logError(regex, true);
|
|
let errorflag = false;
|
|
const promises = [];
|
|
let dictPatt = [];
|
|
let link;
|
|
let anchor;
|
|
let replp;
|
|
let caption = "";
|
|
let filesArr = [];
|
|
let AttSize = "";
|
|
regex.forEach((element) => {
|
|
var _a;
|
|
logError("cur regex: " + element);
|
|
const matches = str.matchAll(element);
|
|
for (const match of matches) {
|
|
logError("match: " + match);
|
|
anchor = trimAny(match.groups.anchor, [")", "(", "]", "[", " "]);
|
|
const AttSizeMatch = anchor.matchAll(ATT_SIZE_ACHOR);
|
|
for (const match of AttSizeMatch) {
|
|
AttSize = (match.groups.attsize !== undefined) ? trimAny(match.groups.attsize, [")", "(", "]", "[", " "]) :
|
|
(match.groups.attsize2 !== undefined) ? trimAny(match.groups.attsize2, [")", "(", "]", "[", " "]) :
|
|
"";
|
|
}
|
|
link = ((_a = match.groups.link.match(MD_LINK)) !== null && _a !== void 0 ? _a : [match.groups.link])[0];
|
|
caption = trimAny((match.groups.link.match(MD_LINK) !== null ?
|
|
(match.groups.link.split(link).length > 1 ?
|
|
match.groups.link.split(link)[1] : "") :
|
|
""), [")", "]", "(", "[", " "]);
|
|
link = trimAny(link, [")", "(", "]", "[", " "]);
|
|
replp = trimAny(match[0], ["[", "(", "]"]);
|
|
logError("repl: " + replp +
|
|
"\r\nahc: " + anchor +
|
|
"\r\nlink: " + link +
|
|
"\r\ncaption: " + caption +
|
|
"\r\nAttSize: " + AttSize);
|
|
dictPatt[replp] = [anchor, link, caption, AttSize];
|
|
}
|
|
});
|
|
for (var key in dictPatt) {
|
|
const promise = asyncFn(key, dictPatt[key][0], dictPatt[key][1], dictPatt[key][2], dictPatt[key][3]);
|
|
logError(promise, true);
|
|
promises.push(promise);
|
|
}
|
|
const data = yield Promise.all(promises);
|
|
logError("Promises: ");
|
|
logError(data, true);
|
|
// return str.replace((reg: RegExp, str: String) => {
|
|
data.forEach((element) => {
|
|
if (element !== null) {
|
|
logError("el: " + element[0] + " el2: " + element[1] + element[2]);
|
|
str = str.replaceAll(element[0], element[1] + element[2]);
|
|
filesArr.push(element[1]);
|
|
}
|
|
else {
|
|
errorflag = true;
|
|
}
|
|
});
|
|
return [str, errorflag, filesArr];
|
|
// return str.replace( () => data.shift());
|
|
});
|
|
}
|
|
function isUrl(link) {
|
|
logError("IsUrl: " + link, false);
|
|
try {
|
|
return Boolean(new URL(link));
|
|
}
|
|
catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
function base64ToBuff(data) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
logError("base64ToBuff: \r\n", false);
|
|
try {
|
|
const BufferData = Buffer.from(data.split("base64,")[1], 'base64');
|
|
logError(BufferData);
|
|
return BufferData;
|
|
}
|
|
catch (e) {
|
|
logError("Cannot read base64: " + e, false);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
function readFromDiskB(file, count = undefined) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
try {
|
|
const buffer = Buffer.alloc(count);
|
|
const fd = fs__default["default"].openSync(file, "r+");
|
|
fs__default["default"].readSync(fd, buffer, 0, buffer.length, 0);
|
|
logError(buffer);
|
|
fs__default["default"].closeSync(fd);
|
|
return buffer;
|
|
}
|
|
catch (e) {
|
|
logError("Cannot read the file: " + e, false);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
function readFromDisk(file) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
logError("readFromDisk: " + file, false);
|
|
try {
|
|
const data = yield fs2.readFile(file, null);
|
|
return Buffer.from(data);
|
|
}
|
|
catch (e) {
|
|
logError("Cannot read the file: " + e, false);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
function downloadImage(url) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
logError("Downloading: " + url, false);
|
|
const headers = {
|
|
'method': 'GET',
|
|
'User-Agent': USER_AGENT
|
|
};
|
|
try {
|
|
const res = yield obsidian.requestUrl({ url: url, headers });
|
|
logError(res, true);
|
|
return res.arrayBuffer;
|
|
}
|
|
catch (e) {
|
|
logError("Cannot download the file: " + e, false);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
function getFileExt(content, link) {
|
|
var _a;
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const fileExtByLink = path__default["default"].extname(link).replace("\.", "");
|
|
const fileExtByBuffer = (_a = (yield fileType_1.fromBuffer(content))) === null || _a === void 0 ? void 0 : _a.ext;
|
|
// if XML, probably it is SVG
|
|
if (fileExtByBuffer == "xml" || !fileExtByBuffer) {
|
|
const buffer = Buffer.from(content);
|
|
if (isSvg$1(buffer))
|
|
return "svg";
|
|
}
|
|
logError("fileExtByBuffer" + fileExtByBuffer);
|
|
if (fileExtByBuffer != undefined && fileExtByBuffer && fileExtByBuffer.length <= 5 && (fileExtByBuffer === null || fileExtByBuffer === void 0 ? void 0 : fileExtByBuffer.length) > 0) {
|
|
return fileExtByBuffer;
|
|
}
|
|
logError("fileExtByLink " + fileExtByLink);
|
|
if (fileExtByLink != undefined && fileExtByLink.length <= 5 && (fileExtByLink === null || fileExtByLink === void 0 ? void 0 : fileExtByLink.length) > 0) {
|
|
return fileExtByLink;
|
|
}
|
|
return "unknown";
|
|
});
|
|
}
|
|
//https://stackoverflow.com/questions/26156292/trim-specific-character-from-a-string
|
|
function trimAny(str, chars) {
|
|
var start = 0, end = str.length;
|
|
while (start < end && chars.indexOf(str[start]) >= 0)
|
|
++start;
|
|
while (end > start && chars.indexOf(str[end - 1]) >= 0)
|
|
--end;
|
|
return (start > 0 || end < str.length) ? str.substring(start, end) : str;
|
|
}
|
|
function cFileName(name) {
|
|
const cleanedName = name.replace(/(\)|\(|\"|\'|\#|\]|\[|\:|\>|\<|\*|\|)/g, " ");
|
|
return cleanedName;
|
|
}
|
|
function cleanFileName(name) {
|
|
const cleanedName = filenamify(name).replace(FORBIDDEN_SYMBOLS_FILENAME_PATTERN, "_");
|
|
return cleanedName;
|
|
}
|
|
function pathJoin(parts) {
|
|
const result = path__default["default"].join(...parts);
|
|
// it seems that obsidian do not understand paths with backslashes in Windows, so turn them into forward slashes
|
|
return result.replace(/\\/g, "/");
|
|
}
|
|
function normalizePath(path) {
|
|
return path.replace(/\\/g, "/");
|
|
}
|
|
function encObsURI(e) {
|
|
return e.replace(/[\\\x00\x08\x0B\x0C\x0E-\x1F ]/g, (function (e) {
|
|
return encodeURIComponent(e);
|
|
}));
|
|
}
|
|
/**
|
|
* https://github.com/mnaoumov/obsidian-dev-utils
|
|
*
|
|
* Converts a Blob object to a JPEG ArrayBuffer with the specified quality.
|
|
*
|
|
* @param blob - The Blob object to convert.
|
|
* @param jpegQuality - The quality of the JPEG image (0 to 1).
|
|
* @returns A promise that resolves to an ArrayBuffer.
|
|
*/
|
|
function blobToJpegArrayBuffer(blob, jpegQuality) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
return new Promise((resolve) => {
|
|
const reader = new FileReader();
|
|
reader.onloadend = () => {
|
|
const image = new Image();
|
|
image.onload = () => {
|
|
const canvas = document.createElement('canvas');
|
|
const context = canvas.getContext('2d');
|
|
if (!context) {
|
|
throw new Error('Could not get 2D context.');
|
|
}
|
|
const imageWidth = image.width;
|
|
const imageHeight = image.height;
|
|
let data = '';
|
|
canvas.width = imageWidth;
|
|
canvas.height = imageHeight;
|
|
context.fillStyle = '#fff';
|
|
context.fillRect(0, 0, imageWidth, imageHeight);
|
|
context.save();
|
|
context.translate(imageWidth / 2, imageHeight / 2);
|
|
context.drawImage(image, 0, 0, imageWidth, imageHeight, -imageWidth / 2, -imageHeight / 2, imageWidth, imageHeight);
|
|
context.restore();
|
|
data = canvas.toDataURL('image/jpeg', jpegQuality);
|
|
const arrayBuffer = base64ToBuff(data);
|
|
resolve(arrayBuffer);
|
|
};
|
|
image.src = reader.result;
|
|
};
|
|
reader.readAsDataURL(blob);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Generic options
|
|
class AnalyzerOptions {
|
|
constructor(heuristic_replimit) {
|
|
this.heuristic_replimit = heuristic_replimit;
|
|
}
|
|
}
|
|
|
|
// Abstract class
|
|
class Analyzer {
|
|
constructor(analyzerOptions) {
|
|
this.options = analyzerOptions;
|
|
}
|
|
|
|
// Subclasser must implement
|
|
// Return boolean
|
|
isVulnerable(regExp) {
|
|
return false;
|
|
}
|
|
|
|
// Subclass must implement
|
|
// Returns an AttackString or null
|
|
genAttackString(regExp) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
var analyzer$2 = function(re, replimit) {
|
|
// Build an AST
|
|
let myRegExp = null;
|
|
let ast = null;
|
|
try {
|
|
// Construct a RegExp object
|
|
if (re instanceof RegExp) {
|
|
myRegExp = re;
|
|
} else if (typeof re === "string") {
|
|
myRegExp = new RegExp(re);
|
|
} else {
|
|
myRegExp = new RegExp(String(re));
|
|
}
|
|
|
|
// Build an AST
|
|
ast = regexpTree.parse(myRegExp);
|
|
} catch (err) {
|
|
// Invalid or unparseable input
|
|
return false;
|
|
}
|
|
|
|
let currentStarHeight = 0;
|
|
let maxObservedStarHeight = 0;
|
|
|
|
let repetitionCount = 0;
|
|
|
|
regexpTree.traverse(ast, {
|
|
Repetition: {
|
|
pre({ node }) {
|
|
repetitionCount++;
|
|
|
|
currentStarHeight++;
|
|
if (maxObservedStarHeight < currentStarHeight) {
|
|
maxObservedStarHeight = currentStarHeight;
|
|
}
|
|
},
|
|
|
|
post({ node }) {
|
|
currentStarHeight--;
|
|
}
|
|
}
|
|
});
|
|
|
|
return maxObservedStarHeight <= 1 && repetitionCount <= replimit;
|
|
};
|
|
|
|
analyzer$2 = {
|
|
"AnalyzerOptions": AnalyzerOptions,
|
|
"Analyzer": Analyzer,
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to translate `/./s` to `/[\0-\uFFFF]/`.
|
|
*/
|
|
|
|
var compatDotallSTransform = {
|
|
|
|
// Whether `u` flag present. In which case we transform to
|
|
// \u{10FFFF} instead of \uFFFF.
|
|
_hasUFlag: false,
|
|
|
|
// Only run this plugin if we have `s` flag.
|
|
shouldRun: function shouldRun(ast) {
|
|
var shouldRun = ast.flags.includes('s');
|
|
|
|
if (!shouldRun) {
|
|
return false;
|
|
}
|
|
|
|
// Strip the `s` flag.
|
|
ast.flags = ast.flags.replace('s', '');
|
|
|
|
// Whether we have also `u`.
|
|
this._hasUFlag = ast.flags.includes('u');
|
|
|
|
return true;
|
|
},
|
|
Char: function Char(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.kind !== 'meta' || node.value !== '.') {
|
|
return;
|
|
}
|
|
|
|
var toValue = '\\uFFFF';
|
|
var toSymbol = '\uFFFF';
|
|
|
|
if (this._hasUFlag) {
|
|
toValue = '\\u{10FFFF}';
|
|
toSymbol = '\uDBFF\uDFFF';
|
|
}
|
|
|
|
path.replace({
|
|
type: 'CharacterClass',
|
|
expressions: [{
|
|
type: 'ClassRange',
|
|
from: {
|
|
type: 'Char',
|
|
value: '\\0',
|
|
kind: 'decimal',
|
|
symbol: '\0'
|
|
},
|
|
to: {
|
|
type: 'Char',
|
|
value: toValue,
|
|
kind: 'unicode',
|
|
symbol: toSymbol
|
|
}
|
|
}]
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to translate `/(?<name>a)\k<name>/` to `/(a)\1/`.
|
|
*/
|
|
|
|
var compatNamedCapturingGroupsTransform = {
|
|
// To track the names of the groups, and return them
|
|
// in the transform result state.
|
|
//
|
|
// A map from name to number: {foo: 2, bar: 4}
|
|
_groupNames: {},
|
|
|
|
/**
|
|
* Initialises the trasnform.
|
|
*/
|
|
init: function init() {
|
|
this._groupNames = {};
|
|
},
|
|
|
|
|
|
/**
|
|
* Returns extra state, which eventually is returned to
|
|
*/
|
|
getExtra: function getExtra() {
|
|
return this._groupNames;
|
|
},
|
|
Group: function Group(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!node.name) {
|
|
return;
|
|
}
|
|
|
|
// Record group name.
|
|
this._groupNames[node.name] = node.number;
|
|
|
|
delete node.name;
|
|
delete node.nameRaw;
|
|
},
|
|
Backreference: function Backreference(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.kind !== 'name') {
|
|
return;
|
|
}
|
|
|
|
node.kind = 'number';
|
|
node.reference = node.number;
|
|
delete node.referenceRaw;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to remove `x` flag `/foo/x` to `/foo/`.
|
|
*
|
|
* Note: other features of `x` flags (whitespace, comments) are
|
|
* already removed at parsing stage.
|
|
*/
|
|
|
|
var compatXFlagTransform = {
|
|
RegExp: function RegExp(_ref) {
|
|
var node = _ref.node;
|
|
|
|
if (node.flags.includes('x')) {
|
|
node.flags = node.flags.replace('x', '');
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var transforms$1 = {
|
|
// "dotAll" `s` flag
|
|
dotAll: compatDotallSTransform,
|
|
|
|
// Named capturing groups.
|
|
namedCapturingGroups: compatNamedCapturingGroupsTransform,
|
|
|
|
// `x` flag
|
|
xFlag: compatXFlagTransform
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* Helper `gen` function calls node type handler.
|
|
*/
|
|
|
|
function gen$1(node) {
|
|
return node ? generator$3[node.type](node) : '';
|
|
}
|
|
|
|
/**
|
|
* AST handler.
|
|
*/
|
|
var generator$3 = {
|
|
RegExp: function RegExp(node) {
|
|
return '/' + gen$1(node.body) + '/' + node.flags;
|
|
},
|
|
Alternative: function Alternative(node) {
|
|
return (node.expressions || []).map(gen$1).join('');
|
|
},
|
|
Disjunction: function Disjunction(node) {
|
|
return gen$1(node.left) + '|' + gen$1(node.right);
|
|
},
|
|
Group: function Group(node) {
|
|
var expression = gen$1(node.expression);
|
|
|
|
if (node.capturing) {
|
|
// A named group.
|
|
if (node.name) {
|
|
return '(?<' + (node.nameRaw || node.name) + '>' + expression + ')';
|
|
}
|
|
|
|
return '(' + expression + ')';
|
|
}
|
|
|
|
return '(?:' + expression + ')';
|
|
},
|
|
Backreference: function Backreference(node) {
|
|
switch (node.kind) {
|
|
case 'number':
|
|
return '\\' + node.reference;
|
|
case 'name':
|
|
return '\\k<' + (node.referenceRaw || node.reference) + '>';
|
|
default:
|
|
throw new TypeError('Unknown Backreference kind: ' + node.kind);
|
|
}
|
|
},
|
|
Assertion: function Assertion(node) {
|
|
switch (node.kind) {
|
|
case '^':
|
|
case '$':
|
|
case '\\b':
|
|
case '\\B':
|
|
return node.kind;
|
|
|
|
case 'Lookahead':
|
|
{
|
|
var assertion = gen$1(node.assertion);
|
|
|
|
if (node.negative) {
|
|
return '(?!' + assertion + ')';
|
|
}
|
|
|
|
return '(?=' + assertion + ')';
|
|
}
|
|
|
|
case 'Lookbehind':
|
|
{
|
|
var _assertion = gen$1(node.assertion);
|
|
|
|
if (node.negative) {
|
|
return '(?<!' + _assertion + ')';
|
|
}
|
|
|
|
return '(?<=' + _assertion + ')';
|
|
}
|
|
|
|
default:
|
|
throw new TypeError('Unknown Assertion kind: ' + node.kind);
|
|
}
|
|
},
|
|
CharacterClass: function CharacterClass(node) {
|
|
var expressions = node.expressions.map(gen$1).join('');
|
|
|
|
if (node.negative) {
|
|
return '[^' + expressions + ']';
|
|
}
|
|
|
|
return '[' + expressions + ']';
|
|
},
|
|
ClassRange: function ClassRange(node) {
|
|
return gen$1(node.from) + '-' + gen$1(node.to);
|
|
},
|
|
Repetition: function Repetition(node) {
|
|
return '' + gen$1(node.expression) + gen$1(node.quantifier);
|
|
},
|
|
Quantifier: function Quantifier(node) {
|
|
var quantifier = void 0;
|
|
var greedy = node.greedy ? '' : '?';
|
|
|
|
switch (node.kind) {
|
|
case '+':
|
|
case '?':
|
|
case '*':
|
|
quantifier = node.kind;
|
|
break;
|
|
case 'Range':
|
|
// Exact: {1}
|
|
if (node.from === node.to) {
|
|
quantifier = '{' + node.from + '}';
|
|
}
|
|
// Open: {1,}
|
|
else if (!node.to) {
|
|
quantifier = '{' + node.from + ',}';
|
|
}
|
|
// Closed: {1,3}
|
|
else {
|
|
quantifier = '{' + node.from + ',' + node.to + '}';
|
|
}
|
|
break;
|
|
default:
|
|
throw new TypeError('Unknown Quantifier kind: ' + node.kind);
|
|
}
|
|
|
|
return '' + quantifier + greedy;
|
|
},
|
|
Char: function Char(node) {
|
|
var value = node.value;
|
|
|
|
switch (node.kind) {
|
|
case 'simple':
|
|
{
|
|
if (node.escaped) {
|
|
return '\\' + value;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
case 'hex':
|
|
case 'unicode':
|
|
case 'oct':
|
|
case 'decimal':
|
|
case 'control':
|
|
case 'meta':
|
|
return value;
|
|
|
|
default:
|
|
throw new TypeError('Unknown Char kind: ' + node.kind);
|
|
}
|
|
},
|
|
UnicodeProperty: function UnicodeProperty(node) {
|
|
var escapeChar = node.negative ? 'P' : 'p';
|
|
var namePart = void 0;
|
|
|
|
if (!node.shorthand && !node.binary) {
|
|
namePart = node.name + '=';
|
|
} else {
|
|
namePart = '';
|
|
}
|
|
|
|
return '\\' + escapeChar + '{' + namePart + node.value + '}';
|
|
}
|
|
};
|
|
|
|
var generator_1 = {
|
|
/**
|
|
* Generates a regexp string from an AST.
|
|
*
|
|
* @param Object ast - an AST node
|
|
*/
|
|
generate: gen$1
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var NON_BINARY_PROP_NAMES_TO_ALIASES = {
|
|
General_Category: 'gc',
|
|
Script: 'sc',
|
|
Script_Extensions: 'scx'
|
|
};
|
|
|
|
var NON_BINARY_ALIASES_TO_PROP_NAMES = inverseMap(NON_BINARY_PROP_NAMES_TO_ALIASES);
|
|
|
|
var BINARY_PROP_NAMES_TO_ALIASES = {
|
|
ASCII: 'ASCII',
|
|
ASCII_Hex_Digit: 'AHex',
|
|
Alphabetic: 'Alpha',
|
|
Any: 'Any',
|
|
Assigned: 'Assigned',
|
|
Bidi_Control: 'Bidi_C',
|
|
Bidi_Mirrored: 'Bidi_M',
|
|
Case_Ignorable: 'CI',
|
|
Cased: 'Cased',
|
|
Changes_When_Casefolded: 'CWCF',
|
|
Changes_When_Casemapped: 'CWCM',
|
|
Changes_When_Lowercased: 'CWL',
|
|
Changes_When_NFKC_Casefolded: 'CWKCF',
|
|
Changes_When_Titlecased: 'CWT',
|
|
Changes_When_Uppercased: 'CWU',
|
|
Dash: 'Dash',
|
|
Default_Ignorable_Code_Point: 'DI',
|
|
Deprecated: 'Dep',
|
|
Diacritic: 'Dia',
|
|
Emoji: 'Emoji',
|
|
Emoji_Component: 'Emoji_Component',
|
|
Emoji_Modifier: 'Emoji_Modifier',
|
|
Emoji_Modifier_Base: 'Emoji_Modifier_Base',
|
|
Emoji_Presentation: 'Emoji_Presentation',
|
|
Extended_Pictographic: 'Extended_Pictographic',
|
|
Extender: 'Ext',
|
|
Grapheme_Base: 'Gr_Base',
|
|
Grapheme_Extend: 'Gr_Ext',
|
|
Hex_Digit: 'Hex',
|
|
IDS_Binary_Operator: 'IDSB',
|
|
IDS_Trinary_Operator: 'IDST',
|
|
ID_Continue: 'IDC',
|
|
ID_Start: 'IDS',
|
|
Ideographic: 'Ideo',
|
|
Join_Control: 'Join_C',
|
|
Logical_Order_Exception: 'LOE',
|
|
Lowercase: 'Lower',
|
|
Math: 'Math',
|
|
Noncharacter_Code_Point: 'NChar',
|
|
Pattern_Syntax: 'Pat_Syn',
|
|
Pattern_White_Space: 'Pat_WS',
|
|
Quotation_Mark: 'QMark',
|
|
Radical: 'Radical',
|
|
Regional_Indicator: 'RI',
|
|
Sentence_Terminal: 'STerm',
|
|
Soft_Dotted: 'SD',
|
|
Terminal_Punctuation: 'Term',
|
|
Unified_Ideograph: 'UIdeo',
|
|
Uppercase: 'Upper',
|
|
Variation_Selector: 'VS',
|
|
White_Space: 'space',
|
|
XID_Continue: 'XIDC',
|
|
XID_Start: 'XIDS'
|
|
};
|
|
|
|
var BINARY_ALIASES_TO_PROP_NAMES = inverseMap(BINARY_PROP_NAMES_TO_ALIASES);
|
|
|
|
var GENERAL_CATEGORY_VALUE_TO_ALIASES = {
|
|
Cased_Letter: 'LC',
|
|
Close_Punctuation: 'Pe',
|
|
Connector_Punctuation: 'Pc',
|
|
Control: ['Cc', 'cntrl'],
|
|
Currency_Symbol: 'Sc',
|
|
Dash_Punctuation: 'Pd',
|
|
Decimal_Number: ['Nd', 'digit'],
|
|
Enclosing_Mark: 'Me',
|
|
Final_Punctuation: 'Pf',
|
|
Format: 'Cf',
|
|
Initial_Punctuation: 'Pi',
|
|
Letter: 'L',
|
|
Letter_Number: 'Nl',
|
|
Line_Separator: 'Zl',
|
|
Lowercase_Letter: 'Ll',
|
|
Mark: ['M', 'Combining_Mark'],
|
|
Math_Symbol: 'Sm',
|
|
Modifier_Letter: 'Lm',
|
|
Modifier_Symbol: 'Sk',
|
|
Nonspacing_Mark: 'Mn',
|
|
Number: 'N',
|
|
Open_Punctuation: 'Ps',
|
|
Other: 'C',
|
|
Other_Letter: 'Lo',
|
|
Other_Number: 'No',
|
|
Other_Punctuation: 'Po',
|
|
Other_Symbol: 'So',
|
|
Paragraph_Separator: 'Zp',
|
|
Private_Use: 'Co',
|
|
Punctuation: ['P', 'punct'],
|
|
Separator: 'Z',
|
|
Space_Separator: 'Zs',
|
|
Spacing_Mark: 'Mc',
|
|
Surrogate: 'Cs',
|
|
Symbol: 'S',
|
|
Titlecase_Letter: 'Lt',
|
|
Unassigned: 'Cn',
|
|
Uppercase_Letter: 'Lu'
|
|
};
|
|
|
|
var GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES = inverseMap(GENERAL_CATEGORY_VALUE_TO_ALIASES);
|
|
|
|
var SCRIPT_VALUE_TO_ALIASES = {
|
|
Adlam: 'Adlm',
|
|
Ahom: 'Ahom',
|
|
Anatolian_Hieroglyphs: 'Hluw',
|
|
Arabic: 'Arab',
|
|
Armenian: 'Armn',
|
|
Avestan: 'Avst',
|
|
Balinese: 'Bali',
|
|
Bamum: 'Bamu',
|
|
Bassa_Vah: 'Bass',
|
|
Batak: 'Batk',
|
|
Bengali: 'Beng',
|
|
Bhaiksuki: 'Bhks',
|
|
Bopomofo: 'Bopo',
|
|
Brahmi: 'Brah',
|
|
Braille: 'Brai',
|
|
Buginese: 'Bugi',
|
|
Buhid: 'Buhd',
|
|
Canadian_Aboriginal: 'Cans',
|
|
Carian: 'Cari',
|
|
Caucasian_Albanian: 'Aghb',
|
|
Chakma: 'Cakm',
|
|
Cham: 'Cham',
|
|
Cherokee: 'Cher',
|
|
Common: 'Zyyy',
|
|
Coptic: ['Copt', 'Qaac'],
|
|
Cuneiform: 'Xsux',
|
|
Cypriot: 'Cprt',
|
|
Cyrillic: 'Cyrl',
|
|
Deseret: 'Dsrt',
|
|
Devanagari: 'Deva',
|
|
Dogra: 'Dogr',
|
|
Duployan: 'Dupl',
|
|
Egyptian_Hieroglyphs: 'Egyp',
|
|
Elbasan: 'Elba',
|
|
Ethiopic: 'Ethi',
|
|
Georgian: 'Geor',
|
|
Glagolitic: 'Glag',
|
|
Gothic: 'Goth',
|
|
Grantha: 'Gran',
|
|
Greek: 'Grek',
|
|
Gujarati: 'Gujr',
|
|
Gunjala_Gondi: 'Gong',
|
|
Gurmukhi: 'Guru',
|
|
Han: 'Hani',
|
|
Hangul: 'Hang',
|
|
Hanifi_Rohingya: 'Rohg',
|
|
Hanunoo: 'Hano',
|
|
Hatran: 'Hatr',
|
|
Hebrew: 'Hebr',
|
|
Hiragana: 'Hira',
|
|
Imperial_Aramaic: 'Armi',
|
|
Inherited: ['Zinh', 'Qaai'],
|
|
Inscriptional_Pahlavi: 'Phli',
|
|
Inscriptional_Parthian: 'Prti',
|
|
Javanese: 'Java',
|
|
Kaithi: 'Kthi',
|
|
Kannada: 'Knda',
|
|
Katakana: 'Kana',
|
|
Kayah_Li: 'Kali',
|
|
Kharoshthi: 'Khar',
|
|
Khmer: 'Khmr',
|
|
Khojki: 'Khoj',
|
|
Khudawadi: 'Sind',
|
|
Lao: 'Laoo',
|
|
Latin: 'Latn',
|
|
Lepcha: 'Lepc',
|
|
Limbu: 'Limb',
|
|
Linear_A: 'Lina',
|
|
Linear_B: 'Linb',
|
|
Lisu: 'Lisu',
|
|
Lycian: 'Lyci',
|
|
Lydian: 'Lydi',
|
|
Mahajani: 'Mahj',
|
|
Makasar: 'Maka',
|
|
Malayalam: 'Mlym',
|
|
Mandaic: 'Mand',
|
|
Manichaean: 'Mani',
|
|
Marchen: 'Marc',
|
|
Medefaidrin: 'Medf',
|
|
Masaram_Gondi: 'Gonm',
|
|
Meetei_Mayek: 'Mtei',
|
|
Mende_Kikakui: 'Mend',
|
|
Meroitic_Cursive: 'Merc',
|
|
Meroitic_Hieroglyphs: 'Mero',
|
|
Miao: 'Plrd',
|
|
Modi: 'Modi',
|
|
Mongolian: 'Mong',
|
|
Mro: 'Mroo',
|
|
Multani: 'Mult',
|
|
Myanmar: 'Mymr',
|
|
Nabataean: 'Nbat',
|
|
New_Tai_Lue: 'Talu',
|
|
Newa: 'Newa',
|
|
Nko: 'Nkoo',
|
|
Nushu: 'Nshu',
|
|
Ogham: 'Ogam',
|
|
Ol_Chiki: 'Olck',
|
|
Old_Hungarian: 'Hung',
|
|
Old_Italic: 'Ital',
|
|
Old_North_Arabian: 'Narb',
|
|
Old_Permic: 'Perm',
|
|
Old_Persian: 'Xpeo',
|
|
Old_Sogdian: 'Sogo',
|
|
Old_South_Arabian: 'Sarb',
|
|
Old_Turkic: 'Orkh',
|
|
Oriya: 'Orya',
|
|
Osage: 'Osge',
|
|
Osmanya: 'Osma',
|
|
Pahawh_Hmong: 'Hmng',
|
|
Palmyrene: 'Palm',
|
|
Pau_Cin_Hau: 'Pauc',
|
|
Phags_Pa: 'Phag',
|
|
Phoenician: 'Phnx',
|
|
Psalter_Pahlavi: 'Phlp',
|
|
Rejang: 'Rjng',
|
|
Runic: 'Runr',
|
|
Samaritan: 'Samr',
|
|
Saurashtra: 'Saur',
|
|
Sharada: 'Shrd',
|
|
Shavian: 'Shaw',
|
|
Siddham: 'Sidd',
|
|
SignWriting: 'Sgnw',
|
|
Sinhala: 'Sinh',
|
|
Sogdian: 'Sogd',
|
|
Sora_Sompeng: 'Sora',
|
|
Soyombo: 'Soyo',
|
|
Sundanese: 'Sund',
|
|
Syloti_Nagri: 'Sylo',
|
|
Syriac: 'Syrc',
|
|
Tagalog: 'Tglg',
|
|
Tagbanwa: 'Tagb',
|
|
Tai_Le: 'Tale',
|
|
Tai_Tham: 'Lana',
|
|
Tai_Viet: 'Tavt',
|
|
Takri: 'Takr',
|
|
Tamil: 'Taml',
|
|
Tangut: 'Tang',
|
|
Telugu: 'Telu',
|
|
Thaana: 'Thaa',
|
|
Thai: 'Thai',
|
|
Tibetan: 'Tibt',
|
|
Tifinagh: 'Tfng',
|
|
Tirhuta: 'Tirh',
|
|
Ugaritic: 'Ugar',
|
|
Vai: 'Vaii',
|
|
Warang_Citi: 'Wara',
|
|
Yi: 'Yiii',
|
|
Zanabazar_Square: 'Zanb'
|
|
};
|
|
|
|
var SCRIPT_VALUE_ALIASES_TO_VALUE = inverseMap(SCRIPT_VALUE_TO_ALIASES);
|
|
|
|
function inverseMap(data) {
|
|
var inverse = {};
|
|
|
|
for (var name in data) {
|
|
if (!data.hasOwnProperty(name)) {
|
|
continue;
|
|
}
|
|
var value = data[name];
|
|
if (Array.isArray(value)) {
|
|
for (var i = 0; i < value.length; i++) {
|
|
inverse[value[i]] = name;
|
|
}
|
|
} else {
|
|
inverse[value] = name;
|
|
}
|
|
}
|
|
|
|
return inverse;
|
|
}
|
|
|
|
function isValidName(name) {
|
|
return NON_BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) || NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) || BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) || BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name);
|
|
}
|
|
|
|
function isValidValue(name, value) {
|
|
if (isGeneralCategoryName(name)) {
|
|
return isGeneralCategoryValue(value);
|
|
}
|
|
|
|
if (isScriptCategoryName(name)) {
|
|
return isScriptCategoryValue(value);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function isAlias(name) {
|
|
return NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name) || BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name);
|
|
}
|
|
|
|
function isGeneralCategoryName(name) {
|
|
return name === 'General_Category' || name == 'gc';
|
|
}
|
|
|
|
function isScriptCategoryName(name) {
|
|
return name === 'Script' || name === 'Script_Extensions' || name === 'sc' || name === 'scx';
|
|
}
|
|
|
|
function isGeneralCategoryValue(value) {
|
|
return GENERAL_CATEGORY_VALUE_TO_ALIASES.hasOwnProperty(value) || GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value);
|
|
}
|
|
|
|
function isScriptCategoryValue(value) {
|
|
return SCRIPT_VALUE_TO_ALIASES.hasOwnProperty(value) || SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value);
|
|
}
|
|
|
|
function isBinaryPropertyName(name) {
|
|
return BINARY_PROP_NAMES_TO_ALIASES.hasOwnProperty(name) || BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name);
|
|
}
|
|
|
|
function getCanonicalName(name) {
|
|
if (NON_BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
|
|
return NON_BINARY_ALIASES_TO_PROP_NAMES[name];
|
|
}
|
|
|
|
if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(name)) {
|
|
return BINARY_ALIASES_TO_PROP_NAMES[name];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getCanonicalValue(value) {
|
|
if (GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES.hasOwnProperty(value)) {
|
|
return GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES[value];
|
|
}
|
|
|
|
if (SCRIPT_VALUE_ALIASES_TO_VALUE.hasOwnProperty(value)) {
|
|
return SCRIPT_VALUE_ALIASES_TO_VALUE[value];
|
|
}
|
|
|
|
if (BINARY_ALIASES_TO_PROP_NAMES.hasOwnProperty(value)) {
|
|
return BINARY_ALIASES_TO_PROP_NAMES[value];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
var parserUnicodeProperties = {
|
|
isAlias: isAlias,
|
|
isValidName: isValidName,
|
|
isValidValue: isValidValue,
|
|
isGeneralCategoryValue: isGeneralCategoryValue,
|
|
isScriptCategoryValue: isScriptCategoryValue,
|
|
isBinaryPropertyName: isBinaryPropertyName,
|
|
getCanonicalName: getCanonicalName,
|
|
getCanonicalValue: getCanonicalValue,
|
|
|
|
NON_BINARY_PROP_NAMES_TO_ALIASES: NON_BINARY_PROP_NAMES_TO_ALIASES,
|
|
NON_BINARY_ALIASES_TO_PROP_NAMES: NON_BINARY_ALIASES_TO_PROP_NAMES,
|
|
|
|
BINARY_PROP_NAMES_TO_ALIASES: BINARY_PROP_NAMES_TO_ALIASES,
|
|
BINARY_ALIASES_TO_PROP_NAMES: BINARY_ALIASES_TO_PROP_NAMES,
|
|
|
|
GENERAL_CATEGORY_VALUE_TO_ALIASES: GENERAL_CATEGORY_VALUE_TO_ALIASES,
|
|
GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES: GENERAL_CATEGORY_VALUE_ALIASES_TO_VALUES,
|
|
|
|
SCRIPT_VALUE_TO_ALIASES: SCRIPT_VALUE_TO_ALIASES,
|
|
SCRIPT_VALUE_ALIASES_TO_VALUE: SCRIPT_VALUE_ALIASES_TO_VALUE
|
|
};
|
|
|
|
/**
|
|
* LR parser generated by the Syntax tool.
|
|
*
|
|
* https://www.npmjs.com/package/syntax-cli
|
|
*
|
|
* npm install -g syntax-cli
|
|
*
|
|
* syntax-cli --help
|
|
*
|
|
* To regenerate run:
|
|
*
|
|
* syntax-cli \
|
|
* --grammar ~/path-to-grammar-file \
|
|
* --mode <parsing-mode> \
|
|
* --output ~/path-to-output-parser-file.js
|
|
*/
|
|
|
|
/**
|
|
* Matched token text.
|
|
*/
|
|
|
|
var _slicedToArray$2 = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
|
|
function _toConsumableArray$8(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
var yytext = void 0;
|
|
|
|
/**
|
|
* Storage object.
|
|
*/
|
|
var yy = {};
|
|
|
|
/**
|
|
* Result of semantic action.
|
|
*/
|
|
var __ = void 0;
|
|
|
|
/**
|
|
* Result location object.
|
|
*/
|
|
var __loc = void 0;
|
|
|
|
function yyloc(start, end) {
|
|
if (!yy.options.captureLocations) {
|
|
return null;
|
|
}
|
|
|
|
// Epsilon doesn't produce location.
|
|
if (!start || !end) {
|
|
return start || end;
|
|
}
|
|
|
|
return {
|
|
startOffset: start.startOffset,
|
|
endOffset: end.endOffset,
|
|
startLine: start.startLine,
|
|
endLine: end.endLine,
|
|
startColumn: start.startColumn,
|
|
endColumn: end.endColumn
|
|
};
|
|
}
|
|
|
|
var EOF = '$';
|
|
|
|
/**
|
|
* List of productions (generated by Syntax tool).
|
|
*/
|
|
var productions = [[-1, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [0, 4, function (_1, _2, _3, _4, _1loc, _2loc, _3loc, _4loc) {
|
|
__loc = yyloc(_1loc, _4loc);
|
|
__ = Node({
|
|
type: 'RegExp',
|
|
body: _2,
|
|
flags: checkFlags(_4)
|
|
}, loc(_1loc, _4loc || _3loc));
|
|
}], [1, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [1, 0, function () {
|
|
__loc = null;__ = '';
|
|
}], [2, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [2, 2, function (_1, _2, _1loc, _2loc) {
|
|
__loc = yyloc(_1loc, _2loc);__ = _1 + _2;
|
|
}], [3, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [4, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [4, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
// Location for empty disjunction: /|/
|
|
var _loc = null;
|
|
|
|
if (_2loc) {
|
|
_loc = loc(_1loc || _2loc, _3loc || _2loc);
|
|
}
|
|
__ = Node({
|
|
type: 'Disjunction',
|
|
left: _1,
|
|
right: _3
|
|
}, _loc);
|
|
}], [5, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);
|
|
if (_1.length === 0) {
|
|
__ = null;
|
|
return;
|
|
}
|
|
|
|
if (_1.length === 1) {
|
|
__ = Node(_1[0], __loc);
|
|
} else {
|
|
__ = Node({
|
|
type: 'Alternative',
|
|
expressions: _1
|
|
}, __loc);
|
|
}
|
|
}], [6, 0, function () {
|
|
__loc = null;__ = [];
|
|
}], [6, 2, function (_1, _2, _1loc, _2loc) {
|
|
__loc = yyloc(_1loc, _2loc);__ = _1.concat(_2);
|
|
}], [7, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Node(Object.assign({ type: 'Assertion' }, _1), __loc);
|
|
}], [7, 2, function (_1, _2, _1loc, _2loc) {
|
|
__loc = yyloc(_1loc, _2loc);
|
|
__ = _1;
|
|
|
|
if (_2) {
|
|
__ = Node({
|
|
type: 'Repetition',
|
|
expression: _1,
|
|
quantifier: _2
|
|
}, __loc);
|
|
}
|
|
}], [8, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = { kind: '^' };
|
|
}], [8, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = { kind: '$' };
|
|
}], [8, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = { kind: '\\b' };
|
|
}], [8, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = { kind: '\\B' };
|
|
}], [8, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = {
|
|
kind: 'Lookahead',
|
|
assertion: _2
|
|
};
|
|
}], [8, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = {
|
|
kind: 'Lookahead',
|
|
negative: true,
|
|
assertion: _2
|
|
};
|
|
}], [8, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = {
|
|
kind: 'Lookbehind',
|
|
assertion: _2
|
|
};
|
|
}], [8, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = {
|
|
kind: 'Lookbehind',
|
|
negative: true,
|
|
assertion: _2
|
|
};
|
|
}], [9, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [9, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [9, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'simple', __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1.slice(1), 'simple', __loc);__.escaped = true;
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'unicode', __loc);__.isSurrogatePair = true;
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'unicode', __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = UnicodeProperty(_1, __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'control', __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'hex', __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'oct', __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = GroupRefOrDecChar(_1, __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'meta', __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'meta', __loc);
|
|
}], [10, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = NamedGroupRefOrChars(_1, _1loc);
|
|
}], [11, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [11, 0], [12, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [12, 2, function (_1, _2, _1loc, _2loc) {
|
|
__loc = yyloc(_1loc, _2loc);
|
|
_1.greedy = false;
|
|
__ = _1;
|
|
}], [13, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);
|
|
__ = Node({
|
|
type: 'Quantifier',
|
|
kind: _1,
|
|
greedy: true
|
|
}, __loc);
|
|
}], [13, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);
|
|
__ = Node({
|
|
type: 'Quantifier',
|
|
kind: _1,
|
|
greedy: true
|
|
}, __loc);
|
|
}], [13, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);
|
|
__ = Node({
|
|
type: 'Quantifier',
|
|
kind: _1,
|
|
greedy: true
|
|
}, __loc);
|
|
}], [13, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);
|
|
var range = getRange(_1);
|
|
__ = Node({
|
|
type: 'Quantifier',
|
|
kind: 'Range',
|
|
from: range[0],
|
|
to: range[0],
|
|
greedy: true
|
|
}, __loc);
|
|
}], [13, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);
|
|
__ = Node({
|
|
type: 'Quantifier',
|
|
kind: 'Range',
|
|
from: getRange(_1)[0],
|
|
greedy: true
|
|
}, __loc);
|
|
}], [13, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);
|
|
var range = getRange(_1);
|
|
__ = Node({
|
|
type: 'Quantifier',
|
|
kind: 'Range',
|
|
from: range[0],
|
|
to: range[1],
|
|
greedy: true
|
|
}, __loc);
|
|
}], [14, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [14, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [15, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
var nameRaw = String(_1);
|
|
var name = decodeUnicodeGroupName(nameRaw);
|
|
if (!yy.options.allowGroupNameDuplicates && namedGroups.hasOwnProperty(name)) {
|
|
throw new SyntaxError('Duplicate of the named group "' + name + '".');
|
|
}
|
|
|
|
namedGroups[name] = _1.groupNumber;
|
|
|
|
__ = Node({
|
|
type: 'Group',
|
|
capturing: true,
|
|
name: name,
|
|
nameRaw: nameRaw,
|
|
number: _1.groupNumber,
|
|
expression: _2
|
|
}, __loc);
|
|
}], [15, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = Node({
|
|
type: 'Group',
|
|
capturing: true,
|
|
number: _1.groupNumber,
|
|
expression: _2
|
|
}, __loc);
|
|
}], [16, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = Node({
|
|
type: 'Group',
|
|
capturing: false,
|
|
expression: _2
|
|
}, __loc);
|
|
}], [17, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = Node({
|
|
type: 'CharacterClass',
|
|
negative: true,
|
|
expressions: _2
|
|
}, __loc);
|
|
}], [17, 3, function (_1, _2, _3, _1loc, _2loc, _3loc) {
|
|
__loc = yyloc(_1loc, _3loc);
|
|
__ = Node({
|
|
type: 'CharacterClass',
|
|
expressions: _2
|
|
}, __loc);
|
|
}], [18, 0, function () {
|
|
__loc = null;__ = [];
|
|
}], [18, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [19, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = [_1];
|
|
}], [19, 2, function (_1, _2, _1loc, _2loc) {
|
|
__loc = yyloc(_1loc, _2loc);__ = [_1].concat(_2);
|
|
}], [19, 4, function (_1, _2, _3, _4, _1loc, _2loc, _3loc, _4loc) {
|
|
__loc = yyloc(_1loc, _4loc);
|
|
checkClassRange(_1, _3);
|
|
|
|
__ = [Node({
|
|
type: 'ClassRange',
|
|
from: _1,
|
|
to: _3
|
|
}, loc(_1loc, _3loc))];
|
|
|
|
if (_4) {
|
|
__ = __.concat(_4);
|
|
}
|
|
}], [20, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [20, 2, function (_1, _2, _1loc, _2loc) {
|
|
__loc = yyloc(_1loc, _2loc);__ = [_1].concat(_2);
|
|
}], [20, 4, function (_1, _2, _3, _4, _1loc, _2loc, _3loc, _4loc) {
|
|
__loc = yyloc(_1loc, _4loc);
|
|
checkClassRange(_1, _3);
|
|
|
|
__ = [Node({
|
|
type: 'ClassRange',
|
|
from: _1,
|
|
to: _3
|
|
}, loc(_1loc, _3loc))];
|
|
|
|
if (_4) {
|
|
__ = __.concat(_4);
|
|
}
|
|
}], [21, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'simple', __loc);
|
|
}], [21, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [22, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = _1;
|
|
}], [22, 1, function (_1, _1loc) {
|
|
__loc = yyloc(_1loc, _1loc);__ = Char(_1, 'meta', __loc);
|
|
}]];
|
|
|
|
/**
|
|
* Encoded tokens map.
|
|
*/
|
|
var tokens = { "SLASH": "23", "CHAR": "24", "BAR": "25", "BOS": "26", "EOS": "27", "ESC_b": "28", "ESC_B": "29", "POS_LA_ASSERT": "30", "R_PAREN": "31", "NEG_LA_ASSERT": "32", "POS_LB_ASSERT": "33", "NEG_LB_ASSERT": "34", "ESC_CHAR": "35", "U_CODE_SURROGATE": "36", "U_CODE": "37", "U_PROP_VALUE_EXP": "38", "CTRL_CH": "39", "HEX_CODE": "40", "OCT_CODE": "41", "DEC_CODE": "42", "META_CHAR": "43", "ANY": "44", "NAMED_GROUP_REF": "45", "Q_MARK": "46", "STAR": "47", "PLUS": "48", "RANGE_EXACT": "49", "RANGE_OPEN": "50", "RANGE_CLOSED": "51", "NAMED_CAPTURE_GROUP": "52", "L_PAREN": "53", "NON_CAPTURE_GROUP": "54", "NEG_CLASS": "55", "R_BRACKET": "56", "L_BRACKET": "57", "DASH": "58", "$": "59" };
|
|
|
|
/**
|
|
* Parsing table (generated by Syntax tool).
|
|
*/
|
|
var table = [{ "0": 1, "23": "s2" }, { "59": "acc" }, { "3": 3, "4": 4, "5": 5, "6": 6, "23": "r10", "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "23": "s7" }, { "23": "r6", "25": "s12" }, { "23": "r7", "25": "r7", "31": "r7" }, { "7": 14, "8": 15, "9": 16, "10": 25, "14": 27, "15": 42, "16": 43, "17": 26, "23": "r9", "24": "s28", "25": "r9", "26": "s17", "27": "s18", "28": "s19", "29": "s20", "30": "s21", "31": "r9", "32": "s22", "33": "s23", "34": "s24", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "52": "s44", "53": "s45", "54": "s46", "55": "s40", "57": "s41" }, { "1": 8, "2": 9, "24": "s10", "59": "r3" }, { "59": "r1" }, { "24": "s11", "59": "r2" }, { "24": "r4", "59": "r4" }, { "24": "r5", "59": "r5" }, { "5": 13, "6": 6, "23": "r10", "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "23": "r8", "25": "r8", "31": "r8" }, { "23": "r11", "24": "r11", "25": "r11", "26": "r11", "27": "r11", "28": "r11", "29": "r11", "30": "r11", "31": "r11", "32": "r11", "33": "r11", "34": "r11", "35": "r11", "36": "r11", "37": "r11", "38": "r11", "39": "r11", "40": "r11", "41": "r11", "42": "r11", "43": "r11", "44": "r11", "45": "r11", "52": "r11", "53": "r11", "54": "r11", "55": "r11", "57": "r11" }, { "23": "r12", "24": "r12", "25": "r12", "26": "r12", "27": "r12", "28": "r12", "29": "r12", "30": "r12", "31": "r12", "32": "r12", "33": "r12", "34": "r12", "35": "r12", "36": "r12", "37": "r12", "38": "r12", "39": "r12", "40": "r12", "41": "r12", "42": "r12", "43": "r12", "44": "r12", "45": "r12", "52": "r12", "53": "r12", "54": "r12", "55": "r12", "57": "r12" }, { "11": 47, "12": 48, "13": 49, "23": "r38", "24": "r38", "25": "r38", "26": "r38", "27": "r38", "28": "r38", "29": "r38", "30": "r38", "31": "r38", "32": "r38", "33": "r38", "34": "r38", "35": "r38", "36": "r38", "37": "r38", "38": "r38", "39": "r38", "40": "r38", "41": "r38", "42": "r38", "43": "r38", "44": "r38", "45": "r38", "46": "s52", "47": "s50", "48": "s51", "49": "s53", "50": "s54", "51": "s55", "52": "r38", "53": "r38", "54": "r38", "55": "r38", "57": "r38" }, { "23": "r14", "24": "r14", "25": "r14", "26": "r14", "27": "r14", "28": "r14", "29": "r14", "30": "r14", "31": "r14", "32": "r14", "33": "r14", "34": "r14", "35": "r14", "36": "r14", "37": "r14", "38": "r14", "39": "r14", "40": "r14", "41": "r14", "42": "r14", "43": "r14", "44": "r14", "45": "r14", "52": "r14", "53": "r14", "54": "r14", "55": "r14", "57": "r14" }, { "23": "r15", "24": "r15", "25": "r15", "26": "r15", "27": "r15", "28": "r15", "29": "r15", "30": "r15", "31": "r15", "32": "r15", "33": "r15", "34": "r15", "35": "r15", "36": "r15", "37": "r15", "38": "r15", "39": "r15", "40": "r15", "41": "r15", "42": "r15", "43": "r15", "44": "r15", "45": "r15", "52": "r15", "53": "r15", "54": "r15", "55": "r15", "57": "r15" }, { "23": "r16", "24": "r16", "25": "r16", "26": "r16", "27": "r16", "28": "r16", "29": "r16", "30": "r16", "31": "r16", "32": "r16", "33": "r16", "34": "r16", "35": "r16", "36": "r16", "37": "r16", "38": "r16", "39": "r16", "40": "r16", "41": "r16", "42": "r16", "43": "r16", "44": "r16", "45": "r16", "52": "r16", "53": "r16", "54": "r16", "55": "r16", "57": "r16" }, { "23": "r17", "24": "r17", "25": "r17", "26": "r17", "27": "r17", "28": "r17", "29": "r17", "30": "r17", "31": "r17", "32": "r17", "33": "r17", "34": "r17", "35": "r17", "36": "r17", "37": "r17", "38": "r17", "39": "r17", "40": "r17", "41": "r17", "42": "r17", "43": "r17", "44": "r17", "45": "r17", "52": "r17", "53": "r17", "54": "r17", "55": "r17", "57": "r17" }, { "4": 57, "5": 5, "6": 6, "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "4": 59, "5": 5, "6": 6, "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "4": 61, "5": 5, "6": 6, "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "4": 63, "5": 5, "6": 6, "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "23": "r22", "24": "r22", "25": "r22", "26": "r22", "27": "r22", "28": "r22", "29": "r22", "30": "r22", "31": "r22", "32": "r22", "33": "r22", "34": "r22", "35": "r22", "36": "r22", "37": "r22", "38": "r22", "39": "r22", "40": "r22", "41": "r22", "42": "r22", "43": "r22", "44": "r22", "45": "r22", "46": "r22", "47": "r22", "48": "r22", "49": "r22", "50": "r22", "51": "r22", "52": "r22", "53": "r22", "54": "r22", "55": "r22", "57": "r22" }, { "23": "r23", "24": "r23", "25": "r23", "26": "r23", "27": "r23", "28": "r23", "29": "r23", "30": "r23", "31": "r23", "32": "r23", "33": "r23", "34": "r23", "35": "r23", "36": "r23", "37": "r23", "38": "r23", "39": "r23", "40": "r23", "41": "r23", "42": "r23", "43": "r23", "44": "r23", "45": "r23", "46": "r23", "47": "r23", "48": "r23", "49": "r23", "50": "r23", "51": "r23", "52": "r23", "53": "r23", "54": "r23", "55": "r23", "57": "r23" }, { "23": "r24", "24": "r24", "25": "r24", "26": "r24", "27": "r24", "28": "r24", "29": "r24", "30": "r24", "31": "r24", "32": "r24", "33": "r24", "34": "r24", "35": "r24", "36": "r24", "37": "r24", "38": "r24", "39": "r24", "40": "r24", "41": "r24", "42": "r24", "43": "r24", "44": "r24", "45": "r24", "46": "r24", "47": "r24", "48": "r24", "49": "r24", "50": "r24", "51": "r24", "52": "r24", "53": "r24", "54": "r24", "55": "r24", "57": "r24" }, { "23": "r25", "24": "r25", "25": "r25", "26": "r25", "27": "r25", "28": "r25", "29": "r25", "30": "r25", "31": "r25", "32": "r25", "33": "r25", "34": "r25", "35": "r25", "36": "r25", "37": "r25", "38": "r25", "39": "r25", "40": "r25", "41": "r25", "42": "r25", "43": "r25", "44": "r25", "45": "r25", "46": "r25", "47": "r25", "48": "r25", "49": "r25", "50": "r25", "51": "r25", "52": "r25", "53": "r25", "54": "r25", "55": "r25", "56": "r25", "57": "r25", "58": "r25" }, { "23": "r26", "24": "r26", "25": "r26", "26": "r26", "27": "r26", "28": "r26", "29": "r26", "30": "r26", "31": "r26", "32": "r26", "33": "r26", "34": "r26", "35": "r26", "36": "r26", "37": "r26", "38": "r26", "39": "r26", "40": "r26", "41": "r26", "42": "r26", "43": "r26", "44": "r26", "45": "r26", "46": "r26", "47": "r26", "48": "r26", "49": "r26", "50": "r26", "51": "r26", "52": "r26", "53": "r26", "54": "r26", "55": "r26", "56": "r26", "57": "r26", "58": "r26" }, { "23": "r27", "24": "r27", "25": "r27", "26": "r27", "27": "r27", "28": "r27", "29": "r27", "30": "r27", "31": "r27", "32": "r27", "33": "r27", "34": "r27", "35": "r27", "36": "r27", "37": "r27", "38": "r27", "39": "r27", "40": "r27", "41": "r27", "42": "r27", "43": "r27", "44": "r27", "45": "r27", "46": "r27", "47": "r27", "48": "r27", "49": "r27", "50": "r27", "51": "r27", "52": "r27", "53": "r27", "54": "r27", "55": "r27", "56": "r27", "57": "r27", "58": "r27" }, { "23": "r28", "24": "r28", "25": "r28", "26": "r28", "27": "r28", "28": "r28", "29": "r28", "30": "r28", "31": "r28", "32": "r28", "33": "r28", "34": "r28", "35": "r28", "36": "r28", "37": "r28", "38": "r28", "39": "r28", "40": "r28", "41": "r28", "42": "r28", "43": "r28", "44": "r28", "45": "r28", "46": "r28", "47": "r28", "48": "r28", "49": "r28", "50": "r28", "51": "r28", "52": "r28", "53": "r28", "54": "r28", "55": "r28", "56": "r28", "57": "r28", "58": "r28" }, { "23": "r29", "24": "r29", "25": "r29", "26": "r29", "27": "r29", "28": "r29", "29": "r29", "30": "r29", "31": "r29", "32": "r29", "33": "r29", "34": "r29", "35": "r29", "36": "r29", "37": "r29", "38": "r29", "39": "r29", "40": "r29", "41": "r29", "42": "r29", "43": "r29", "44": "r29", "45": "r29", "46": "r29", "47": "r29", "48": "r29", "49": "r29", "50": "r29", "51": "r29", "52": "r29", "53": "r29", "54": "r29", "55": "r29", "56": "r29", "57": "r29", "58": "r29" }, { "23": "r30", "24": "r30", "25": "r30", "26": "r30", "27": "r30", "28": "r30", "29": "r30", "30": "r30", "31": "r30", "32": "r30", "33": "r30", "34": "r30", "35": "r30", "36": "r30", "37": "r30", "38": "r30", "39": "r30", "40": "r30", "41": "r30", "42": "r30", "43": "r30", "44": "r30", "45": "r30", "46": "r30", "47": "r30", "48": "r30", "49": "r30", "50": "r30", "51": "r30", "52": "r30", "53": "r30", "54": "r30", "55": "r30", "56": "r30", "57": "r30", "58": "r30" }, { "23": "r31", "24": "r31", "25": "r31", "26": "r31", "27": "r31", "28": "r31", "29": "r31", "30": "r31", "31": "r31", "32": "r31", "33": "r31", "34": "r31", "35": "r31", "36": "r31", "37": "r31", "38": "r31", "39": "r31", "40": "r31", "41": "r31", "42": "r31", "43": "r31", "44": "r31", "45": "r31", "46": "r31", "47": "r31", "48": "r31", "49": "r31", "50": "r31", "51": "r31", "52": "r31", "53": "r31", "54": "r31", "55": "r31", "56": "r31", "57": "r31", "58": "r31" }, { "23": "r32", "24": "r32", "25": "r32", "26": "r32", "27": "r32", "28": "r32", "29": "r32", "30": "r32", "31": "r32", "32": "r32", "33": "r32", "34": "r32", "35": "r32", "36": "r32", "37": "r32", "38": "r32", "39": "r32", "40": "r32", "41": "r32", "42": "r32", "43": "r32", "44": "r32", "45": "r32", "46": "r32", "47": "r32", "48": "r32", "49": "r32", "50": "r32", "51": "r32", "52": "r32", "53": "r32", "54": "r32", "55": "r32", "56": "r32", "57": "r32", "58": "r32" }, { "23": "r33", "24": "r33", "25": "r33", "26": "r33", "27": "r33", "28": "r33", "29": "r33", "30": "r33", "31": "r33", "32": "r33", "33": "r33", "34": "r33", "35": "r33", "36": "r33", "37": "r33", "38": "r33", "39": "r33", "40": "r33", "41": "r33", "42": "r33", "43": "r33", "44": "r33", "45": "r33", "46": "r33", "47": "r33", "48": "r33", "49": "r33", "50": "r33", "51": "r33", "52": "r33", "53": "r33", "54": "r33", "55": "r33", "56": "r33", "57": "r33", "58": "r33" }, { "23": "r34", "24": "r34", "25": "r34", "26": "r34", "27": "r34", "28": "r34", "29": "r34", "30": "r34", "31": "r34", "32": "r34", "33": "r34", "34": "r34", "35": "r34", "36": "r34", "37": "r34", "38": "r34", "39": "r34", "40": "r34", "41": "r34", "42": "r34", "43": "r34", "44": "r34", "45": "r34", "46": "r34", "47": "r34", "48": "r34", "49": "r34", "50": "r34", "51": "r34", "52": "r34", "53": "r34", "54": "r34", "55": "r34", "56": "r34", "57": "r34", "58": "r34" }, { "23": "r35", "24": "r35", "25": "r35", "26": "r35", "27": "r35", "28": "r35", "29": "r35", "30": "r35", "31": "r35", "32": "r35", "33": "r35", "34": "r35", "35": "r35", "36": "r35", "37": "r35", "38": "r35", "39": "r35", "40": "r35", "41": "r35", "42": "r35", "43": "r35", "44": "r35", "45": "r35", "46": "r35", "47": "r35", "48": "r35", "49": "r35", "50": "r35", "51": "r35", "52": "r35", "53": "r35", "54": "r35", "55": "r35", "56": "r35", "57": "r35", "58": "r35" }, { "23": "r36", "24": "r36", "25": "r36", "26": "r36", "27": "r36", "28": "r36", "29": "r36", "30": "r36", "31": "r36", "32": "r36", "33": "r36", "34": "r36", "35": "r36", "36": "r36", "37": "r36", "38": "r36", "39": "r36", "40": "r36", "41": "r36", "42": "r36", "43": "r36", "44": "r36", "45": "r36", "46": "r36", "47": "r36", "48": "r36", "49": "r36", "50": "r36", "51": "r36", "52": "r36", "53": "r36", "54": "r36", "55": "r36", "56": "r36", "57": "r36", "58": "r36" }, { "10": 70, "18": 65, "19": 66, "21": 67, "22": 69, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r54", "58": "s68" }, { "10": 70, "18": 83, "19": 66, "21": 67, "22": 69, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r54", "58": "s68" }, { "23": "r47", "24": "r47", "25": "r47", "26": "r47", "27": "r47", "28": "r47", "29": "r47", "30": "r47", "31": "r47", "32": "r47", "33": "r47", "34": "r47", "35": "r47", "36": "r47", "37": "r47", "38": "r47", "39": "r47", "40": "r47", "41": "r47", "42": "r47", "43": "r47", "44": "r47", "45": "r47", "46": "r47", "47": "r47", "48": "r47", "49": "r47", "50": "r47", "51": "r47", "52": "r47", "53": "r47", "54": "r47", "55": "r47", "57": "r47" }, { "23": "r48", "24": "r48", "25": "r48", "26": "r48", "27": "r48", "28": "r48", "29": "r48", "30": "r48", "31": "r48", "32": "r48", "33": "r48", "34": "r48", "35": "r48", "36": "r48", "37": "r48", "38": "r48", "39": "r48", "40": "r48", "41": "r48", "42": "r48", "43": "r48", "44": "r48", "45": "r48", "46": "r48", "47": "r48", "48": "r48", "49": "r48", "50": "r48", "51": "r48", "52": "r48", "53": "r48", "54": "r48", "55": "r48", "57": "r48" }, { "4": 85, "5": 5, "6": 6, "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "4": 87, "5": 5, "6": 6, "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "4": 89, "5": 5, "6": 6, "24": "r10", "25": "r10", "26": "r10", "27": "r10", "28": "r10", "29": "r10", "30": "r10", "31": "r10", "32": "r10", "33": "r10", "34": "r10", "35": "r10", "36": "r10", "37": "r10", "38": "r10", "39": "r10", "40": "r10", "41": "r10", "42": "r10", "43": "r10", "44": "r10", "45": "r10", "52": "r10", "53": "r10", "54": "r10", "55": "r10", "57": "r10" }, { "23": "r13", "24": "r13", "25": "r13", "26": "r13", "27": "r13", "28": "r13", "29": "r13", "30": "r13", "31": "r13", "32": "r13", "33": "r13", "34": "r13", "35": "r13", "36": "r13", "37": "r13", "38": "r13", "39": "r13", "40": "r13", "41": "r13", "42": "r13", "43": "r13", "44": "r13", "45": "r13", "52": "r13", "53": "r13", "54": "r13", "55": "r13", "57": "r13" }, { "23": "r37", "24": "r37", "25": "r37", "26": "r37", "27": "r37", "28": "r37", "29": "r37", "30": "r37", "31": "r37", "32": "r37", "33": "r37", "34": "r37", "35": "r37", "36": "r37", "37": "r37", "38": "r37", "39": "r37", "40": "r37", "41": "r37", "42": "r37", "43": "r37", "44": "r37", "45": "r37", "52": "r37", "53": "r37", "54": "r37", "55": "r37", "57": "r37" }, { "23": "r39", "24": "r39", "25": "r39", "26": "r39", "27": "r39", "28": "r39", "29": "r39", "30": "r39", "31": "r39", "32": "r39", "33": "r39", "34": "r39", "35": "r39", "36": "r39", "37": "r39", "38": "r39", "39": "r39", "40": "r39", "41": "r39", "42": "r39", "43": "r39", "44": "r39", "45": "r39", "46": "s56", "52": "r39", "53": "r39", "54": "r39", "55": "r39", "57": "r39" }, { "23": "r41", "24": "r41", "25": "r41", "26": "r41", "27": "r41", "28": "r41", "29": "r41", "30": "r41", "31": "r41", "32": "r41", "33": "r41", "34": "r41", "35": "r41", "36": "r41", "37": "r41", "38": "r41", "39": "r41", "40": "r41", "41": "r41", "42": "r41", "43": "r41", "44": "r41", "45": "r41", "46": "r41", "52": "r41", "53": "r41", "54": "r41", "55": "r41", "57": "r41" }, { "23": "r42", "24": "r42", "25": "r42", "26": "r42", "27": "r42", "28": "r42", "29": "r42", "30": "r42", "31": "r42", "32": "r42", "33": "r42", "34": "r42", "35": "r42", "36": "r42", "37": "r42", "38": "r42", "39": "r42", "40": "r42", "41": "r42", "42": "r42", "43": "r42", "44": "r42", "45": "r42", "46": "r42", "52": "r42", "53": "r42", "54": "r42", "55": "r42", "57": "r42" }, { "23": "r43", "24": "r43", "25": "r43", "26": "r43", "27": "r43", "28": "r43", "29": "r43", "30": "r43", "31": "r43", "32": "r43", "33": "r43", "34": "r43", "35": "r43", "36": "r43", "37": "r43", "38": "r43", "39": "r43", "40": "r43", "41": "r43", "42": "r43", "43": "r43", "44": "r43", "45": "r43", "46": "r43", "52": "r43", "53": "r43", "54": "r43", "55": "r43", "57": "r43" }, { "23": "r44", "24": "r44", "25": "r44", "26": "r44", "27": "r44", "28": "r44", "29": "r44", "30": "r44", "31": "r44", "32": "r44", "33": "r44", "34": "r44", "35": "r44", "36": "r44", "37": "r44", "38": "r44", "39": "r44", "40": "r44", "41": "r44", "42": "r44", "43": "r44", "44": "r44", "45": "r44", "46": "r44", "52": "r44", "53": "r44", "54": "r44", "55": "r44", "57": "r44" }, { "23": "r45", "24": "r45", "25": "r45", "26": "r45", "27": "r45", "28": "r45", "29": "r45", "30": "r45", "31": "r45", "32": "r45", "33": "r45", "34": "r45", "35": "r45", "36": "r45", "37": "r45", "38": "r45", "39": "r45", "40": "r45", "41": "r45", "42": "r45", "43": "r45", "44": "r45", "45": "r45", "46": "r45", "52": "r45", "53": "r45", "54": "r45", "55": "r45", "57": "r45" }, { "23": "r46", "24": "r46", "25": "r46", "26": "r46", "27": "r46", "28": "r46", "29": "r46", "30": "r46", "31": "r46", "32": "r46", "33": "r46", "34": "r46", "35": "r46", "36": "r46", "37": "r46", "38": "r46", "39": "r46", "40": "r46", "41": "r46", "42": "r46", "43": "r46", "44": "r46", "45": "r46", "46": "r46", "52": "r46", "53": "r46", "54": "r46", "55": "r46", "57": "r46" }, { "23": "r40", "24": "r40", "25": "r40", "26": "r40", "27": "r40", "28": "r40", "29": "r40", "30": "r40", "31": "r40", "32": "r40", "33": "r40", "34": "r40", "35": "r40", "36": "r40", "37": "r40", "38": "r40", "39": "r40", "40": "r40", "41": "r40", "42": "r40", "43": "r40", "44": "r40", "45": "r40", "52": "r40", "53": "r40", "54": "r40", "55": "r40", "57": "r40" }, { "25": "s12", "31": "s58" }, { "23": "r18", "24": "r18", "25": "r18", "26": "r18", "27": "r18", "28": "r18", "29": "r18", "30": "r18", "31": "r18", "32": "r18", "33": "r18", "34": "r18", "35": "r18", "36": "r18", "37": "r18", "38": "r18", "39": "r18", "40": "r18", "41": "r18", "42": "r18", "43": "r18", "44": "r18", "45": "r18", "52": "r18", "53": "r18", "54": "r18", "55": "r18", "57": "r18" }, { "25": "s12", "31": "s60" }, { "23": "r19", "24": "r19", "25": "r19", "26": "r19", "27": "r19", "28": "r19", "29": "r19", "30": "r19", "31": "r19", "32": "r19", "33": "r19", "34": "r19", "35": "r19", "36": "r19", "37": "r19", "38": "r19", "39": "r19", "40": "r19", "41": "r19", "42": "r19", "43": "r19", "44": "r19", "45": "r19", "52": "r19", "53": "r19", "54": "r19", "55": "r19", "57": "r19" }, { "25": "s12", "31": "s62" }, { "23": "r20", "24": "r20", "25": "r20", "26": "r20", "27": "r20", "28": "r20", "29": "r20", "30": "r20", "31": "r20", "32": "r20", "33": "r20", "34": "r20", "35": "r20", "36": "r20", "37": "r20", "38": "r20", "39": "r20", "40": "r20", "41": "r20", "42": "r20", "43": "r20", "44": "r20", "45": "r20", "52": "r20", "53": "r20", "54": "r20", "55": "r20", "57": "r20" }, { "25": "s12", "31": "s64" }, { "23": "r21", "24": "r21", "25": "r21", "26": "r21", "27": "r21", "28": "r21", "29": "r21", "30": "r21", "31": "r21", "32": "r21", "33": "r21", "34": "r21", "35": "r21", "36": "r21", "37": "r21", "38": "r21", "39": "r21", "40": "r21", "41": "r21", "42": "r21", "43": "r21", "44": "r21", "45": "r21", "52": "r21", "53": "r21", "54": "r21", "55": "r21", "57": "r21" }, { "56": "s72" }, { "56": "r55" }, { "10": 70, "20": 73, "21": 75, "22": 76, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r56", "58": "s74" }, { "24": "r62", "28": "r62", "35": "r62", "36": "r62", "37": "r62", "38": "r62", "39": "r62", "40": "r62", "41": "r62", "42": "r62", "43": "r62", "44": "r62", "45": "r62", "56": "r62", "58": "r62" }, { "24": "r63", "28": "r63", "35": "r63", "36": "r63", "37": "r63", "38": "r63", "39": "r63", "40": "r63", "41": "r63", "42": "r63", "43": "r63", "44": "r63", "45": "r63", "56": "r63", "58": "r63" }, { "24": "r64", "28": "r64", "35": "r64", "36": "r64", "37": "r64", "38": "r64", "39": "r64", "40": "r64", "41": "r64", "42": "r64", "43": "r64", "44": "r64", "45": "r64", "56": "r64", "58": "r64" }, { "24": "r65", "28": "r65", "35": "r65", "36": "r65", "37": "r65", "38": "r65", "39": "r65", "40": "r65", "41": "r65", "42": "r65", "43": "r65", "44": "r65", "45": "r65", "56": "r65", "58": "r65" }, { "23": "r52", "24": "r52", "25": "r52", "26": "r52", "27": "r52", "28": "r52", "29": "r52", "30": "r52", "31": "r52", "32": "r52", "33": "r52", "34": "r52", "35": "r52", "36": "r52", "37": "r52", "38": "r52", "39": "r52", "40": "r52", "41": "r52", "42": "r52", "43": "r52", "44": "r52", "45": "r52", "46": "r52", "47": "r52", "48": "r52", "49": "r52", "50": "r52", "51": "r52", "52": "r52", "53": "r52", "54": "r52", "55": "r52", "57": "r52" }, { "56": "r57" }, { "10": 70, "21": 77, "22": 69, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r62", "58": "s68" }, { "56": "r59" }, { "10": 70, "20": 79, "21": 75, "22": 76, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r63", "58": "s80" }, { "10": 70, "18": 78, "19": 66, "21": 67, "22": 69, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r54", "58": "s68" }, { "56": "r58" }, { "56": "r60" }, { "10": 70, "21": 81, "22": 69, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r62", "58": "s68" }, { "10": 70, "18": 82, "19": 66, "21": 67, "22": 69, "24": "s28", "28": "s71", "35": "s29", "36": "s30", "37": "s31", "38": "s32", "39": "s33", "40": "s34", "41": "s35", "42": "s36", "43": "s37", "44": "s38", "45": "s39", "56": "r54", "58": "s68" }, { "56": "r61" }, { "56": "s84" }, { "23": "r53", "24": "r53", "25": "r53", "26": "r53", "27": "r53", "28": "r53", "29": "r53", "30": "r53", "31": "r53", "32": "r53", "33": "r53", "34": "r53", "35": "r53", "36": "r53", "37": "r53", "38": "r53", "39": "r53", "40": "r53", "41": "r53", "42": "r53", "43": "r53", "44": "r53", "45": "r53", "46": "r53", "47": "r53", "48": "r53", "49": "r53", "50": "r53", "51": "r53", "52": "r53", "53": "r53", "54": "r53", "55": "r53", "57": "r53" }, { "25": "s12", "31": "s86" }, { "23": "r49", "24": "r49", "25": "r49", "26": "r49", "27": "r49", "28": "r49", "29": "r49", "30": "r49", "31": "r49", "32": "r49", "33": "r49", "34": "r49", "35": "r49", "36": "r49", "37": "r49", "38": "r49", "39": "r49", "40": "r49", "41": "r49", "42": "r49", "43": "r49", "44": "r49", "45": "r49", "46": "r49", "47": "r49", "48": "r49", "49": "r49", "50": "r49", "51": "r49", "52": "r49", "53": "r49", "54": "r49", "55": "r49", "57": "r49" }, { "25": "s12", "31": "s88" }, { "23": "r50", "24": "r50", "25": "r50", "26": "r50", "27": "r50", "28": "r50", "29": "r50", "30": "r50", "31": "r50", "32": "r50", "33": "r50", "34": "r50", "35": "r50", "36": "r50", "37": "r50", "38": "r50", "39": "r50", "40": "r50", "41": "r50", "42": "r50", "43": "r50", "44": "r50", "45": "r50", "46": "r50", "47": "r50", "48": "r50", "49": "r50", "50": "r50", "51": "r50", "52": "r50", "53": "r50", "54": "r50", "55": "r50", "57": "r50" }, { "25": "s12", "31": "s90" }, { "23": "r51", "24": "r51", "25": "r51", "26": "r51", "27": "r51", "28": "r51", "29": "r51", "30": "r51", "31": "r51", "32": "r51", "33": "r51", "34": "r51", "35": "r51", "36": "r51", "37": "r51", "38": "r51", "39": "r51", "40": "r51", "41": "r51", "42": "r51", "43": "r51", "44": "r51", "45": "r51", "46": "r51", "47": "r51", "48": "r51", "49": "r51", "50": "r51", "51": "r51", "52": "r51", "53": "r51", "54": "r51", "55": "r51", "57": "r51" }];
|
|
|
|
/**
|
|
* Parsing stack.
|
|
*/
|
|
var stack = [];
|
|
|
|
/**
|
|
* Tokenizer instance.
|
|
*/
|
|
var tokenizer = void 0;
|
|
/**
|
|
* Generic tokenizer used by the parser in the Syntax tool.
|
|
*
|
|
* https://www.npmjs.com/package/syntax-cli
|
|
*
|
|
* See `--custom-tokinzer` to skip this generation, and use a custom one.
|
|
*/
|
|
|
|
var lexRules = [[/^#[^\n]+/, function () {/* skip comments */}], [/^\s+/, function () {/* skip whitespace */}], [/^-/, function () {
|
|
return 'DASH';
|
|
}], [/^\//, function () {
|
|
return 'CHAR';
|
|
}], [/^#/, function () {
|
|
return 'CHAR';
|
|
}], [/^\|/, function () {
|
|
return 'CHAR';
|
|
}], [/^\./, function () {
|
|
return 'CHAR';
|
|
}], [/^\{/, function () {
|
|
return 'CHAR';
|
|
}], [/^\{\d+\}/, function () {
|
|
return 'RANGE_EXACT';
|
|
}], [/^\{\d+,\}/, function () {
|
|
return 'RANGE_OPEN';
|
|
}], [/^\{\d+,\d+\}/, function () {
|
|
return 'RANGE_CLOSED';
|
|
}], [/^\\k<(([\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e-\u066f\u0671-\u06d3\u06d5\u06e5-\u06e6\u06ee-\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4-\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09f0-\u09f1\u09fc\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0af9\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60-\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0cf1-\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32-\u0e33\u0e40-\u0e46\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae-\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5-\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a-\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd-\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5-\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\ude80-\ude9c\udea0-\uded0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf75\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00\ude10-\ude13\ude15-\ude17\ude19-\ude35\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee4\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd23\udf00-\udf1c\udf27\udf30-\udf45\udfe0-\udff6]|\ud804[\udc03-\udc37\udc83-\udcaf\udcd0-\udce8\udd03-\udd26\udd44\udd50-\udd72\udd76\udd83-\uddb2\uddc1-\uddc4\uddda\udddc\ude00-\ude11\ude13-\ude2b\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udede\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3d\udf50\udf5d-\udf61]|\ud805[\udc00-\udc34\udc47-\udc4a\udc5f\udc80-\udcaf\udcc4-\udcc5\udcc7\udd80-\uddae\uddd8-\udddb\ude00-\ude2f\ude44\ude80-\udeaa\udeb8\udf00-\udf1a]|\ud806[\udc00-\udc2b\udca0-\udcdf\udcff\udda0-\udda7\uddaa-\uddd0\udde1\udde3\ude00\ude0b-\ude32\ude3a\ude50\ude5c-\ude89\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc2e\udc40\udc72-\udc8f\udd00-\udd06\udd08-\udd09\udd0b-\udd30\udd46\udd60-\udd65\udd67-\udd68\udd6a-\udd89\udd98\udee0-\udef2]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\uded0-\udeed\udf00-\udf2f\udf40-\udf43\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf50\udf93-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb]|\ud838[\udd00-\udd2c\udd37-\udd3d\udd4e\udec0-\udeeb]|\ud83a[\udc00-\udcc4\udd00-\udd43\udd4b]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d])|[$_]|(\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]{1,}\}))(([\u0030-\u0039\u0041-\u005a\u005f\u0061-\u007a\u00aa\u00b5\u00b7\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u05d0-\u05ea\u05ef-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u07fd\u0800-\u082d\u0840-\u085b\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u08d3-\u08e1\u08e3-\u0963\u0966-\u096f\u0971-\u0983\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7-\u09c8\u09cb-\u09ce\u09d7\u09dc-\u09dd\u09df-\u09e3\u09e6-\u09f1\u09fc\u09fe\u0a01-\u0a03\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a3c\u0a3e-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0af9-\u0aff\u0b01-\u0b03\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57\u0b5c-\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82-\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c00-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c58-\u0c5a\u0c60-\u0c63\u0c66-\u0c6f\u0c80-\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1-\u0cf2\u0d00-\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d54-\u0d57\u0d5f-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82-\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18-\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1369-\u1371\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772-\u1773\u1780-\u17d3\u17d7\u17dc-\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1878\u1880-\u18aa\u18b0-\u18f5\u1900-\u191e\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1ab0-\u1abd\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1cd0-\u1cd2\u1cd4-\u1cfa\u1d00-\u1df9\u1dfb-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u203f-\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua827\ua840-\ua873\ua880-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua8fd-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\ua9e0-\ua9fe\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabea\uabec-\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe2f\ufe33-\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\uddfd\ude80-\ude9c\udea0-\uded0\udee0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf7a\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udca0-\udca9\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00-\ude03\ude05-\ude06\ude0c-\ude13\ude15-\ude17\ude19-\ude35\ude38-\ude3a\ude3f\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee6\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd27\udd30-\udd39\udf00-\udf1c\udf27\udf30-\udf50\udfe0-\udff6]|\ud804[\udc00-\udc46\udc66-\udc6f\udc7f-\udcba\udcd0-\udce8\udcf0-\udcf9\udd00-\udd34\udd36-\udd3f\udd44-\udd46\udd50-\udd73\udd76\udd80-\uddc4\uddc9-\uddcc\uddd0-\uddda\udddc\ude00-\ude11\ude13-\ude37\ude3e\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udeea\udef0-\udef9\udf00-\udf03\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3b-\udf44\udf47-\udf48\udf4b-\udf4d\udf50\udf57\udf5d-\udf63\udf66-\udf6c\udf70-\udf74]|\ud805[\udc00-\udc4a\udc50-\udc59\udc5e-\udc5f\udc80-\udcc5\udcc7\udcd0-\udcd9\udd80-\uddb5\uddb8-\uddc0\uddd8-\udddd\ude00-\ude40\ude44\ude50-\ude59\ude80-\udeb8\udec0-\udec9\udf00-\udf1a\udf1d-\udf2b\udf30-\udf39]|\ud806[\udc00-\udc3a\udca0-\udce9\udcff\udda0-\udda7\uddaa-\uddd7\uddda-\udde1\udde3-\udde4\ude00-\ude3e\ude47\ude50-\ude99\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc36\udc38-\udc40\udc50-\udc59\udc72-\udc8f\udc92-\udca7\udca9-\udcb6\udd00-\udd06\udd08-\udd09\udd0b-\udd36\udd3a\udd3c-\udd3d\udd3f-\udd47\udd50-\udd59\udd60-\udd65\udd67-\udd68\udd6a-\udd8e\udd90-\udd91\udd93-\udd98\udda0-\udda9\udee0-\udef6]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\ude60-\ude69\uded0-\udeed\udef0-\udef4\udf00-\udf36\udf40-\udf43\udf50-\udf59\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf4f-\udf87\udf8f-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99\udc9d-\udc9e]|\ud834[\udd65-\udd69\udd6d-\udd72\udd7b-\udd82\udd85-\udd8b\uddaa-\uddad\ude42-\ude44]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb\udfce-\udfff]|\ud836[\ude00-\ude36\ude3b-\ude6c\ude75\ude84\ude9b-\ude9f\udea1-\udeaf]|\ud838[\udc00-\udc06\udc08-\udc18\udc1b-\udc21\udc23-\udc24\udc26-\udc2a\udd00-\udd2c\udd30-\udd3d\udd40-\udd49\udd4e\udec0-\udef9]|\ud83a[\udc00-\udcc4\udcd0-\udcd6\udd00-\udd4b\udd50-\udd59]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d]|\udb40[\udd00-\uddef])|[$_]|(\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]{1,}\})|[\u200c\u200d])*>/, function () {
|
|
var groupName = yytext.slice(3, -1);
|
|
validateUnicodeGroupName(groupName, this.getCurrentState());
|
|
return 'NAMED_GROUP_REF';
|
|
}], [/^\\b/, function () {
|
|
return 'ESC_b';
|
|
}], [/^\\B/, function () {
|
|
return 'ESC_B';
|
|
}], [/^\\c[a-zA-Z]/, function () {
|
|
return 'CTRL_CH';
|
|
}], [/^\\0\d{1,2}/, function () {
|
|
return 'OCT_CODE';
|
|
}], [/^\\0/, function () {
|
|
return 'DEC_CODE';
|
|
}], [/^\\\d{1,3}/, function () {
|
|
return 'DEC_CODE';
|
|
}], [/^\\u[dD][89abAB][0-9a-fA-F]{2}\\u[dD][c-fC-F][0-9a-fA-F]{2}/, function () {
|
|
return 'U_CODE_SURROGATE';
|
|
}], [/^\\u\{[0-9a-fA-F]{1,}\}/, function () {
|
|
return 'U_CODE';
|
|
}], [/^\\u[0-9a-fA-F]{4}/, function () {
|
|
return 'U_CODE';
|
|
}], [/^\\[pP]\{\w+(?:=\w+)?\}/, function () {
|
|
return 'U_PROP_VALUE_EXP';
|
|
}], [/^\\x[0-9a-fA-F]{2}/, function () {
|
|
return 'HEX_CODE';
|
|
}], [/^\\[tnrdDsSwWvf]/, function () {
|
|
return 'META_CHAR';
|
|
}], [/^\\\//, function () {
|
|
return 'ESC_CHAR';
|
|
}], [/^\\[ #]/, function () {
|
|
return 'ESC_CHAR';
|
|
}], [/^\\[\^\$\.\*\+\?\(\)\\\[\]\{\}\|\/]/, function () {
|
|
return 'ESC_CHAR';
|
|
}], [/^\\[^*?+\[()\\|]/, function () {
|
|
var s = this.getCurrentState();
|
|
if (s === 'u_class' && yytext === "\\-") {
|
|
return 'ESC_CHAR';
|
|
} else if (s === 'u' || s === 'xu' || s === 'u_class') {
|
|
throw new SyntaxError('invalid Unicode escape ' + yytext);
|
|
}
|
|
return 'ESC_CHAR';
|
|
}], [/^\(/, function () {
|
|
return 'CHAR';
|
|
}], [/^\)/, function () {
|
|
return 'CHAR';
|
|
}], [/^\(\?=/, function () {
|
|
return 'POS_LA_ASSERT';
|
|
}], [/^\(\?!/, function () {
|
|
return 'NEG_LA_ASSERT';
|
|
}], [/^\(\?<=/, function () {
|
|
return 'POS_LB_ASSERT';
|
|
}], [/^\(\?<!/, function () {
|
|
return 'NEG_LB_ASSERT';
|
|
}], [/^\(\?:/, function () {
|
|
return 'NON_CAPTURE_GROUP';
|
|
}], [/^\(\?<(([\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e-\u066f\u0671-\u06d3\u06d5\u06e5-\u06e6\u06ee-\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4-\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09f0-\u09f1\u09fc\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0af9\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60-\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0cf1-\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32-\u0e33\u0e40-\u0e46\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae-\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5-\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a-\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd-\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5-\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\ude80-\ude9c\udea0-\uded0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf75\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00\ude10-\ude13\ude15-\ude17\ude19-\ude35\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee4\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd23\udf00-\udf1c\udf27\udf30-\udf45\udfe0-\udff6]|\ud804[\udc03-\udc37\udc83-\udcaf\udcd0-\udce8\udd03-\udd26\udd44\udd50-\udd72\udd76\udd83-\uddb2\uddc1-\uddc4\uddda\udddc\ude00-\ude11\ude13-\ude2b\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udede\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3d\udf50\udf5d-\udf61]|\ud805[\udc00-\udc34\udc47-\udc4a\udc5f\udc80-\udcaf\udcc4-\udcc5\udcc7\udd80-\uddae\uddd8-\udddb\ude00-\ude2f\ude44\ude80-\udeaa\udeb8\udf00-\udf1a]|\ud806[\udc00-\udc2b\udca0-\udcdf\udcff\udda0-\udda7\uddaa-\uddd0\udde1\udde3\ude00\ude0b-\ude32\ude3a\ude50\ude5c-\ude89\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc2e\udc40\udc72-\udc8f\udd00-\udd06\udd08-\udd09\udd0b-\udd30\udd46\udd60-\udd65\udd67-\udd68\udd6a-\udd89\udd98\udee0-\udef2]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\uded0-\udeed\udf00-\udf2f\udf40-\udf43\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf50\udf93-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb]|\ud838[\udd00-\udd2c\udd37-\udd3d\udd4e\udec0-\udeeb]|\ud83a[\udc00-\udcc4\udd00-\udd43\udd4b]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d])|[$_]|(\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]{1,}\}))(([\u0030-\u0039\u0041-\u005a\u005f\u0061-\u007a\u00aa\u00b5\u00b7\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376-\u0377\u037a-\u037d\u037f\u0386-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u05d0-\u05ea\u05ef-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u07fd\u0800-\u082d\u0840-\u085b\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u08d3-\u08e1\u08e3-\u0963\u0966-\u096f\u0971-\u0983\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7-\u09c8\u09cb-\u09ce\u09d7\u09dc-\u09dd\u09df-\u09e3\u09e6-\u09f1\u09fc\u09fe\u0a01-\u0a03\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a3c\u0a3e-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0af9-\u0aff\u0b01-\u0b03\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57\u0b5c-\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82-\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c00-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c58-\u0c5a\u0c60-\u0c63\u0c66-\u0c6f\u0c80-\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1-\u0cf2\u0d00-\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d54-\u0d57\u0d5f-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82-\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81-\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18-\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1369-\u1371\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772-\u1773\u1780-\u17d3\u17d7\u17dc-\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1878\u1880-\u18aa\u18b0-\u18f5\u1900-\u191e\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1ab0-\u1abd\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1cd0-\u1cd2\u1cd4-\u1cfa\u1d00-\u1df9\u1dfb-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u203f-\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua827\ua840-\ua873\ua880-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua8fd-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\ua9e0-\ua9fe\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabea\uabec-\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe2f\ufe33-\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c-\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\udd40-\udd74\uddfd\ude80-\ude9c\udea0-\uded0\udee0\udf00-\udf1f\udf2d-\udf4a\udf50-\udf7a\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf\udfd1-\udfd5]|\ud801[\udc00-\udc9d\udca0-\udca9\udcb0-\udcd3\udcd8-\udcfb\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37-\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udce0-\udcf2\udcf4-\udcf5\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe-\uddbf\ude00-\ude03\ude05-\ude06\ude0c-\ude13\ude15-\ude17\ude19-\ude35\ude38-\ude3a\ude3f\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee6\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48\udc80-\udcb2\udcc0-\udcf2\udd00-\udd27\udd30-\udd39\udf00-\udf1c\udf27\udf30-\udf50\udfe0-\udff6]|\ud804[\udc00-\udc46\udc66-\udc6f\udc7f-\udcba\udcd0-\udce8\udcf0-\udcf9\udd00-\udd34\udd36-\udd3f\udd44-\udd46\udd50-\udd73\udd76\udd80-\uddc4\uddc9-\uddcc\uddd0-\uddda\udddc\ude00-\ude11\ude13-\ude37\ude3e\ude80-\ude86\ude88\ude8a-\ude8d\ude8f-\ude9d\ude9f-\udea8\udeb0-\udeea\udef0-\udef9\udf00-\udf03\udf05-\udf0c\udf0f-\udf10\udf13-\udf28\udf2a-\udf30\udf32-\udf33\udf35-\udf39\udf3b-\udf44\udf47-\udf48\udf4b-\udf4d\udf50\udf57\udf5d-\udf63\udf66-\udf6c\udf70-\udf74]|\ud805[\udc00-\udc4a\udc50-\udc59\udc5e-\udc5f\udc80-\udcc5\udcc7\udcd0-\udcd9\udd80-\uddb5\uddb8-\uddc0\uddd8-\udddd\ude00-\ude40\ude44\ude50-\ude59\ude80-\udeb8\udec0-\udec9\udf00-\udf1a\udf1d-\udf2b\udf30-\udf39]|\ud806[\udc00-\udc3a\udca0-\udce9\udcff\udda0-\udda7\uddaa-\uddd7\uddda-\udde1\udde3-\udde4\ude00-\ude3e\ude47\ude50-\ude99\ude9d\udec0-\udef8]|\ud807[\udc00-\udc08\udc0a-\udc36\udc38-\udc40\udc50-\udc59\udc72-\udc8f\udc92-\udca7\udca9-\udcb6\udd00-\udd06\udd08-\udd09\udd0b-\udd36\udd3a\udd3c-\udd3d\udd3f-\udd47\udd50-\udd59\udd60-\udd65\udd67-\udd68\udd6a-\udd8e\udd90-\udd91\udd93-\udd98\udda0-\udda9\udee0-\udef6]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38\ude40-\ude5e\ude60-\ude69\uded0-\udeed\udef0-\udef4\udf00-\udf36\udf40-\udf43\udf50-\udf59\udf63-\udf77\udf7d-\udf8f]|\ud81b[\ude40-\ude7f\udf00-\udf4a\udf4f-\udf87\udf8f-\udf9f\udfe0-\udfe1\udfe3]|\ud81c[\udc00-\udfff]|\ud81d[\udc00-\udfff]|\ud81e[\udc00-\udfff]|\ud81f[\udc00-\udfff]|\ud820[\udc00-\udfff]|\ud821[\udc00-\udff7]|\ud822[\udc00-\udef2]|\ud82c[\udc00-\udd1e\udd50-\udd52\udd64-\udd67\udd70-\udefb]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99\udc9d-\udc9e]|\ud834[\udd65-\udd69\udd6d-\udd72\udd7b-\udd82\udd85-\udd8b\uddaa-\uddad\ude42-\ude44]|\ud835[\udc00-\udc54\udc56-\udc9c\udc9e-\udc9f\udca2\udca5-\udca6\udca9-\udcac\udcae-\udcb9\udcbb\udcbd-\udcc3\udcc5-\udd05\udd07-\udd0a\udd0d-\udd14\udd16-\udd1c\udd1e-\udd39\udd3b-\udd3e\udd40-\udd44\udd46\udd4a-\udd50\udd52-\udea5\udea8-\udec0\udec2-\udeda\udedc-\udefa\udefc-\udf14\udf16-\udf34\udf36-\udf4e\udf50-\udf6e\udf70-\udf88\udf8a-\udfa8\udfaa-\udfc2\udfc4-\udfcb\udfce-\udfff]|\ud836[\ude00-\ude36\ude3b-\ude6c\ude75\ude84\ude9b-\ude9f\udea1-\udeaf]|\ud838[\udc00-\udc06\udc08-\udc18\udc1b-\udc21\udc23-\udc24\udc26-\udc2a\udd00-\udd2c\udd30-\udd3d\udd40-\udd49\udd4e\udec0-\udef9]|\ud83a[\udc00-\udcc4\udcd0-\udcd6\udd00-\udd4b\udd50-\udd59]|\ud83b[\ude00-\ude03\ude05-\ude1f\ude21-\ude22\ude24\ude27\ude29-\ude32\ude34-\ude37\ude39\ude3b\ude42\ude47\ude49\ude4b\ude4d-\ude4f\ude51-\ude52\ude54\ude57\ude59\ude5b\ude5d\ude5f\ude61-\ude62\ude64\ude67-\ude6a\ude6c-\ude72\ude74-\ude77\ude79-\ude7c\ude7e\ude80-\ude89\ude8b-\ude9b\udea1-\udea3\udea5-\udea9\udeab-\udebb]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d\udc20-\udfff]|\ud86f[\udc00-\udfff]|\ud870[\udc00-\udfff]|\ud871[\udc00-\udfff]|\ud872[\udc00-\udfff]|\ud873[\udc00-\udea1\udeb0-\udfff]|\ud874[\udc00-\udfff]|\ud875[\udc00-\udfff]|\ud876[\udc00-\udfff]|\ud877[\udc00-\udfff]|\ud878[\udc00-\udfff]|\ud879[\udc00-\udfff]|\ud87a[\udc00-\udfe0]|\ud87e[\udc00-\ude1d]|\udb40[\udd00-\uddef])|[$_]|(\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]{1,}\})|[\u200c\u200d])*>/, function () {
|
|
yytext = yytext.slice(3, -1);
|
|
validateUnicodeGroupName(yytext, this.getCurrentState());
|
|
return 'NAMED_CAPTURE_GROUP';
|
|
}], [/^\(/, function () {
|
|
return 'L_PAREN';
|
|
}], [/^\)/, function () {
|
|
return 'R_PAREN';
|
|
}], [/^[*?+[^$]/, function () {
|
|
return 'CHAR';
|
|
}], [/^\\\]/, function () {
|
|
return 'ESC_CHAR';
|
|
}], [/^\]/, function () {
|
|
this.popState();return 'R_BRACKET';
|
|
}], [/^\^/, function () {
|
|
return 'BOS';
|
|
}], [/^\$/, function () {
|
|
return 'EOS';
|
|
}], [/^\*/, function () {
|
|
return 'STAR';
|
|
}], [/^\?/, function () {
|
|
return 'Q_MARK';
|
|
}], [/^\+/, function () {
|
|
return 'PLUS';
|
|
}], [/^\|/, function () {
|
|
return 'BAR';
|
|
}], [/^\./, function () {
|
|
return 'ANY';
|
|
}], [/^\//, function () {
|
|
return 'SLASH';
|
|
}], [/^[^*?+\[()\\|]/, function () {
|
|
return 'CHAR';
|
|
}], [/^\[\^/, function () {
|
|
var s = this.getCurrentState();this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class');return 'NEG_CLASS';
|
|
}], [/^\[/, function () {
|
|
var s = this.getCurrentState();this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class');return 'L_BRACKET';
|
|
}]];
|
|
var lexRulesByConditions = { "INITIAL": [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 22, 23, 24, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "u": [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "xu": [0, 1, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "x": [0, 1, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 22, 23, 24, 26, 27, 30, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "u_class": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "class": [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 22, 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51] };
|
|
|
|
var EOF_TOKEN = {
|
|
type: EOF,
|
|
value: ''
|
|
};
|
|
|
|
tokenizer = {
|
|
initString: function initString(string) {
|
|
this._string = string;
|
|
this._cursor = 0;
|
|
|
|
this._states = ['INITIAL'];
|
|
this._tokensQueue = [];
|
|
|
|
this._currentLine = 1;
|
|
this._currentColumn = 0;
|
|
this._currentLineBeginOffset = 0;
|
|
|
|
/**
|
|
* Matched token location data.
|
|
*/
|
|
this._tokenStartOffset = 0;
|
|
this._tokenEndOffset = 0;
|
|
this._tokenStartLine = 1;
|
|
this._tokenEndLine = 1;
|
|
this._tokenStartColumn = 0;
|
|
this._tokenEndColumn = 0;
|
|
|
|
return this;
|
|
},
|
|
|
|
|
|
/**
|
|
* Returns tokenizer states.
|
|
*/
|
|
getStates: function getStates() {
|
|
return this._states;
|
|
},
|
|
getCurrentState: function getCurrentState() {
|
|
return this._states[this._states.length - 1];
|
|
},
|
|
pushState: function pushState(state) {
|
|
this._states.push(state);
|
|
},
|
|
begin: function begin(state) {
|
|
this.pushState(state);
|
|
},
|
|
popState: function popState() {
|
|
if (this._states.length > 1) {
|
|
return this._states.pop();
|
|
}
|
|
return this._states[0];
|
|
},
|
|
getNextToken: function getNextToken() {
|
|
// Something was queued, return it.
|
|
if (this._tokensQueue.length > 0) {
|
|
return this.onToken(this._toToken(this._tokensQueue.shift()));
|
|
}
|
|
|
|
if (!this.hasMoreTokens()) {
|
|
return this.onToken(EOF_TOKEN);
|
|
}
|
|
|
|
var string = this._string.slice(this._cursor);
|
|
var lexRulesForState = lexRulesByConditions[this.getCurrentState()];
|
|
|
|
for (var i = 0; i < lexRulesForState.length; i++) {
|
|
var lexRuleIndex = lexRulesForState[i];
|
|
var lexRule = lexRules[lexRuleIndex];
|
|
|
|
var matched = this._match(string, lexRule[0]);
|
|
|
|
// Manual handling of EOF token (the end of string). Return it
|
|
// as `EOF` symbol.
|
|
if (string === '' && matched === '') {
|
|
this._cursor++;
|
|
}
|
|
|
|
if (matched !== null) {
|
|
yytext = matched;
|
|
yytext.length;
|
|
var token = lexRule[1].call(this);
|
|
|
|
if (!token) {
|
|
return this.getNextToken();
|
|
}
|
|
|
|
// If multiple tokens are returned, save them to return
|
|
// on next `getNextToken` call.
|
|
|
|
if (Array.isArray(token)) {
|
|
var tokensToQueue = token.slice(1);
|
|
token = token[0];
|
|
if (tokensToQueue.length > 0) {
|
|
var _tokensQueue;
|
|
|
|
(_tokensQueue = this._tokensQueue).unshift.apply(_tokensQueue, _toConsumableArray$8(tokensToQueue));
|
|
}
|
|
}
|
|
|
|
return this.onToken(this._toToken(token, yytext));
|
|
}
|
|
}
|
|
|
|
if (this.isEOF()) {
|
|
this._cursor++;
|
|
return EOF_TOKEN;
|
|
}
|
|
|
|
this.throwUnexpectedToken(string[0], this._currentLine, this._currentColumn);
|
|
},
|
|
|
|
|
|
/**
|
|
* Throws default "Unexpected token" exception, showing the actual
|
|
* line from the source, pointing with the ^ marker to the bad token.
|
|
* In addition, shows `line:column` location.
|
|
*/
|
|
throwUnexpectedToken: function throwUnexpectedToken(symbol, line, column) {
|
|
var lineSource = this._string.split('\n')[line - 1];
|
|
var lineData = '';
|
|
|
|
if (lineSource) {
|
|
var pad = ' '.repeat(column);
|
|
lineData = '\n\n' + lineSource + '\n' + pad + '^\n';
|
|
}
|
|
|
|
throw new SyntaxError(lineData + 'Unexpected token: "' + symbol + '" ' + ('at ' + line + ':' + column + '.'));
|
|
},
|
|
getCursor: function getCursor() {
|
|
return this._cursor;
|
|
},
|
|
getCurrentLine: function getCurrentLine() {
|
|
return this._currentLine;
|
|
},
|
|
getCurrentColumn: function getCurrentColumn() {
|
|
return this._currentColumn;
|
|
},
|
|
_captureLocation: function _captureLocation(matched) {
|
|
var nlRe = /\n/g;
|
|
|
|
// Absolute offsets.
|
|
this._tokenStartOffset = this._cursor;
|
|
|
|
// Line-based locations, start.
|
|
this._tokenStartLine = this._currentLine;
|
|
this._tokenStartColumn = this._tokenStartOffset - this._currentLineBeginOffset;
|
|
|
|
// Extract `\n` in the matched token.
|
|
var nlMatch = void 0;
|
|
while ((nlMatch = nlRe.exec(matched)) !== null) {
|
|
this._currentLine++;
|
|
this._currentLineBeginOffset = this._tokenStartOffset + nlMatch.index + 1;
|
|
}
|
|
|
|
this._tokenEndOffset = this._cursor + matched.length;
|
|
|
|
// Line-based locations, end.
|
|
this._tokenEndLine = this._currentLine;
|
|
this._tokenEndColumn = this._currentColumn = this._tokenEndOffset - this._currentLineBeginOffset;
|
|
},
|
|
_toToken: function _toToken(tokenType) {
|
|
var yytext = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
|
|
return {
|
|
// Basic data.
|
|
type: tokenType,
|
|
value: yytext,
|
|
|
|
// Location data.
|
|
startOffset: this._tokenStartOffset,
|
|
endOffset: this._tokenEndOffset,
|
|
startLine: this._tokenStartLine,
|
|
endLine: this._tokenEndLine,
|
|
startColumn: this._tokenStartColumn,
|
|
endColumn: this._tokenEndColumn
|
|
};
|
|
},
|
|
isEOF: function isEOF() {
|
|
return this._cursor === this._string.length;
|
|
},
|
|
hasMoreTokens: function hasMoreTokens() {
|
|
return this._cursor <= this._string.length;
|
|
},
|
|
_match: function _match(string, regexp) {
|
|
var matched = string.match(regexp);
|
|
if (matched) {
|
|
// Handle `\n` in the matched token to track line numbers.
|
|
this._captureLocation(matched[0]);
|
|
this._cursor += matched[0].length;
|
|
return matched[0];
|
|
}
|
|
return null;
|
|
},
|
|
|
|
|
|
/**
|
|
* Allows analyzing, and transforming token. Default implementation
|
|
* just passes the token through.
|
|
*/
|
|
onToken: function onToken(token) {
|
|
return token;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Expose tokenizer so it can be accessed in semantic actions.
|
|
*/
|
|
yy.lexer = tokenizer;
|
|
yy.tokenizer = tokenizer;
|
|
|
|
/**
|
|
* Global parsing options. Some options can be shadowed per
|
|
* each `parse` call, if the optations are passed.
|
|
*
|
|
* Initalized to the `captureLocations` which is passed
|
|
* from the generator. Other options can be added at runtime.
|
|
*/
|
|
yy.options = {
|
|
captureLocations: true
|
|
};
|
|
|
|
/**
|
|
* Parsing module.
|
|
*/
|
|
var yyparse = {
|
|
/**
|
|
* Sets global parsing options.
|
|
*/
|
|
setOptions: function setOptions(options) {
|
|
yy.options = options;
|
|
return this;
|
|
},
|
|
|
|
|
|
/**
|
|
* Returns parsing options.
|
|
*/
|
|
getOptions: function getOptions() {
|
|
return yy.options;
|
|
},
|
|
|
|
|
|
/**
|
|
* Parses a string.
|
|
*/
|
|
parse: function parse(string, parseOptions) {
|
|
if (!tokenizer) {
|
|
throw new Error('Tokenizer instance wasn\'t specified.');
|
|
}
|
|
|
|
tokenizer.initString(string);
|
|
|
|
/**
|
|
* If parse options are passed, override global parse options for
|
|
* this call, and later restore global options.
|
|
*/
|
|
var globalOptions = yy.options;
|
|
if (parseOptions) {
|
|
yy.options = Object.assign({}, yy.options, parseOptions);
|
|
}
|
|
|
|
/**
|
|
* Allow callers to do setup work based on the
|
|
* parsing string, and passed options.
|
|
*/
|
|
yyparse.onParseBegin(string, tokenizer, yy.options);
|
|
|
|
stack.length = 0;
|
|
stack.push(0);
|
|
|
|
var token = tokenizer.getNextToken();
|
|
var shiftedToken = null;
|
|
|
|
do {
|
|
if (!token) {
|
|
// Restore options.
|
|
yy.options = globalOptions;
|
|
unexpectedEndOfInput();
|
|
}
|
|
|
|
var state = stack[stack.length - 1];
|
|
var column = tokens[token.type];
|
|
|
|
if (!table[state].hasOwnProperty(column)) {
|
|
yy.options = globalOptions;
|
|
unexpectedToken(token);
|
|
}
|
|
|
|
var entry = table[state][column];
|
|
|
|
// Shift action.
|
|
if (entry[0] === 's') {
|
|
var _loc2 = null;
|
|
|
|
if (yy.options.captureLocations) {
|
|
_loc2 = {
|
|
startOffset: token.startOffset,
|
|
endOffset: token.endOffset,
|
|
startLine: token.startLine,
|
|
endLine: token.endLine,
|
|
startColumn: token.startColumn,
|
|
endColumn: token.endColumn
|
|
};
|
|
}
|
|
|
|
shiftedToken = this.onShift(token);
|
|
|
|
stack.push({ symbol: tokens[shiftedToken.type], semanticValue: shiftedToken.value, loc: _loc2 }, Number(entry.slice(1)));
|
|
|
|
token = tokenizer.getNextToken();
|
|
}
|
|
|
|
// Reduce action.
|
|
else if (entry[0] === 'r') {
|
|
var productionNumber = entry.slice(1);
|
|
var production = productions[productionNumber];
|
|
var hasSemanticAction = typeof production[2] === 'function';
|
|
var semanticValueArgs = hasSemanticAction ? [] : null;
|
|
|
|
var locationArgs = hasSemanticAction && yy.options.captureLocations ? [] : null;
|
|
|
|
if (production[1] !== 0) {
|
|
var rhsLength = production[1];
|
|
while (rhsLength-- > 0) {
|
|
stack.pop();
|
|
var stackEntry = stack.pop();
|
|
|
|
if (hasSemanticAction) {
|
|
semanticValueArgs.unshift(stackEntry.semanticValue);
|
|
|
|
if (locationArgs) {
|
|
locationArgs.unshift(stackEntry.loc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var reduceStackEntry = { symbol: production[0] };
|
|
|
|
if (hasSemanticAction) {
|
|
yytext = shiftedToken ? shiftedToken.value : null;
|
|
shiftedToken ? shiftedToken.value.length : null;
|
|
|
|
var semanticActionArgs = locationArgs !== null ? semanticValueArgs.concat(locationArgs) : semanticValueArgs;
|
|
|
|
production[2].apply(production, _toConsumableArray$8(semanticActionArgs));
|
|
|
|
reduceStackEntry.semanticValue = __;
|
|
|
|
if (locationArgs) {
|
|
reduceStackEntry.loc = __loc;
|
|
}
|
|
}
|
|
|
|
var nextState = stack[stack.length - 1];
|
|
var symbolToReduceWith = production[0];
|
|
|
|
stack.push(reduceStackEntry, table[nextState][symbolToReduceWith]);
|
|
}
|
|
|
|
// Accept.
|
|
else if (entry === 'acc') {
|
|
stack.pop();
|
|
var parsed = stack.pop();
|
|
|
|
if (stack.length !== 1 || stack[0] !== 0 || tokenizer.hasMoreTokens()) {
|
|
// Restore options.
|
|
yy.options = globalOptions;
|
|
unexpectedToken(token);
|
|
}
|
|
|
|
if (parsed.hasOwnProperty('semanticValue')) {
|
|
yy.options = globalOptions;
|
|
yyparse.onParseEnd(parsed.semanticValue);
|
|
return parsed.semanticValue;
|
|
}
|
|
|
|
yyparse.onParseEnd();
|
|
|
|
// Restore options.
|
|
yy.options = globalOptions;
|
|
return true;
|
|
}
|
|
} while (tokenizer.hasMoreTokens() || stack.length > 1);
|
|
},
|
|
setTokenizer: function setTokenizer(customTokenizer) {
|
|
tokenizer = customTokenizer;
|
|
return yyparse;
|
|
},
|
|
getTokenizer: function getTokenizer() {
|
|
return tokenizer;
|
|
},
|
|
onParseBegin: function onParseBegin(string, tokenizer, options) {},
|
|
onParseEnd: function onParseEnd(parsed) {},
|
|
|
|
|
|
/**
|
|
* Allows analyzing, and transforming shifted token. Default implementation
|
|
* just passes the token through.
|
|
*/
|
|
onShift: function onShift(token) {
|
|
return token;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Tracks capturing groups.
|
|
*/
|
|
var capturingGroupsCount = 0;
|
|
|
|
/**
|
|
* Tracks named groups.
|
|
*/
|
|
var namedGroups = {};
|
|
|
|
/**
|
|
* Parsing string.
|
|
*/
|
|
var parsingString = '';
|
|
|
|
yyparse.onParseBegin = function (string, lexer) {
|
|
parsingString = string;
|
|
capturingGroupsCount = 0;
|
|
namedGroups = {};
|
|
|
|
var lastSlash = string.lastIndexOf('/');
|
|
var flags = string.slice(lastSlash);
|
|
|
|
if (flags.includes('x') && flags.includes('u')) {
|
|
lexer.pushState('xu');
|
|
} else {
|
|
if (flags.includes('x')) {
|
|
lexer.pushState('x');
|
|
}
|
|
if (flags.includes('u')) {
|
|
lexer.pushState('u');
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* On shifting `(` remember its number to used on reduce.
|
|
*/
|
|
yyparse.onShift = function (token) {
|
|
if (token.type === 'L_PAREN' || token.type === 'NAMED_CAPTURE_GROUP') {
|
|
token.value = new String(token.value);
|
|
token.value.groupNumber = ++capturingGroupsCount;
|
|
}
|
|
return token;
|
|
};
|
|
|
|
/**
|
|
* Extracts ranges from the range string.
|
|
*/
|
|
function getRange(text) {
|
|
var range = text.match(/\d+/g).map(Number);
|
|
|
|
if (Number.isFinite(range[1]) && range[1] < range[0]) {
|
|
throw new SyntaxError('Numbers out of order in ' + text + ' quantifier');
|
|
}
|
|
|
|
return range;
|
|
}
|
|
|
|
/**
|
|
* Checks class range
|
|
*/
|
|
function checkClassRange(from, to) {
|
|
if (from.kind === 'control' || to.kind === 'control' || !isNaN(from.codePoint) && !isNaN(to.codePoint) && from.codePoint > to.codePoint) {
|
|
throw new SyntaxError('Range ' + from.value + '-' + to.value + ' out of order in character class');
|
|
}
|
|
}
|
|
|
|
// ---------------------- Unicode property -------------------------------------------
|
|
|
|
var unicodeProperties = parserUnicodeProperties;
|
|
|
|
/**
|
|
* Unicode property.
|
|
*/
|
|
function UnicodeProperty(matched, loc) {
|
|
var negative = matched[1] === 'P';
|
|
var separatorIdx = matched.indexOf('=');
|
|
|
|
var name = matched.slice(3, separatorIdx !== -1 ? separatorIdx : -1);
|
|
var value = void 0;
|
|
|
|
// General_Category allows using only value as a shorthand.
|
|
var isShorthand = separatorIdx === -1 && unicodeProperties.isGeneralCategoryValue(name);
|
|
|
|
// Binary propery name.
|
|
var isBinaryProperty = separatorIdx === -1 && unicodeProperties.isBinaryPropertyName(name);
|
|
|
|
if (isShorthand) {
|
|
value = name;
|
|
name = 'General_Category';
|
|
} else if (isBinaryProperty) {
|
|
value = name;
|
|
} else {
|
|
if (!unicodeProperties.isValidName(name)) {
|
|
throw new SyntaxError('Invalid unicode property name: ' + name + '.');
|
|
}
|
|
|
|
value = matched.slice(separatorIdx + 1, -1);
|
|
|
|
if (!unicodeProperties.isValidValue(name, value)) {
|
|
throw new SyntaxError('Invalid ' + name + ' unicode property value: ' + value + '.');
|
|
}
|
|
}
|
|
|
|
return Node({
|
|
type: 'UnicodeProperty',
|
|
name: name,
|
|
value: value,
|
|
negative: negative,
|
|
shorthand: isShorthand,
|
|
binary: isBinaryProperty,
|
|
canonicalName: unicodeProperties.getCanonicalName(name) || name,
|
|
canonicalValue: unicodeProperties.getCanonicalValue(value) || value
|
|
}, loc);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
|
|
|
|
/**
|
|
* Creates a character node.
|
|
*/
|
|
function Char(value, kind, loc) {
|
|
var symbol = void 0;
|
|
var codePoint = void 0;
|
|
|
|
switch (kind) {
|
|
case 'decimal':
|
|
{
|
|
codePoint = Number(value.slice(1));
|
|
symbol = String.fromCodePoint(codePoint);
|
|
break;
|
|
}
|
|
case 'oct':
|
|
{
|
|
codePoint = parseInt(value.slice(1), 8);
|
|
symbol = String.fromCodePoint(codePoint);
|
|
break;
|
|
}
|
|
case 'hex':
|
|
case 'unicode':
|
|
{
|
|
if (value.lastIndexOf('\\u') > 0) {
|
|
var _value$split$slice = value.split('\\u').slice(1),
|
|
_value$split$slice2 = _slicedToArray$2(_value$split$slice, 2),
|
|
lead = _value$split$slice2[0],
|
|
trail = _value$split$slice2[1];
|
|
|
|
lead = parseInt(lead, 16);
|
|
trail = parseInt(trail, 16);
|
|
codePoint = (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000;
|
|
|
|
symbol = String.fromCodePoint(codePoint);
|
|
} else {
|
|
var hex = value.slice(2).replace('{', '');
|
|
codePoint = parseInt(hex, 16);
|
|
if (codePoint > 0x10ffff) {
|
|
throw new SyntaxError('Bad character escape sequence: ' + value);
|
|
}
|
|
|
|
symbol = String.fromCodePoint(codePoint);
|
|
}
|
|
break;
|
|
}
|
|
case 'meta':
|
|
{
|
|
switch (value) {
|
|
case '\\t':
|
|
symbol = '\t';
|
|
codePoint = symbol.codePointAt(0);
|
|
break;
|
|
case '\\n':
|
|
symbol = '\n';
|
|
codePoint = symbol.codePointAt(0);
|
|
break;
|
|
case '\\r':
|
|
symbol = '\r';
|
|
codePoint = symbol.codePointAt(0);
|
|
break;
|
|
case '\\v':
|
|
symbol = '\v';
|
|
codePoint = symbol.codePointAt(0);
|
|
break;
|
|
case '\\f':
|
|
symbol = '\f';
|
|
codePoint = symbol.codePointAt(0);
|
|
break;
|
|
case '\\b':
|
|
symbol = '\b';
|
|
codePoint = symbol.codePointAt(0);
|
|
case '\\0':
|
|
symbol = '\0';
|
|
codePoint = 0;
|
|
case '.':
|
|
symbol = '.';
|
|
codePoint = NaN;
|
|
break;
|
|
default:
|
|
codePoint = NaN;
|
|
}
|
|
break;
|
|
}
|
|
case 'simple':
|
|
{
|
|
symbol = value;
|
|
codePoint = symbol.codePointAt(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Node({
|
|
type: 'Char',
|
|
value: value,
|
|
kind: kind,
|
|
symbol: symbol,
|
|
codePoint: codePoint
|
|
}, loc);
|
|
}
|
|
|
|
/**
|
|
* Valid flags per current ECMAScript spec and
|
|
* stage 3+ proposals.
|
|
*/
|
|
var validFlags = 'gimsuxy';
|
|
|
|
/**
|
|
* Checks the flags are valid, and that
|
|
* we don't duplicate flags.
|
|
*/
|
|
function checkFlags(flags) {
|
|
var seen = new Set();
|
|
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = flags[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var flag = _step.value;
|
|
|
|
if (seen.has(flag) || !validFlags.includes(flag)) {
|
|
throw new SyntaxError('Invalid flags: ' + flags);
|
|
}
|
|
seen.add(flag);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
return flags.split('').sort().join('');
|
|
}
|
|
|
|
/**
|
|
* Parses patterns like \1, \2, etc. either as a backreference
|
|
* to a group, or a deciaml char code.
|
|
*/
|
|
function GroupRefOrDecChar(text, textLoc) {
|
|
var reference = Number(text.slice(1));
|
|
|
|
if (reference > 0 && reference <= capturingGroupsCount) {
|
|
return Node({
|
|
type: 'Backreference',
|
|
kind: 'number',
|
|
number: reference,
|
|
reference: reference
|
|
}, textLoc);
|
|
}
|
|
|
|
return Char(text, 'decimal', textLoc);
|
|
}
|
|
|
|
/**
|
|
* Unicode names.
|
|
*/
|
|
var uReStart = /^\\u[0-9a-fA-F]{4}/; // only matches start of string
|
|
var ucpReStart = /^\\u\{[0-9a-fA-F]{1,}\}/; // only matches start of string
|
|
var ucpReAnywhere = /\\u\{[0-9a-fA-F]{1,}\}/; // matches anywhere in string
|
|
|
|
/**
|
|
* Validates Unicode group name.
|
|
*/
|
|
function validateUnicodeGroupName(name, state) {
|
|
var isUnicodeName = ucpReAnywhere.test(name);
|
|
var isUnicodeState = state === 'u' || state === 'xu' || state === 'u_class';
|
|
|
|
if (isUnicodeName && !isUnicodeState) {
|
|
throw new SyntaxError('invalid group Unicode name "' + name + '", use `u` flag.');
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
// Matches the following production: https://tc39.es/ecma262/#prod-RegExpUnicodeEscapeSequence
|
|
//
|
|
// RegExpUnicodeEscapeSequence ::
|
|
// `u` LeadSurrogate `\u` TrailSurrogate # as 'leadSurrogate', 'trailSurrogate'
|
|
// `u` LeadSurrogate # as 'leadSurrogateOnly'
|
|
// `u` TrailSurrogate # as 'trailSurrogateOnly'
|
|
// `u` NonSurrogate # as 'nonSurrogate'
|
|
// `u` `{` CodePoint `}` # as 'codePoint'
|
|
//
|
|
// LeadSurrogate ::
|
|
// Hex4Digits but only if the SV of Hex4Digits is in the inclusive range 0xD800 to 0xDBFF # [dD][89aAbB][0-9a-fA-F]{2}
|
|
//
|
|
// TrailSurrogate ::
|
|
// Hex4Digits but only if the SV of Hex4Digits is in the inclusive range 0xDC00 to 0xDFFF # [dD][c-fC-F][0-9a-fA-F]{2}
|
|
//
|
|
// NonSurrogate ::
|
|
// Hex4Digits but only if the SV of Hex4Digits is not in the inclusive range 0xD800 to 0xDFFF # [0-9a-ce-fA-CE-F][0-9a-fA-F]{3}|[dD][0-7][0-9a-fA-F]{2}
|
|
//
|
|
// CodePoint ::
|
|
// HexDigits but only if MV of HexDigits ≤ 0x10FFFF # 0*(?:[0-9a-fA-F]{1,5}|10[0-9a-fA-F]{4})
|
|
//
|
|
var uidRe = /\\u(?:([dD][89aAbB][0-9a-fA-F]{2})\\u([dD][c-fC-F][0-9a-fA-F]{2})|([dD][89aAbB][0-9a-fA-F]{2})|([dD][c-fC-F][0-9a-fA-F]{2})|([0-9a-ce-fA-CE-F][0-9a-fA-F]{3}|[dD][0-7][0-9a-fA-F]{2})|\{(0*(?:[0-9a-fA-F]{1,5}|10[0-9a-fA-F]{4}))\})/;
|
|
|
|
function decodeUnicodeGroupName(name) {
|
|
return name.replace(new RegExp(uidRe, 'g'), function (_, leadSurrogate, trailSurrogate, leadSurrogateOnly, trailSurrogateOnly, nonSurrogate, codePoint) {
|
|
if (leadSurrogate) {
|
|
return String.fromCodePoint(parseInt(leadSurrogate, 16), parseInt(trailSurrogate, 16));
|
|
}
|
|
if (leadSurrogateOnly) {
|
|
return String.fromCodePoint(parseInt(leadSurrogateOnly, 16));
|
|
}
|
|
if (trailSurrogateOnly) {
|
|
// TODO: Per the spec: https://tc39.es/ecma262/#prod-RegExpUnicodeEscapeSequence
|
|
// > Each `\u` TrailSurrogate for which the choice of associated `u` LeadSurrogate is ambiguous shall be associated with the nearest possible `u` LeadSurrogate that would otherwise have no corresponding `\u` TrailSurrogate.
|
|
return String.fromCodePoint(parseInt(trailSurrogateOnly, 16));
|
|
}
|
|
if (nonSurrogate) {
|
|
return String.fromCodePoint(parseInt(nonSurrogate, 16));
|
|
}
|
|
if (codePoint) {
|
|
return String.fromCodePoint(parseInt(codePoint, 16));
|
|
}
|
|
return _;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Extracts from `\k<foo>` pattern either a backreference
|
|
* to a named capturing group (if it presents), or parses it
|
|
* as a list of char: `\k`, `<`, `f`, etc.
|
|
*/
|
|
function NamedGroupRefOrChars(text, textLoc) {
|
|
var referenceRaw = text.slice(3, -1);
|
|
var reference = decodeUnicodeGroupName(referenceRaw);
|
|
|
|
if (namedGroups.hasOwnProperty(reference)) {
|
|
return Node({
|
|
type: 'Backreference',
|
|
kind: 'name',
|
|
number: namedGroups[reference],
|
|
reference: reference,
|
|
referenceRaw: referenceRaw
|
|
}, textLoc);
|
|
}
|
|
|
|
// Else `\k<foo>` should be parsed as a list of `Char`s.
|
|
// This is really a 0.01% edge case, but we should handle it.
|
|
|
|
var startOffset = null;
|
|
var startLine = null;
|
|
var endLine = null;
|
|
var startColumn = null;
|
|
|
|
if (textLoc) {
|
|
startOffset = textLoc.startOffset;
|
|
startLine = textLoc.startLine;
|
|
endLine = textLoc.endLine;
|
|
startColumn = textLoc.startColumn;
|
|
}
|
|
|
|
var charRe = /^[\w$<>]/;
|
|
var loc = void 0;
|
|
|
|
var chars = [
|
|
// Init to first \k, taking 2 symbols.
|
|
Char(text.slice(1, 2), 'simple', startOffset ? {
|
|
startLine: startLine,
|
|
endLine: endLine,
|
|
startColumn: startColumn,
|
|
startOffset: startOffset,
|
|
endOffset: startOffset += 2,
|
|
endColumn: startColumn += 2
|
|
} : null)];
|
|
|
|
// For \k
|
|
chars[0].escaped = true;
|
|
|
|
// Other symbols.
|
|
text = text.slice(2);
|
|
|
|
while (text.length > 0) {
|
|
var matched = null;
|
|
|
|
// Unicode, \u003B or \u{003B}
|
|
if ((matched = text.match(uReStart)) || (matched = text.match(ucpReStart))) {
|
|
if (startOffset) {
|
|
loc = {
|
|
startLine: startLine,
|
|
endLine: endLine,
|
|
startColumn: startColumn,
|
|
startOffset: startOffset,
|
|
endOffset: startOffset += matched[0].length,
|
|
endColumn: startColumn += matched[0].length
|
|
};
|
|
}
|
|
chars.push(Char(matched[0], 'unicode', loc));
|
|
text = text.slice(matched[0].length);
|
|
}
|
|
|
|
// Simple char.
|
|
else if (matched = text.match(charRe)) {
|
|
if (startOffset) {
|
|
loc = {
|
|
startLine: startLine,
|
|
endLine: endLine,
|
|
startColumn: startColumn,
|
|
startOffset: startOffset,
|
|
endOffset: ++startOffset,
|
|
endColumn: ++startColumn
|
|
};
|
|
}
|
|
chars.push(Char(matched[0], 'simple', loc));
|
|
text = text.slice(1);
|
|
}
|
|
}
|
|
|
|
return chars;
|
|
}
|
|
|
|
/**
|
|
* Creates an AST node with a location.
|
|
*/
|
|
function Node(node, loc) {
|
|
if (yy.options.captureLocations) {
|
|
node.loc = {
|
|
source: parsingString.slice(loc.startOffset, loc.endOffset),
|
|
start: {
|
|
line: loc.startLine,
|
|
column: loc.startColumn,
|
|
offset: loc.startOffset
|
|
},
|
|
end: {
|
|
line: loc.endLine,
|
|
column: loc.endColumn,
|
|
offset: loc.endOffset
|
|
}
|
|
};
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Creates location node.
|
|
*/
|
|
function loc(start, end) {
|
|
if (!yy.options.captureLocations) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
startOffset: start.startOffset,
|
|
endOffset: end.endOffset,
|
|
startLine: start.startLine,
|
|
endLine: end.endLine,
|
|
startColumn: start.startColumn,
|
|
endColumn: end.endColumn
|
|
};
|
|
}
|
|
|
|
function unexpectedToken(token) {
|
|
if (token.type === EOF) {
|
|
unexpectedEndOfInput();
|
|
}
|
|
|
|
tokenizer.throwUnexpectedToken(token.value, token.startLine, token.startColumn);
|
|
}
|
|
|
|
function unexpectedEndOfInput() {
|
|
parseError('Unexpected end of input.');
|
|
}
|
|
|
|
function parseError(message) {
|
|
throw new SyntaxError(message);
|
|
}
|
|
|
|
var regexpTree$4 = yyparse;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var regexpTreeParser = regexpTree$4;
|
|
|
|
/**
|
|
* Original parse function.
|
|
*/
|
|
var generatedParseFn = regexpTreeParser.parse.bind(regexpTreeParser);
|
|
|
|
/**
|
|
* Parses a regular expression.
|
|
*
|
|
* Override original `regexpTreeParser.parse` to convert a value to a string,
|
|
* since in regexp-tree we may pass strings, and RegExp instance.
|
|
*/
|
|
regexpTreeParser.parse = function (regexp, options) {
|
|
return generatedParseFn('' + regexp, options);
|
|
};
|
|
|
|
// By default do not capture locations; callers may override.
|
|
regexpTreeParser.setOptions({ captureLocations: false });
|
|
|
|
var parser$4 = regexpTreeParser;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var _createClass$6 = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _classCallCheck$6(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var DEFAULT_COLLECTION_PROP = 'expressions';
|
|
var DEFAULT_SINGLE_PROP = 'expression';
|
|
|
|
/**
|
|
* NodePath class encapsulates a traversing node,
|
|
* its parent node, property name in the parent node, and
|
|
* an index (in case if a node is part of a collection).
|
|
* It also provides set of methods for AST manipulation.
|
|
*/
|
|
|
|
var NodePath$3 = function () {
|
|
/**
|
|
* NodePath constructor.
|
|
*
|
|
* @param Object node - an AST node
|
|
* @param NodePath parentPath - a nullable parent path
|
|
* @param string property - property name of the node in the parent
|
|
* @param number index - index of the node in a collection.
|
|
*/
|
|
function NodePath(node) {
|
|
var parentPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
|
|
|
|
_classCallCheck$6(this, NodePath);
|
|
|
|
this.node = node;
|
|
this.parentPath = parentPath;
|
|
this.parent = parentPath ? parentPath.node : null;
|
|
this.property = property;
|
|
this.index = index;
|
|
}
|
|
|
|
_createClass$6(NodePath, [{
|
|
key: '_enforceProp',
|
|
value: function _enforceProp(property) {
|
|
if (!this.node.hasOwnProperty(property)) {
|
|
throw new Error('Node of type ' + this.node.type + ' doesn\'t have "' + property + '" collection.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a node into a children collection or the single child.
|
|
* By default child nodes are supposed to be under `expressions` property.
|
|
* An explicit property can be passed.
|
|
*
|
|
* @param Object node - a node to set into a collection or as single child
|
|
* @param number index - index at which to set
|
|
* @param string property - name of the collection or single property
|
|
*/
|
|
|
|
}, {
|
|
key: 'setChild',
|
|
value: function setChild(node) {
|
|
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
|
|
|
|
var childPath = void 0;
|
|
if (index != null) {
|
|
if (!property) {
|
|
property = DEFAULT_COLLECTION_PROP;
|
|
}
|
|
this._enforceProp(property);
|
|
this.node[property][index] = node;
|
|
childPath = NodePath.getForNode(node, this, property, index);
|
|
} else {
|
|
if (!property) {
|
|
property = DEFAULT_SINGLE_PROP;
|
|
}
|
|
this._enforceProp(property);
|
|
this.node[property] = node;
|
|
childPath = NodePath.getForNode(node, this, property, null);
|
|
}
|
|
return childPath;
|
|
}
|
|
|
|
/**
|
|
* Appends a node to a children collection.
|
|
* By default child nodes are supposed to be under `expressions` property.
|
|
* An explicit property can be passed.
|
|
*
|
|
* @param Object node - a node to set into a collection or as single child
|
|
* @param string property - name of the collection or single property
|
|
*/
|
|
|
|
}, {
|
|
key: 'appendChild',
|
|
value: function appendChild(node) {
|
|
var property = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
|
|
|
|
if (!property) {
|
|
property = DEFAULT_COLLECTION_PROP;
|
|
}
|
|
this._enforceProp(property);
|
|
var end = this.node[property].length;
|
|
return this.setChild(node, end, property);
|
|
}
|
|
|
|
/**
|
|
* Inserts a node into a collection.
|
|
* By default child nodes are supposed to be under `expressions` property.
|
|
* An explicit property can be passed.
|
|
*
|
|
* @param Object node - a node to insert into a collection
|
|
* @param number index - index at which to insert
|
|
* @param string property - name of the collection property
|
|
*/
|
|
|
|
}, {
|
|
key: 'insertChildAt',
|
|
value: function insertChildAt(node, index) {
|
|
var property = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_COLLECTION_PROP;
|
|
|
|
this._enforceProp(property);
|
|
|
|
this.node[property].splice(index, 0, node);
|
|
|
|
// If we inserted a node before the traversing index,
|
|
// we should increase the later.
|
|
if (index <= NodePath.getTraversingIndex()) {
|
|
NodePath.updateTraversingIndex(+1);
|
|
}
|
|
|
|
this._rebuildIndex(this.node, property);
|
|
}
|
|
|
|
/**
|
|
* Removes a node.
|
|
*/
|
|
|
|
}, {
|
|
key: 'remove',
|
|
value: function remove() {
|
|
if (this.isRemoved()) {
|
|
return;
|
|
}
|
|
NodePath.registry.delete(this.node);
|
|
|
|
this.node = null;
|
|
|
|
if (!this.parent) {
|
|
return;
|
|
}
|
|
|
|
// A node is in a collection.
|
|
if (this.index !== null) {
|
|
this.parent[this.property].splice(this.index, 1);
|
|
|
|
// If we remove a node before the traversing index,
|
|
// we should increase the later.
|
|
if (this.index <= NodePath.getTraversingIndex()) {
|
|
NodePath.updateTraversingIndex(-1);
|
|
}
|
|
|
|
// Rebuild index.
|
|
this._rebuildIndex(this.parent, this.property);
|
|
|
|
this.index = null;
|
|
this.property = null;
|
|
|
|
return;
|
|
}
|
|
|
|
// A simple node.
|
|
delete this.parent[this.property];
|
|
this.property = null;
|
|
}
|
|
|
|
/**
|
|
* Rebuilds child nodes index (used on remove/insert).
|
|
*/
|
|
|
|
}, {
|
|
key: '_rebuildIndex',
|
|
value: function _rebuildIndex(parent, property) {
|
|
var parentPath = NodePath.getForNode(parent);
|
|
|
|
for (var i = 0; i < parent[property].length; i++) {
|
|
var path = NodePath.getForNode(parent[property][i], parentPath, property, i);
|
|
path.index = i;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the path was removed.
|
|
*/
|
|
|
|
}, {
|
|
key: 'isRemoved',
|
|
value: function isRemoved() {
|
|
return this.node === null;
|
|
}
|
|
|
|
/**
|
|
* Replaces a node with the passed one.
|
|
*/
|
|
|
|
}, {
|
|
key: 'replace',
|
|
value: function replace(newNode) {
|
|
NodePath.registry.delete(this.node);
|
|
|
|
this.node = newNode;
|
|
|
|
if (!this.parent) {
|
|
return null;
|
|
}
|
|
|
|
// A node is in a collection.
|
|
if (this.index !== null) {
|
|
this.parent[this.property][this.index] = newNode;
|
|
}
|
|
|
|
// A simple node.
|
|
else {
|
|
this.parent[this.property] = newNode;
|
|
}
|
|
|
|
// Rebuild the node path for the new node.
|
|
return NodePath.getForNode(newNode, this.parentPath, this.property, this.index);
|
|
}
|
|
|
|
/**
|
|
* Updates a node inline.
|
|
*/
|
|
|
|
}, {
|
|
key: 'update',
|
|
value: function update(nodeProps) {
|
|
Object.assign(this.node, nodeProps);
|
|
}
|
|
|
|
/**
|
|
* Returns parent.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getParent',
|
|
value: function getParent() {
|
|
return this.parentPath;
|
|
}
|
|
|
|
/**
|
|
* Returns nth child.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getChild',
|
|
value: function getChild() {
|
|
var n = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
|
|
if (this.node.expressions) {
|
|
return NodePath.getForNode(this.node.expressions[n], this, DEFAULT_COLLECTION_PROP, n);
|
|
} else if (this.node.expression && n == 0) {
|
|
return NodePath.getForNode(this.node.expression, this, DEFAULT_SINGLE_PROP);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Whether a path node is syntactically equal to the passed one.
|
|
*
|
|
* NOTE: we don't rely on `source` property from the `loc` data
|
|
* (which would be the fastest comparison), since it might be unsync
|
|
* after several modifications. We use here simple `JSON.stringify`
|
|
* excluding the `loc` data.
|
|
*
|
|
* @param NodePath other - path to compare to.
|
|
* @return boolean
|
|
*/
|
|
|
|
}, {
|
|
key: 'hasEqualSource',
|
|
value: function hasEqualSource(path) {
|
|
return JSON.stringify(this.node, jsonSkipLoc) === JSON.stringify(path.node, jsonSkipLoc);
|
|
}
|
|
|
|
/**
|
|
* JSON-encodes a node skipping location.
|
|
*/
|
|
|
|
}, {
|
|
key: 'jsonEncode',
|
|
value: function jsonEncode() {
|
|
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
format = _ref.format,
|
|
useLoc = _ref.useLoc;
|
|
|
|
return JSON.stringify(this.node, useLoc ? null : jsonSkipLoc, format);
|
|
}
|
|
|
|
/**
|
|
* Returns previous sibling.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getPreviousSibling',
|
|
value: function getPreviousSibling() {
|
|
if (!this.parent || this.index == null) {
|
|
return null;
|
|
}
|
|
return NodePath.getForNode(this.parent[this.property][this.index - 1], NodePath.getForNode(this.parent), this.property, this.index - 1);
|
|
}
|
|
|
|
/**
|
|
* Returns next sibling.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getNextSibling',
|
|
value: function getNextSibling() {
|
|
if (!this.parent || this.index == null) {
|
|
return null;
|
|
}
|
|
return NodePath.getForNode(this.parent[this.property][this.index + 1], NodePath.getForNode(this.parent), this.property, this.index + 1);
|
|
}
|
|
|
|
/**
|
|
* Returns a NodePath instance for a node.
|
|
*
|
|
* The same NodePath can be reused in several places, e.g.
|
|
* a parent node passed for all its children.
|
|
*/
|
|
|
|
}], [{
|
|
key: 'getForNode',
|
|
value: function getForNode(node) {
|
|
var parentPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
var prop = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : -1;
|
|
|
|
if (!node) {
|
|
return null;
|
|
}
|
|
|
|
if (!NodePath.registry.has(node)) {
|
|
NodePath.registry.set(node, new NodePath(node, parentPath, prop, index == -1 ? null : index));
|
|
}
|
|
|
|
var path = NodePath.registry.get(node);
|
|
|
|
if (parentPath !== null) {
|
|
path.parentPath = parentPath;
|
|
path.parent = path.parentPath.node;
|
|
}
|
|
|
|
if (prop !== null) {
|
|
path.property = prop;
|
|
}
|
|
|
|
if (index >= 0) {
|
|
path.index = index;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* Initializes the NodePath registry. The registry is a map from
|
|
* a node to its NodePath instance.
|
|
*/
|
|
|
|
}, {
|
|
key: 'initRegistry',
|
|
value: function initRegistry() {
|
|
if (!NodePath.registry) {
|
|
NodePath.registry = new Map();
|
|
}
|
|
NodePath.registry.clear();
|
|
}
|
|
|
|
/**
|
|
* Updates index of a currently traversing collection.
|
|
*/
|
|
|
|
}, {
|
|
key: 'updateTraversingIndex',
|
|
value: function updateTraversingIndex(dx) {
|
|
return NodePath.traversingIndexStack[NodePath.traversingIndexStack.length - 1] += dx;
|
|
}
|
|
|
|
/**
|
|
* Returns current traversing index.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getTraversingIndex',
|
|
value: function getTraversingIndex() {
|
|
return NodePath.traversingIndexStack[NodePath.traversingIndexStack.length - 1];
|
|
}
|
|
}]);
|
|
|
|
return NodePath;
|
|
}();
|
|
|
|
NodePath$3.initRegistry();
|
|
|
|
/**
|
|
* Index of a currently traversing collection is stored on top of the
|
|
* `NodePath.traversingIndexStack`. Remove/insert methods can adjust
|
|
* this index.
|
|
*/
|
|
NodePath$3.traversingIndexStack = [];
|
|
|
|
// Helper function used to skip `loc` in JSON operations.
|
|
function jsonSkipLoc(prop, value) {
|
|
if (prop === 'loc') {
|
|
return undefined;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
var nodePath = NodePath$3;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var NodePath$2 = nodePath;
|
|
|
|
/**
|
|
* Does an actual AST traversal, using visitor pattern,
|
|
* and calling set of callbacks.
|
|
*
|
|
* Based on https://github.com/olov/ast-traverse
|
|
*
|
|
* Expects AST in Mozilla Parser API: nodes which are supposed to be
|
|
* handled should have `type` property.
|
|
*
|
|
* @param Object root - a root node to start traversal from.
|
|
*
|
|
* @param Object options - an object with set of callbacks:
|
|
*
|
|
* - `pre(node, parent, prop, index)` - a hook called on node enter
|
|
* - `post`(node, parent, prop, index) - a hook called on node exit
|
|
* - `skipProperty(prop)` - a predicated whether a property should be skipped
|
|
*/
|
|
function astTraverse(root) {
|
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
|
|
var pre = options.pre;
|
|
var post = options.post;
|
|
var skipProperty = options.skipProperty;
|
|
|
|
function visit(node, parent, prop, idx) {
|
|
if (!node || typeof node.type !== 'string') {
|
|
return;
|
|
}
|
|
|
|
var res = undefined;
|
|
if (pre) {
|
|
res = pre(node, parent, prop, idx);
|
|
}
|
|
|
|
if (res !== false) {
|
|
|
|
// A node can be replaced during traversal, so we have to
|
|
// recalculate it from the parent, to avoid traversing "dead" nodes.
|
|
if (parent && parent[prop]) {
|
|
if (!isNaN(idx)) {
|
|
node = parent[prop][idx];
|
|
} else {
|
|
node = parent[prop];
|
|
}
|
|
}
|
|
|
|
for (var _prop in node) {
|
|
if (node.hasOwnProperty(_prop)) {
|
|
if (skipProperty ? skipProperty(_prop, node) : _prop[0] === '$') {
|
|
continue;
|
|
}
|
|
|
|
var child = node[_prop];
|
|
|
|
// Collection node.
|
|
//
|
|
// NOTE: a node (or several nodes) can be removed or inserted
|
|
// during traversal.
|
|
//
|
|
// Current traversing index is stored on top of the
|
|
// `NodePath.traversingIndexStack`. The stack is used to support
|
|
// recursive nature of the traversal.
|
|
//
|
|
// In this case `NodePath.traversingIndex` (which we use here) is
|
|
// updated in the NodePath remove/insert methods.
|
|
//
|
|
if (Array.isArray(child)) {
|
|
var index = 0;
|
|
NodePath$2.traversingIndexStack.push(index);
|
|
while (index < child.length) {
|
|
visit(child[index], node, _prop, index);
|
|
index = NodePath$2.updateTraversingIndex(+1);
|
|
}
|
|
NodePath$2.traversingIndexStack.pop();
|
|
}
|
|
|
|
// Simple node.
|
|
else {
|
|
visit(child, node, _prop);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (post) {
|
|
post(node, parent, prop, idx);
|
|
}
|
|
}
|
|
|
|
visit(root, null);
|
|
}
|
|
|
|
var traverse$1 = {
|
|
/**
|
|
* Traverses an AST.
|
|
*
|
|
* @param Object ast - an AST node
|
|
*
|
|
* @param Object | Array<Object> handlers:
|
|
*
|
|
* an object (or an array of objects)
|
|
*
|
|
* Each such object contains a handler function per node.
|
|
* In case of an array of handlers, they are applied in order.
|
|
* A handler may return a transformed node (or a different type).
|
|
*
|
|
* The per-node function may instead be an object with functions pre and post.
|
|
* pre is called before visiting the node, post after.
|
|
* If a handler is a function, it is treated as the pre function, with an empty post.
|
|
*
|
|
* @param Object options:
|
|
*
|
|
* a config object, specifying traversal options:
|
|
*
|
|
* `asNodes`: boolean - whether handlers should receives raw AST nodes
|
|
* (false by default), instead of a `NodePath` wrapper. Note, by default
|
|
* `NodePath` wrapper provides a set of convenient method to manipulate
|
|
* a traversing AST, and also has access to all parents list. A raw
|
|
* nodes traversal should be used in rare cases, when no `NodePath`
|
|
* features are needed.
|
|
*
|
|
* Special hooks:
|
|
*
|
|
* - `shouldRun(ast)` - a predicate determining whether the handler
|
|
* should be applied.
|
|
*
|
|
* NOTE: Multiple handlers are used as an optimization of applying all of
|
|
* them in one AST traversal pass.
|
|
*/
|
|
traverse: function traverse(ast, handlers) {
|
|
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { asNodes: false };
|
|
|
|
|
|
if (!Array.isArray(handlers)) {
|
|
handlers = [handlers];
|
|
}
|
|
|
|
// Filter out handlers by result of `shouldRun`, if the method is present.
|
|
handlers = handlers.filter(function (handler) {
|
|
if (typeof handler.shouldRun !== 'function') {
|
|
return true;
|
|
}
|
|
return handler.shouldRun(ast);
|
|
});
|
|
|
|
NodePath$2.initRegistry();
|
|
|
|
// Allow handlers to initializer themselves.
|
|
handlers.forEach(function (handler) {
|
|
if (typeof handler.init === 'function') {
|
|
handler.init(ast);
|
|
}
|
|
});
|
|
|
|
function getPathFor(node, parent, prop, index) {
|
|
var parentPath = NodePath$2.getForNode(parent);
|
|
var nodePath = NodePath$2.getForNode(node, parentPath, prop, index);
|
|
|
|
return nodePath;
|
|
}
|
|
|
|
// Handle actual nodes.
|
|
astTraverse(ast, {
|
|
/**
|
|
* Handler on node enter.
|
|
*/
|
|
pre: function pre(node, parent, prop, index) {
|
|
var nodePath = void 0;
|
|
if (!options.asNodes) {
|
|
nodePath = getPathFor(node, parent, prop, index);
|
|
}
|
|
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = handlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var handler = _step.value;
|
|
|
|
// "Catch-all" `*` handler.
|
|
if (typeof handler['*'] === 'function') {
|
|
if (nodePath) {
|
|
// A path/node can be removed by some previous handler.
|
|
if (!nodePath.isRemoved()) {
|
|
var handlerResult = handler['*'](nodePath);
|
|
// Explicitly stop traversal.
|
|
if (handlerResult === false) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
handler['*'](node, parent, prop, index);
|
|
}
|
|
}
|
|
|
|
// Per-node handler.
|
|
var handlerFuncPre = void 0;
|
|
if (typeof handler[node.type] === 'function') {
|
|
handlerFuncPre = handler[node.type];
|
|
} else if (typeof handler[node.type] === 'object' && typeof handler[node.type].pre === 'function') {
|
|
handlerFuncPre = handler[node.type].pre;
|
|
}
|
|
|
|
if (handlerFuncPre) {
|
|
if (nodePath) {
|
|
// A path/node can be removed by some previous handler.
|
|
if (!nodePath.isRemoved()) {
|
|
var _handlerResult = handlerFuncPre.call(handler, nodePath);
|
|
// Explicitly stop traversal.
|
|
if (_handlerResult === false) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
handlerFuncPre.call(handler, node, parent, prop, index);
|
|
}
|
|
}
|
|
} // Loop over handlers
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
// pre func
|
|
|
|
/**
|
|
* Handler on node exit.
|
|
*/
|
|
post: function post(node, parent, prop, index) {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
|
|
var nodePath = void 0;
|
|
if (!options.asNodes) {
|
|
nodePath = getPathFor(node, parent, prop, index);
|
|
}
|
|
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = handlers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var handler = _step2.value;
|
|
|
|
// Per-node handler.
|
|
var handlerFuncPost = void 0;
|
|
if (typeof handler[node.type] === 'object' && typeof handler[node.type].post === 'function') {
|
|
handlerFuncPost = handler[node.type].post;
|
|
}
|
|
|
|
if (handlerFuncPost) {
|
|
if (nodePath) {
|
|
// A path/node can be removed by some previous handler.
|
|
if (!nodePath.isRemoved()) {
|
|
var handlerResult = handlerFuncPost.call(handler, nodePath);
|
|
// Explicitly stop traversal.
|
|
if (handlerResult === false) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
handlerFuncPost.call(handler, node, parent, prop, index);
|
|
}
|
|
}
|
|
} // Loop over handlers
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
// post func
|
|
|
|
/**
|
|
* Skip locations by default.
|
|
*/
|
|
skipProperty: function skipProperty(prop) {
|
|
return prop === 'loc';
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var _createClass$5 = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _classCallCheck$5(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var generator$2 = generator_1;
|
|
var parser$3 = parser$4;
|
|
var traverse = traverse$1;
|
|
|
|
/**
|
|
* Transform result.
|
|
*/
|
|
|
|
var TransformResult = function () {
|
|
/**
|
|
* Initializes a transform result for an AST.
|
|
*
|
|
* @param Object ast - an AST node
|
|
* @param mixed extra - any extra data a transform may return
|
|
*/
|
|
function TransformResult(ast) {
|
|
var extra = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
|
|
_classCallCheck$5(this, TransformResult);
|
|
|
|
this._ast = ast;
|
|
this._source = null;
|
|
this._string = null;
|
|
this._regexp = null;
|
|
this._extra = extra;
|
|
}
|
|
|
|
_createClass$5(TransformResult, [{
|
|
key: 'getAST',
|
|
value: function getAST() {
|
|
return this._ast;
|
|
}
|
|
}, {
|
|
key: 'setExtra',
|
|
value: function setExtra(extra) {
|
|
this._extra = extra;
|
|
}
|
|
}, {
|
|
key: 'getExtra',
|
|
value: function getExtra() {
|
|
return this._extra;
|
|
}
|
|
}, {
|
|
key: 'toRegExp',
|
|
value: function toRegExp() {
|
|
if (!this._regexp) {
|
|
this._regexp = new RegExp(this.getSource(), this._ast.flags);
|
|
}
|
|
return this._regexp;
|
|
}
|
|
}, {
|
|
key: 'getSource',
|
|
value: function getSource() {
|
|
if (!this._source) {
|
|
this._source = generator$2.generate(this._ast.body);
|
|
}
|
|
return this._source;
|
|
}
|
|
}, {
|
|
key: 'getFlags',
|
|
value: function getFlags() {
|
|
return this._ast.flags;
|
|
}
|
|
}, {
|
|
key: 'toString',
|
|
value: function toString() {
|
|
if (!this._string) {
|
|
this._string = generator$2.generate(this._ast);
|
|
}
|
|
return this._string;
|
|
}
|
|
}]);
|
|
|
|
return TransformResult;
|
|
}();
|
|
|
|
var transform$1 = {
|
|
/**
|
|
* Expose `TransformResult`.
|
|
*/
|
|
TransformResult: TransformResult,
|
|
|
|
/**
|
|
* Transforms a regular expression applying a set of
|
|
* transformation handlers.
|
|
*
|
|
* @param string | AST | RegExp:
|
|
*
|
|
* a regular expression in different representations: a string,
|
|
* a RegExp object, or an AST.
|
|
*
|
|
* @param Object | Array<Object>:
|
|
*
|
|
* a handler (or a list of handlers) from `traverse` API.
|
|
*
|
|
* @return TransformResult instance.
|
|
*
|
|
* Example:
|
|
*
|
|
* transform(/[a-z]/i, {
|
|
* onChar(path) {
|
|
* const {node} = path;
|
|
*
|
|
* if (...) {
|
|
* path.remove();
|
|
* }
|
|
* }
|
|
* });
|
|
*/
|
|
transform: function transform(regexp, handlers) {
|
|
var ast = regexp;
|
|
|
|
if (regexp instanceof RegExp) {
|
|
regexp = '' + regexp;
|
|
}
|
|
|
|
if (typeof regexp === 'string') {
|
|
ast = parser$3.parse(regexp, {
|
|
captureLocations: true
|
|
});
|
|
}
|
|
|
|
traverse.traverse(ast, handlers);
|
|
|
|
return new TransformResult(ast);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var compatTransforms = transforms$1;
|
|
var _transform$1 = transform$1;
|
|
|
|
var compatTranspiler$1 = {
|
|
/**
|
|
* Translates a regexp in new syntax to equivalent regexp in old syntax.
|
|
*
|
|
* @param string|RegExp|AST - regexp
|
|
* @param Array transformsWhitelist - names of the transforms to apply
|
|
*/
|
|
transform: function transform(regexp) {
|
|
var transformsWhitelist = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
|
|
var transformToApply = transformsWhitelist.length > 0 ? transformsWhitelist : Object.keys(compatTransforms);
|
|
|
|
var result = void 0;
|
|
|
|
// Collect extra data per transform.
|
|
var extra = {};
|
|
|
|
transformToApply.forEach(function (transformName) {
|
|
|
|
if (!compatTransforms.hasOwnProperty(transformName)) {
|
|
throw new Error('Unknown compat-transform: ' + transformName + '. ' + 'Available transforms are: ' + Object.keys(compatTransforms).join(', '));
|
|
}
|
|
|
|
var handler = compatTransforms[transformName];
|
|
|
|
result = _transform$1.transform(regexp, handler);
|
|
regexp = result.getAST();
|
|
|
|
// Collect `extra` transform result.
|
|
if (typeof handler.getExtra === 'function') {
|
|
extra[transformName] = handler.getExtra();
|
|
}
|
|
});
|
|
|
|
// Set the final extras for all transforms.
|
|
result.setExtra(extra);
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* Performs a deep copy of an simple object.
|
|
* Only handles scalar values, arrays and objects.
|
|
*
|
|
* @param obj Object
|
|
*/
|
|
|
|
var clone$1 = function clone(obj) {
|
|
if (obj === null || typeof obj !== 'object') {
|
|
return obj;
|
|
}
|
|
var res = void 0;
|
|
if (Array.isArray(obj)) {
|
|
res = [];
|
|
} else {
|
|
res = {};
|
|
}
|
|
for (var i in obj) {
|
|
res[i] = clone(obj[i]);
|
|
}
|
|
return res;
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to transform surrogate pairs into single unicode code point
|
|
*
|
|
* \ud83d\ude80 -> \u{1f680}
|
|
*/
|
|
|
|
var charSurrogatePairToSingleUnicodeTransform = {
|
|
shouldRun: function shouldRun(ast) {
|
|
return ast.flags.includes('u');
|
|
},
|
|
Char: function Char(path) {
|
|
var node = path.node;
|
|
|
|
if (node.kind !== 'unicode' || !node.isSurrogatePair || isNaN(node.codePoint)) {
|
|
return;
|
|
}
|
|
node.value = '\\u{' + node.codePoint.toString(16) + '}';
|
|
delete node.isSurrogatePair;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var UPPER_A_CP$1 = 'A'.codePointAt(0);
|
|
var UPPER_Z_CP$1 = 'Z'.codePointAt(0);
|
|
var LOWER_A_CP = 'a'.codePointAt(0);
|
|
var LOWER_Z_CP = 'z'.codePointAt(0);
|
|
var DIGIT_0_CP = '0'.codePointAt(0);
|
|
var DIGIT_9_CP = '9'.codePointAt(0);
|
|
|
|
/**
|
|
* A regexp-tree plugin to transform coded chars into simple chars.
|
|
*
|
|
* \u0061 -> a
|
|
*/
|
|
var charCodeToSimpleCharTransform = {
|
|
Char: function Char(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
if (isNaN(node.codePoint) || node.kind === 'simple') {
|
|
return;
|
|
}
|
|
|
|
if (parent.type === 'ClassRange') {
|
|
if (!isSimpleRange(parent)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!isPrintableASCIIChar(node.codePoint)) {
|
|
return;
|
|
}
|
|
|
|
var symbol = String.fromCodePoint(node.codePoint);
|
|
var newChar = {
|
|
type: 'Char',
|
|
kind: 'simple',
|
|
value: symbol,
|
|
symbol: symbol,
|
|
codePoint: node.codePoint
|
|
};
|
|
if (needsEscape(symbol, parent.type)) {
|
|
newChar.escaped = true;
|
|
}
|
|
path.replace(newChar);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Checks if a range is included either in 0-9, a-z or A-Z
|
|
* @param classRange
|
|
* @returns {boolean}
|
|
*/
|
|
function isSimpleRange(classRange) {
|
|
var from = classRange.from,
|
|
to = classRange.to;
|
|
|
|
return from.codePoint >= DIGIT_0_CP && from.codePoint <= DIGIT_9_CP && to.codePoint >= DIGIT_0_CP && to.codePoint <= DIGIT_9_CP || from.codePoint >= UPPER_A_CP$1 && from.codePoint <= UPPER_Z_CP$1 && to.codePoint >= UPPER_A_CP$1 && to.codePoint <= UPPER_Z_CP$1 || from.codePoint >= LOWER_A_CP && from.codePoint <= LOWER_Z_CP && to.codePoint >= LOWER_A_CP && to.codePoint <= LOWER_Z_CP;
|
|
}
|
|
|
|
/**
|
|
* Checks if a code point in the range of printable ASCII chars
|
|
* (DEL char excluded)
|
|
* @param codePoint
|
|
* @returns {boolean}
|
|
*/
|
|
function isPrintableASCIIChar(codePoint) {
|
|
return codePoint >= 0x20 && codePoint <= 0x7e;
|
|
}
|
|
|
|
function needsEscape(symbol, parentType) {
|
|
if (parentType === 'ClassRange' || parentType === 'CharacterClass') {
|
|
return (/[\]\\^-]/.test(symbol)
|
|
);
|
|
}
|
|
|
|
return (/[*[()+?^$./\\|{}]/.test(symbol)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var UPPER_A_CP = 'A'.codePointAt(0);
|
|
var UPPER_Z_CP = 'Z'.codePointAt(0);
|
|
/**
|
|
* Transforms case-insensitive regexp to lowercase
|
|
*
|
|
* /AaBbÏ/i -> /aabbï/i
|
|
*/
|
|
var charCaseInsensitiveLowercaseTransform = {
|
|
_AZClassRanges: null,
|
|
_hasUFlag: false,
|
|
init: function init(ast) {
|
|
this._AZClassRanges = new Set();
|
|
this._hasUFlag = ast.flags.includes('u');
|
|
},
|
|
shouldRun: function shouldRun(ast) {
|
|
return ast.flags.includes('i');
|
|
},
|
|
Char: function Char(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
if (isNaN(node.codePoint)) {
|
|
return;
|
|
}
|
|
|
|
// Engine support for case-insensitive matching without the u flag
|
|
// for characters above \u1000 does not seem reliable.
|
|
if (!this._hasUFlag && node.codePoint >= 0x1000) {
|
|
return;
|
|
}
|
|
|
|
if (parent.type === 'ClassRange') {
|
|
// The only class ranges we handle must be inside A-Z.
|
|
// After the `from` char is processed, the isAZClassRange test
|
|
// will be false, so we use a Set to keep track of parents and
|
|
// process the `to` char.
|
|
if (!this._AZClassRanges.has(parent) && !isAZClassRange(parent)) {
|
|
return;
|
|
}
|
|
this._AZClassRanges.add(parent);
|
|
}
|
|
|
|
var lower = node.symbol.toLowerCase();
|
|
if (lower !== node.symbol) {
|
|
node.value = displaySymbolAsValue(lower, node);
|
|
node.symbol = lower;
|
|
node.codePoint = lower.codePointAt(0);
|
|
}
|
|
}
|
|
};
|
|
|
|
function isAZClassRange(classRange) {
|
|
var from = classRange.from,
|
|
to = classRange.to;
|
|
// A-Z
|
|
|
|
return from.codePoint >= UPPER_A_CP && from.codePoint <= UPPER_Z_CP && to.codePoint >= UPPER_A_CP && to.codePoint <= UPPER_Z_CP;
|
|
}
|
|
|
|
function displaySymbolAsValue(symbol, node) {
|
|
var codePoint = symbol.codePointAt(0);
|
|
if (node.kind === 'decimal') {
|
|
return '\\' + codePoint;
|
|
}
|
|
if (node.kind === 'oct') {
|
|
return '\\0' + codePoint.toString(8);
|
|
}
|
|
if (node.kind === 'hex') {
|
|
return '\\x' + codePoint.toString(16);
|
|
}
|
|
if (node.kind === 'unicode') {
|
|
if (node.isSurrogatePair) {
|
|
var _getSurrogatePairFrom = getSurrogatePairFromCodePoint(codePoint),
|
|
lead = _getSurrogatePairFrom.lead,
|
|
trail = _getSurrogatePairFrom.trail;
|
|
|
|
return '\\u' + '0'.repeat(4 - lead.length) + lead + '\\u' + '0'.repeat(4 - trail.length) + trail;
|
|
} else if (node.value.includes('{')) {
|
|
return '\\u{' + codePoint.toString(16) + '}';
|
|
} else {
|
|
var code = codePoint.toString(16);
|
|
return '\\u' + '0'.repeat(4 - code.length) + code;
|
|
}
|
|
}
|
|
// simple
|
|
return symbol;
|
|
}
|
|
|
|
/**
|
|
* Converts a code point to a surrogate pair.
|
|
* Conversion algorithm is taken from The Unicode Standard 3.0 Section 3.7
|
|
* (https://www.unicode.org/versions/Unicode3.0.0/ch03.pdf)
|
|
* @param {number} codePoint - Between 0x10000 and 0x10ffff
|
|
* @returns {{lead: string, trail: string}}
|
|
*/
|
|
function getSurrogatePairFromCodePoint(codePoint) {
|
|
var lead = Math.floor((codePoint - 0x10000) / 0x400) + 0xd800;
|
|
var trail = (codePoint - 0x10000) % 0x400 + 0xdc00;
|
|
return {
|
|
lead: lead.toString(16),
|
|
trail: trail.toString(16)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to remove duplicates from character classes.
|
|
*/
|
|
|
|
var charClassRemoveDuplicatesTransform = {
|
|
CharacterClass: function CharacterClass(path) {
|
|
var node = path.node;
|
|
|
|
var sources = {};
|
|
|
|
for (var i = 0; i < node.expressions.length; i++) {
|
|
var childPath = path.getChild(i);
|
|
var source = childPath.jsonEncode();
|
|
|
|
if (sources.hasOwnProperty(source)) {
|
|
childPath.remove();
|
|
|
|
// Since we remove the current node.
|
|
// TODO: make it simpler for users with a method.
|
|
i--;
|
|
}
|
|
|
|
sources[source] = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* Flattens a nested disjunction node to a list.
|
|
*
|
|
* /a|b|c|d/
|
|
*
|
|
* {{{a, b}, c}, d} -> [a, b, c, d]
|
|
*/
|
|
|
|
function _toConsumableArray$7(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
function disjunctionToList$1(node) {
|
|
if (node.type !== 'Disjunction') {
|
|
throw new TypeError('Expected "Disjunction" node, got "' + node.type + '"');
|
|
}
|
|
|
|
var list = [];
|
|
|
|
if (node.left && node.left.type === 'Disjunction') {
|
|
list.push.apply(list, _toConsumableArray$7(disjunctionToList$1(node.left)).concat([node.right]));
|
|
} else {
|
|
list.push(node.left, node.right);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Builds a nested disjunction node from a list.
|
|
*
|
|
* /a|b|c|d/
|
|
*
|
|
* [a, b, c, d] -> {{{a, b}, c}, d}
|
|
*/
|
|
function listToDisjunction$1(list) {
|
|
return list.reduce(function (left, right) {
|
|
return {
|
|
type: 'Disjunction',
|
|
left: left,
|
|
right: right
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Increases a quantifier by one.
|
|
* Does not change greediness.
|
|
* * -> +
|
|
* + -> {2,}
|
|
* ? -> {1,2}
|
|
* {2} -> {3}
|
|
* {2,} -> {3,}
|
|
* {2,3} -> {3,4}
|
|
*/
|
|
function increaseQuantifierByOne$2(quantifier) {
|
|
if (quantifier.kind === '*') {
|
|
|
|
quantifier.kind = '+';
|
|
} else if (quantifier.kind === '+') {
|
|
|
|
quantifier.kind = 'Range';
|
|
quantifier.from = 2;
|
|
delete quantifier.to;
|
|
} else if (quantifier.kind === '?') {
|
|
|
|
quantifier.kind = 'Range';
|
|
quantifier.from = 1;
|
|
quantifier.to = 2;
|
|
} else if (quantifier.kind === 'Range') {
|
|
|
|
quantifier.from += 1;
|
|
if (quantifier.to) {
|
|
quantifier.to += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
var utils = {
|
|
disjunctionToList: disjunctionToList$1,
|
|
listToDisjunction: listToDisjunction$1,
|
|
increaseQuantifierByOne: increaseQuantifierByOne$2
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var _require$8 = utils,
|
|
increaseQuantifierByOne$1 = _require$8.increaseQuantifierByOne;
|
|
|
|
/**
|
|
* A regexp-tree plugin to merge quantifiers
|
|
*
|
|
* a+a+ -> a{2,}
|
|
* a{2}a{3} -> a{5}
|
|
* a{1,2}a{2,3} -> a{3,5}
|
|
*/
|
|
|
|
|
|
var quantifiersMergeTransform = {
|
|
Repetition: function Repetition(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
|
|
if (parent.type !== 'Alternative' || !path.index) {
|
|
return;
|
|
}
|
|
|
|
var previousSibling = path.getPreviousSibling();
|
|
|
|
if (!previousSibling) {
|
|
return;
|
|
}
|
|
|
|
if (previousSibling.node.type === 'Repetition') {
|
|
if (!previousSibling.getChild().hasEqualSource(path.getChild())) {
|
|
return;
|
|
}
|
|
|
|
var _extractFromTo = extractFromTo(previousSibling.node.quantifier),
|
|
previousSiblingFrom = _extractFromTo.from,
|
|
previousSiblingTo = _extractFromTo.to;
|
|
|
|
var _extractFromTo2 = extractFromTo(node.quantifier),
|
|
nodeFrom = _extractFromTo2.from,
|
|
nodeTo = _extractFromTo2.to;
|
|
|
|
// It's does not seem reliable to merge quantifiers with different greediness
|
|
// when none of both is a greedy open range
|
|
|
|
|
|
if (previousSibling.node.quantifier.greedy !== node.quantifier.greedy && !isGreedyOpenRange(previousSibling.node.quantifier) && !isGreedyOpenRange(node.quantifier)) {
|
|
return;
|
|
}
|
|
|
|
// a*a* -> a*
|
|
// a*a+ -> a+
|
|
// a+a+ -> a{2,}
|
|
// a{2}a{4} -> a{6}
|
|
// a{1,2}a{2,3} -> a{3,5}
|
|
// a{1,}a{2,} -> a{3,}
|
|
// a+a{2,} -> a{3,}
|
|
|
|
// a??a{2,} -> a{2,}
|
|
// a*?a{2,} -> a{2,}
|
|
// a+?a{2,} -> a{3,}
|
|
|
|
node.quantifier.kind = 'Range';
|
|
node.quantifier.from = previousSiblingFrom + nodeFrom;
|
|
if (previousSiblingTo && nodeTo) {
|
|
node.quantifier.to = previousSiblingTo + nodeTo;
|
|
} else {
|
|
delete node.quantifier.to;
|
|
}
|
|
if (isGreedyOpenRange(previousSibling.node.quantifier) || isGreedyOpenRange(node.quantifier)) {
|
|
node.quantifier.greedy = true;
|
|
}
|
|
|
|
previousSibling.remove();
|
|
} else {
|
|
if (!previousSibling.hasEqualSource(path.getChild())) {
|
|
return;
|
|
}
|
|
|
|
increaseQuantifierByOne$1(node.quantifier);
|
|
previousSibling.remove();
|
|
}
|
|
}
|
|
};
|
|
|
|
function isGreedyOpenRange(quantifier) {
|
|
return quantifier.greedy && (quantifier.kind === '+' || quantifier.kind === '*' || quantifier.kind === 'Range' && !quantifier.to);
|
|
}
|
|
|
|
function extractFromTo(quantifier) {
|
|
var from = void 0,
|
|
to = void 0;
|
|
if (quantifier.kind === '*') {
|
|
from = 0;
|
|
} else if (quantifier.kind === '+') {
|
|
from = 1;
|
|
} else if (quantifier.kind === '?') {
|
|
from = 0;
|
|
to = 1;
|
|
} else {
|
|
from = quantifier.from;
|
|
if (quantifier.to) {
|
|
to = quantifier.to;
|
|
}
|
|
}
|
|
return { from: from, to: to };
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to replace different range-based quantifiers
|
|
* with their symbol equivalents.
|
|
*
|
|
* a{0,} -> a*
|
|
* a{1,} -> a+
|
|
* a{1} -> a
|
|
*
|
|
* NOTE: the following is automatically handled in the generator:
|
|
*
|
|
* a{3,3} -> a{3}
|
|
*/
|
|
|
|
var quantifierRangeToSymbolTransform = {
|
|
Quantifier: function Quantifier(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.kind !== 'Range') {
|
|
return;
|
|
}
|
|
|
|
// a{0,} -> a*
|
|
rewriteOpenZero(path);
|
|
|
|
// a{1,} -> a+
|
|
rewriteOpenOne(path);
|
|
|
|
// a{1} -> a
|
|
rewriteExactOne(path);
|
|
}
|
|
};
|
|
|
|
function rewriteOpenZero(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.from !== 0 || node.to) {
|
|
return;
|
|
}
|
|
|
|
node.kind = '*';
|
|
delete node.from;
|
|
}
|
|
|
|
function rewriteOpenOne(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.from !== 1 || node.to) {
|
|
return;
|
|
}
|
|
|
|
node.kind = '+';
|
|
delete node.from;
|
|
}
|
|
|
|
function rewriteExactOne(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.from !== 1 || node.to !== 1) {
|
|
return;
|
|
}
|
|
|
|
path.parentPath.replace(path.parentPath.node.expression);
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to simplify character classes
|
|
* spanning only one or two chars.
|
|
*
|
|
* [a-a] -> [a]
|
|
* [a-b] -> [ab]
|
|
*/
|
|
|
|
var charClassClassrangesToCharsTransform = {
|
|
ClassRange: function ClassRange(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.from.codePoint === node.to.codePoint) {
|
|
|
|
path.replace(node.from);
|
|
} else if (node.from.codePoint === node.to.codePoint - 1) {
|
|
|
|
path.getParent().insertChildAt(node.to, path.index + 1);
|
|
path.replace(node.from);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to replace standard character classes with
|
|
* their meta symbols equivalents.
|
|
*/
|
|
|
|
function _toConsumableArray$6(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
var charClassToMetaTransform = {
|
|
_hasIFlag: false,
|
|
_hasUFlag: false,
|
|
init: function init(ast) {
|
|
this._hasIFlag = ast.flags.includes('i');
|
|
this._hasUFlag = ast.flags.includes('u');
|
|
},
|
|
CharacterClass: function CharacterClass(path) {
|
|
// [0-9] -> \d
|
|
rewriteNumberRanges(path);
|
|
|
|
// [a-zA-Z_0-9] -> \w
|
|
rewriteWordRanges(path, this._hasIFlag, this._hasUFlag);
|
|
|
|
// [ \f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] -> \s
|
|
rewriteWhitespaceRanges(path);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Rewrites number ranges: [0-9] -> \d
|
|
*/
|
|
function rewriteNumberRanges(path) {
|
|
var node = path.node;
|
|
|
|
|
|
node.expressions.forEach(function (expression, i) {
|
|
if (isFullNumberRange(expression)) {
|
|
path.getChild(i).replace({
|
|
type: 'Char',
|
|
value: '\\d',
|
|
kind: 'meta'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Rewrites word ranges: [a-zA-Z_0-9] -> \w
|
|
* Thus, the ranges may go in any order, and other symbols/ranges
|
|
* are kept untouched, e.g. [a-z_\dA-Z$] -> [\w$]
|
|
*/
|
|
function rewriteWordRanges(path, hasIFlag, hasUFlag) {
|
|
var node = path.node;
|
|
|
|
|
|
var numberPath = null;
|
|
var lowerCasePath = null;
|
|
var upperCasePath = null;
|
|
var underscorePath = null;
|
|
var u017fPath = null;
|
|
var u212aPath = null;
|
|
|
|
node.expressions.forEach(function (expression, i) {
|
|
// \d
|
|
if (isMetaChar(expression, '\\d')) {
|
|
numberPath = path.getChild(i);
|
|
}
|
|
|
|
// a-z
|
|
else if (isLowerCaseRange(expression)) {
|
|
lowerCasePath = path.getChild(i);
|
|
}
|
|
|
|
// A-Z
|
|
else if (isUpperCaseRange(expression)) {
|
|
upperCasePath = path.getChild(i);
|
|
}
|
|
|
|
// _
|
|
else if (isUnderscore(expression)) {
|
|
underscorePath = path.getChild(i);
|
|
} else if (hasIFlag && hasUFlag && isCodePoint(expression, 0x017f)) {
|
|
u017fPath = path.getChild(i);
|
|
} else if (hasIFlag && hasUFlag && isCodePoint(expression, 0x212a)) {
|
|
u212aPath = path.getChild(i);
|
|
}
|
|
});
|
|
|
|
// If we found the whole pattern, replace it.
|
|
if (numberPath && (lowerCasePath && upperCasePath || hasIFlag && (lowerCasePath || upperCasePath)) && underscorePath && (!hasUFlag || !hasIFlag || u017fPath && u212aPath)) {
|
|
// Put \w in place of \d.
|
|
numberPath.replace({
|
|
type: 'Char',
|
|
value: '\\w',
|
|
kind: 'meta'
|
|
});
|
|
|
|
// Other paths are removed.
|
|
if (lowerCasePath) {
|
|
lowerCasePath.remove();
|
|
}
|
|
if (upperCasePath) {
|
|
upperCasePath.remove();
|
|
}
|
|
underscorePath.remove();
|
|
if (u017fPath) {
|
|
u017fPath.remove();
|
|
}
|
|
if (u212aPath) {
|
|
u212aPath.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rewrites whitespace ranges: [ \f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] -> \s.
|
|
*/
|
|
var whitespaceRangeTests = [function (node) {
|
|
return isChar(node, ' ');
|
|
}].concat(_toConsumableArray$6(['\\f', '\\n', '\\r', '\\t', '\\v'].map(function (char) {
|
|
return function (node) {
|
|
return isMetaChar(node, char);
|
|
};
|
|
})), _toConsumableArray$6([0x00a0, 0x1680, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, 0xfeff].map(function (codePoint) {
|
|
return function (node) {
|
|
return isCodePoint(node, codePoint);
|
|
};
|
|
})), [function (node) {
|
|
return node.type === 'ClassRange' && isCodePoint(node.from, 0x2000) && isCodePoint(node.to, 0x200a);
|
|
}]);
|
|
|
|
function rewriteWhitespaceRanges(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.expressions.length < whitespaceRangeTests.length || !whitespaceRangeTests.every(function (test) {
|
|
return node.expressions.some(function (expression) {
|
|
return test(expression);
|
|
});
|
|
})) {
|
|
return;
|
|
}
|
|
|
|
// If we found the whole pattern, replace it.
|
|
|
|
// Put \s in place of \n.
|
|
var nNode = node.expressions.find(function (expression) {
|
|
return isMetaChar(expression, '\\n');
|
|
});
|
|
nNode.value = '\\s';
|
|
nNode.symbol = undefined;
|
|
nNode.codePoint = NaN;
|
|
|
|
// Other paths are removed.
|
|
node.expressions.map(function (expression, i) {
|
|
return whitespaceRangeTests.some(function (test) {
|
|
return test(expression);
|
|
}) ? path.getChild(i) : undefined;
|
|
}).filter(Boolean).forEach(function (path) {
|
|
return path.remove();
|
|
});
|
|
}
|
|
|
|
function isFullNumberRange(node) {
|
|
return node.type === 'ClassRange' && node.from.value === '0' && node.to.value === '9';
|
|
}
|
|
|
|
function isChar(node, value) {
|
|
var kind = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'simple';
|
|
|
|
return node.type === 'Char' && node.value === value && node.kind === kind;
|
|
}
|
|
|
|
function isMetaChar(node, value) {
|
|
return isChar(node, value, 'meta');
|
|
}
|
|
|
|
function isLowerCaseRange(node) {
|
|
return node.type === 'ClassRange' && node.from.value === 'a' && node.to.value === 'z';
|
|
}
|
|
|
|
function isUpperCaseRange(node) {
|
|
return node.type === 'ClassRange' && node.from.value === 'A' && node.to.value === 'Z';
|
|
}
|
|
|
|
function isUnderscore(node) {
|
|
return node.type === 'Char' && node.value === '_' && node.kind === 'simple';
|
|
}
|
|
|
|
function isCodePoint(node, codePoint) {
|
|
return node.type === 'Char' && node.kind === 'unicode' && node.codePoint === codePoint;
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to replace single char character classes with
|
|
* just that character.
|
|
*
|
|
* [\d] -> \d, [^\w] -> \W
|
|
*/
|
|
|
|
var charClassToSingleCharTransform = {
|
|
CharacterClass: function CharacterClass(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.expressions.length !== 1 || !hasAppropriateSiblings$1(path) || !isAppropriateChar(node.expressions[0])) {
|
|
return;
|
|
}
|
|
|
|
var _node$expressions$ = node.expressions[0],
|
|
value = _node$expressions$.value,
|
|
kind = _node$expressions$.kind,
|
|
escaped = _node$expressions$.escaped;
|
|
|
|
|
|
if (node.negative) {
|
|
// For negative can extract only meta chars like [^\w] -> \W
|
|
// cannot do for [^a] -> a (wrong).
|
|
if (!isMeta$1(value)) {
|
|
return;
|
|
}
|
|
|
|
value = getInverseMeta(value);
|
|
}
|
|
|
|
path.replace({
|
|
type: 'Char',
|
|
value: value,
|
|
kind: kind,
|
|
escaped: escaped || shouldEscape(value)
|
|
});
|
|
}
|
|
};
|
|
|
|
function isAppropriateChar(node) {
|
|
return node.type === 'Char' &&
|
|
// We don't extract [\b] (backspace) since \b has different
|
|
// semantics (word boundary).
|
|
node.value !== '\\b';
|
|
}
|
|
|
|
function isMeta$1(value) {
|
|
return (/^\\[dwsDWS]$/.test(value)
|
|
);
|
|
}
|
|
|
|
function getInverseMeta(value) {
|
|
return (/[dws]/.test(value) ? value.toUpperCase() : value.toLowerCase()
|
|
);
|
|
}
|
|
|
|
function hasAppropriateSiblings$1(path) {
|
|
var parent = path.parent,
|
|
index = path.index;
|
|
|
|
|
|
if (parent.type !== 'Alternative') {
|
|
return true;
|
|
}
|
|
|
|
var previousNode = parent.expressions[index - 1];
|
|
if (previousNode == null) {
|
|
return true;
|
|
}
|
|
|
|
// Don't optimized \1[0] to \10
|
|
if (previousNode.type === 'Backreference' && previousNode.kind === 'number') {
|
|
return false;
|
|
}
|
|
|
|
// Don't optimized \2[0] to \20
|
|
if (previousNode.type === 'Char' && previousNode.kind === 'decimal') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Note: \{ and \} are always preserved to avoid `a[{]2[}]` turning
|
|
// into `a{2}`.
|
|
function shouldEscape(value) {
|
|
return (/[*[()+?$./{}|]/.test(value)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to remove unnecessary escape.
|
|
*
|
|
* \e -> e
|
|
*
|
|
* [\(] -> [(]
|
|
*/
|
|
|
|
var charEscapeUnescapeTransform = {
|
|
_hasXFlag: false,
|
|
init: function init(ast) {
|
|
this._hasXFlag = ast.flags.includes('x');
|
|
},
|
|
Char: function Char(path) {
|
|
var node = path.node;
|
|
|
|
|
|
if (!node.escaped) {
|
|
return;
|
|
}
|
|
|
|
if (shouldUnescape(path, this._hasXFlag)) {
|
|
delete node.escaped;
|
|
}
|
|
}
|
|
};
|
|
|
|
function shouldUnescape(path, hasXFlag) {
|
|
var value = path.node.value,
|
|
index = path.index,
|
|
parent = path.parent;
|
|
|
|
// In char class (, etc are allowed.
|
|
|
|
if (parent.type !== 'CharacterClass' && parent.type !== 'ClassRange') {
|
|
return !preservesEscape(value, index, parent, hasXFlag);
|
|
}
|
|
|
|
return !preservesInCharClass(value, index, parent);
|
|
}
|
|
|
|
/**
|
|
* \], \\, \^, \-
|
|
*/
|
|
function preservesInCharClass(value, index, parent) {
|
|
if (value === '^') {
|
|
// Avoid [\^a] turning into [^a]
|
|
return index === 0 && !parent.negative;
|
|
}
|
|
if (value === '-') {
|
|
// Avoid [a\-z] turning into [a-z]
|
|
return true;
|
|
}
|
|
return (/[\]\\]/.test(value)
|
|
);
|
|
}
|
|
|
|
function preservesEscape(value, index, parent, hasXFlag) {
|
|
if (value === '{') {
|
|
return preservesOpeningCurlyBraceEscape(index, parent);
|
|
}
|
|
|
|
if (value === '}') {
|
|
return preservesClosingCurlyBraceEscape(index, parent);
|
|
}
|
|
|
|
if (hasXFlag && /[ #]/.test(value)) {
|
|
return true;
|
|
}
|
|
|
|
return (/[*[()+?^$./\\|]/.test(value)
|
|
);
|
|
}
|
|
|
|
function consumeNumbers(startIndex, parent, rtl) {
|
|
var i = startIndex;
|
|
var siblingNode = (rtl ? i >= 0 : i < parent.expressions.length) && parent.expressions[i];
|
|
|
|
while (siblingNode && siblingNode.type === 'Char' && siblingNode.kind === 'simple' && !siblingNode.escaped && /\d/.test(siblingNode.value)) {
|
|
rtl ? i-- : i++;
|
|
siblingNode = (rtl ? i >= 0 : i < parent.expressions.length) && parent.expressions[i];
|
|
}
|
|
|
|
return Math.abs(startIndex - i);
|
|
}
|
|
|
|
function isSimpleChar(node, value) {
|
|
return node && node.type === 'Char' && node.kind === 'simple' && !node.escaped && node.value === value;
|
|
}
|
|
|
|
function preservesOpeningCurlyBraceEscape(index, parent) {
|
|
// (?:\{) -> (?:{)
|
|
if (index == null) {
|
|
return false;
|
|
}
|
|
|
|
var nbFollowingNumbers = consumeNumbers(index + 1, parent);
|
|
var i = index + nbFollowingNumbers + 1;
|
|
var nextSiblingNode = i < parent.expressions.length && parent.expressions[i];
|
|
|
|
if (nbFollowingNumbers) {
|
|
// Avoid \{3} turning into {3}
|
|
if (isSimpleChar(nextSiblingNode, '}')) {
|
|
return true;
|
|
}
|
|
|
|
if (isSimpleChar(nextSiblingNode, ',')) {
|
|
nbFollowingNumbers = consumeNumbers(i + 1, parent);
|
|
i = i + nbFollowingNumbers + 1;
|
|
nextSiblingNode = i < parent.expressions.length && parent.expressions[i];
|
|
|
|
// Avoid \{3,} turning into {3,}
|
|
return isSimpleChar(nextSiblingNode, '}');
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function preservesClosingCurlyBraceEscape(index, parent) {
|
|
// (?:\{) -> (?:{)
|
|
if (index == null) {
|
|
return false;
|
|
}
|
|
|
|
var nbPrecedingNumbers = consumeNumbers(index - 1, parent, true);
|
|
var i = index - nbPrecedingNumbers - 1;
|
|
var previousSiblingNode = i >= 0 && parent.expressions[i];
|
|
|
|
// Avoid {3\} turning into {3}
|
|
if (nbPrecedingNumbers && isSimpleChar(previousSiblingNode, '{')) {
|
|
return true;
|
|
}
|
|
|
|
if (isSimpleChar(previousSiblingNode, ',')) {
|
|
nbPrecedingNumbers = consumeNumbers(i - 1, parent, true);
|
|
i = i - nbPrecedingNumbers - 1;
|
|
previousSiblingNode = i < parent.expressions.length && parent.expressions[i];
|
|
|
|
// Avoid {3,\} turning into {3,}
|
|
return nbPrecedingNumbers && isSimpleChar(previousSiblingNode, '{');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to merge class ranges.
|
|
*
|
|
* [a-ec] -> [a-e]
|
|
* [a-ec-e] -> [a-e]
|
|
* [\w\da-f] -> [\w]
|
|
* [abcdef] -> [a-f]
|
|
*/
|
|
|
|
var charClassClassrangesMergeTransform = {
|
|
_hasIUFlags: false,
|
|
init: function init(ast) {
|
|
this._hasIUFlags = ast.flags.includes('i') && ast.flags.includes('u');
|
|
},
|
|
CharacterClass: function CharacterClass(path) {
|
|
var node = path.node;
|
|
|
|
var expressions = node.expressions;
|
|
|
|
var metas = [];
|
|
// Extract metas
|
|
expressions.forEach(function (expression) {
|
|
if (isMeta(expression)) {
|
|
metas.push(expression.value);
|
|
}
|
|
});
|
|
|
|
expressions.sort(sortCharClass);
|
|
|
|
for (var i = 0; i < expressions.length; i++) {
|
|
var expression = expressions[i];
|
|
if (fitsInMetas(expression, metas, this._hasIUFlags) || combinesWithPrecedingClassRange(expression, expressions[i - 1]) || combinesWithFollowingClassRange(expression, expressions[i + 1])) {
|
|
expressions.splice(i, 1);
|
|
i--;
|
|
} else {
|
|
var nbMergedChars = charCombinesWithPrecedingChars(expression, i, expressions);
|
|
expressions.splice(i - nbMergedChars + 1, nbMergedChars);
|
|
i -= nbMergedChars;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sorts expressions in char class in the following order:
|
|
* - meta chars, ordered alphabetically by value
|
|
* - chars (except `control` kind) and class ranges, ordered alphabetically (`from` char is used for class ranges)
|
|
* - if ambiguous, class range comes before char
|
|
* - if ambiguous between two class ranges, orders alphabetically by `to` char
|
|
* - control chars, ordered alphabetically by value
|
|
* @param {Object} a - Left Char or ClassRange node
|
|
* @param {Object} b - Right Char or ClassRange node
|
|
* @returns {number}
|
|
*/
|
|
function sortCharClass(a, b) {
|
|
var aValue = getSortValue(a);
|
|
var bValue = getSortValue(b);
|
|
|
|
if (aValue === bValue) {
|
|
// We want ClassRange before Char
|
|
// [bb-d] -> [b-db]
|
|
if (a.type === 'ClassRange' && b.type !== 'ClassRange') {
|
|
return -1;
|
|
}
|
|
if (b.type === 'ClassRange' && a.type !== 'ClassRange') {
|
|
return 1;
|
|
}
|
|
if (a.type === 'ClassRange' && b.type === 'ClassRange') {
|
|
return getSortValue(a.to) - getSortValue(b.to);
|
|
}
|
|
if (isMeta(a) && isMeta(b) || isControl(a) && isControl(b)) {
|
|
return a.value < b.value ? -1 : 1;
|
|
}
|
|
}
|
|
return aValue - bValue;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @returns {number}
|
|
*/
|
|
function getSortValue(expression) {
|
|
if (expression.type === 'Char') {
|
|
if (expression.value === '-') {
|
|
return Infinity;
|
|
}
|
|
if (expression.kind === 'control') {
|
|
return Infinity;
|
|
}
|
|
if (expression.kind === 'meta' && isNaN(expression.codePoint)) {
|
|
return -1;
|
|
}
|
|
return expression.codePoint;
|
|
}
|
|
// ClassRange
|
|
return expression.from.codePoint;
|
|
}
|
|
|
|
/**
|
|
* Checks if a node is a meta char from the set \d\w\s\D\W\S
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @param {?string} value
|
|
* @returns {boolean}
|
|
*/
|
|
function isMeta(expression) {
|
|
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
|
|
return expression.type === 'Char' && expression.kind === 'meta' && (value ? expression.value === value : /^\\[dws]$/i.test(expression.value));
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @returns {boolean}
|
|
*/
|
|
function isControl(expression) {
|
|
return expression.type === 'Char' && expression.kind === 'control';
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @param {string[]} metas - Array of meta chars, e.g. ["\\w", "\\s"]
|
|
* @param {boolean} hasIUFlags
|
|
* @returns {boolean}
|
|
*/
|
|
function fitsInMetas(expression, metas, hasIUFlags) {
|
|
for (var i = 0; i < metas.length; i++) {
|
|
if (fitsInMeta(expression, metas[i], hasIUFlags)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @param {string} meta - e.g. "\\w"
|
|
* @param {boolean} hasIUFlags
|
|
* @returns {boolean}
|
|
*/
|
|
function fitsInMeta(expression, meta, hasIUFlags) {
|
|
if (expression.type === 'ClassRange') {
|
|
return fitsInMeta(expression.from, meta, hasIUFlags) && fitsInMeta(expression.to, meta, hasIUFlags);
|
|
}
|
|
|
|
// Special cases:
|
|
// \S contains \w and \d
|
|
if (meta === '\\S' && (isMeta(expression, '\\w') || isMeta(expression, '\\d'))) {
|
|
return true;
|
|
}
|
|
// \D contains \W and \s
|
|
if (meta === '\\D' && (isMeta(expression, '\\W') || isMeta(expression, '\\s'))) {
|
|
return true;
|
|
}
|
|
// \w contains \d
|
|
if (meta === '\\w' && isMeta(expression, '\\d')) {
|
|
return true;
|
|
}
|
|
// \W contains \s
|
|
if (meta === '\\W' && isMeta(expression, '\\s')) {
|
|
return true;
|
|
}
|
|
|
|
if (expression.type !== 'Char' || isNaN(expression.codePoint)) {
|
|
return false;
|
|
}
|
|
|
|
if (meta === '\\s') {
|
|
return fitsInMetaS(expression);
|
|
}
|
|
if (meta === '\\S') {
|
|
return !fitsInMetaS(expression);
|
|
}
|
|
if (meta === '\\d') {
|
|
return fitsInMetaD(expression);
|
|
}
|
|
if (meta === '\\D') {
|
|
return !fitsInMetaD(expression);
|
|
}
|
|
if (meta === '\\w') {
|
|
return fitsInMetaW(expression, hasIUFlags);
|
|
}
|
|
if (meta === '\\W') {
|
|
return !fitsInMetaW(expression, hasIUFlags);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char node with codePoint
|
|
* @returns {boolean}
|
|
*/
|
|
function fitsInMetaS(expression) {
|
|
return expression.codePoint === 0x0009 || // \t
|
|
expression.codePoint === 0x000a || // \n
|
|
expression.codePoint === 0x000b || // \v
|
|
expression.codePoint === 0x000c || // \f
|
|
expression.codePoint === 0x000d || // \r
|
|
expression.codePoint === 0x0020 || // space
|
|
expression.codePoint === 0x00a0 || // nbsp
|
|
expression.codePoint === 0x1680 || // part of Zs
|
|
expression.codePoint >= 0x2000 && expression.codePoint <= 0x200a || // part of Zs
|
|
expression.codePoint === 0x2028 || // line separator
|
|
expression.codePoint === 0x2029 || // paragraph separator
|
|
expression.codePoint === 0x202f || // part of Zs
|
|
expression.codePoint === 0x205f || // part of Zs
|
|
expression.codePoint === 0x3000 || // part of Zs
|
|
expression.codePoint === 0xfeff; // zwnbsp
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char node with codePoint
|
|
* @returns {boolean}
|
|
*/
|
|
function fitsInMetaD(expression) {
|
|
return expression.codePoint >= 0x30 && expression.codePoint <= 0x39; // 0-9
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char node with codePoint
|
|
* @param {boolean} hasIUFlags
|
|
* @returns {boolean}
|
|
*/
|
|
function fitsInMetaW(expression, hasIUFlags) {
|
|
return fitsInMetaD(expression) || expression.codePoint >= 0x41 && expression.codePoint <= 0x5a || // A-Z
|
|
expression.codePoint >= 0x61 && expression.codePoint <= 0x7a || // a-z
|
|
expression.value === '_' || hasIUFlags && (expression.codePoint === 0x017f || expression.codePoint === 0x212a);
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @param {Object} classRange - Char or ClassRange node
|
|
* @returns {boolean}
|
|
*/
|
|
function combinesWithPrecedingClassRange(expression, classRange) {
|
|
if (classRange && classRange.type === 'ClassRange') {
|
|
if (fitsInClassRange(expression, classRange)) {
|
|
// [a-gc] -> [a-g]
|
|
// [a-gc-e] -> [a-g]
|
|
return true;
|
|
} else if (
|
|
// We only want \w chars or char codes to keep readability
|
|
isMetaWCharOrCode(expression) && classRange.to.codePoint === expression.codePoint - 1) {
|
|
// [a-de] -> [a-e]
|
|
classRange.to = expression;
|
|
return true;
|
|
} else if (expression.type === 'ClassRange' && expression.from.codePoint <= classRange.to.codePoint + 1 && expression.to.codePoint >= classRange.from.codePoint - 1) {
|
|
// [a-db-f] -> [a-f]
|
|
// [b-fa-d] -> [a-f]
|
|
// [a-cd-f] -> [a-f]
|
|
if (expression.from.codePoint < classRange.from.codePoint) {
|
|
classRange.from = expression.from;
|
|
}
|
|
if (expression.to.codePoint > classRange.to.codePoint) {
|
|
classRange.to = expression.to;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @param {Object} classRange - Char or ClassRange node
|
|
* @returns {boolean}
|
|
*/
|
|
function combinesWithFollowingClassRange(expression, classRange) {
|
|
if (classRange && classRange.type === 'ClassRange') {
|
|
// Considering the elements were ordered alphabetically,
|
|
// there is only one case to handle
|
|
// [ab-e] -> [a-e]
|
|
if (
|
|
// We only want \w chars or char codes to keep readability
|
|
isMetaWCharOrCode(expression) && classRange.from.codePoint === expression.codePoint + 1) {
|
|
classRange.from = expression;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @param {Object} classRange - ClassRange node
|
|
* @returns {boolean}
|
|
*/
|
|
function fitsInClassRange(expression, classRange) {
|
|
if (expression.type === 'Char' && isNaN(expression.codePoint)) {
|
|
return false;
|
|
}
|
|
if (expression.type === 'ClassRange') {
|
|
return fitsInClassRange(expression.from, classRange) && fitsInClassRange(expression.to, classRange);
|
|
}
|
|
return expression.codePoint >= classRange.from.codePoint && expression.codePoint <= classRange.to.codePoint;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} expression - Char or ClassRange node
|
|
* @param {Number} index
|
|
* @param {Object[]} expressions - expressions in CharClass
|
|
* @returns {number} - Number of characters combined with expression
|
|
*/
|
|
function charCombinesWithPrecedingChars(expression, index, expressions) {
|
|
// We only want \w chars or char codes to keep readability
|
|
if (!isMetaWCharOrCode(expression)) {
|
|
return 0;
|
|
}
|
|
var nbMergedChars = 0;
|
|
while (index > 0) {
|
|
var currentExpression = expressions[index];
|
|
var precedingExpresion = expressions[index - 1];
|
|
if (isMetaWCharOrCode(precedingExpresion) && precedingExpresion.codePoint === currentExpression.codePoint - 1) {
|
|
nbMergedChars++;
|
|
index--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nbMergedChars > 1) {
|
|
expressions[index] = {
|
|
type: 'ClassRange',
|
|
from: expressions[index],
|
|
to: expression
|
|
};
|
|
return nbMergedChars;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function isMetaWCharOrCode(expression) {
|
|
return expression && expression.type === 'Char' && !isNaN(expression.codePoint) && (fitsInMetaW(expression, false) || expression.kind === 'unicode' || expression.kind === 'hex' || expression.kind === 'oct' || expression.kind === 'decimal');
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var NodePath$1 = nodePath;
|
|
|
|
var _require$7 = utils,
|
|
disjunctionToList = _require$7.disjunctionToList,
|
|
listToDisjunction = _require$7.listToDisjunction;
|
|
|
|
/**
|
|
* Removes duplicates from a disjunction sequence:
|
|
*
|
|
* /(ab|bc|ab)+(xy|xy)+/ -> /(ab|bc)+(xy)+/
|
|
*/
|
|
|
|
|
|
var disjunctionRemoveDuplicatesTransform = {
|
|
Disjunction: function Disjunction(path) {
|
|
var node = path.node;
|
|
|
|
// Make unique nodes.
|
|
|
|
var uniqueNodesMap = {};
|
|
|
|
var parts = disjunctionToList(node).filter(function (part) {
|
|
var encoded = part ? NodePath$1.getForNode(part).jsonEncode() : 'null';
|
|
|
|
// Already recorded this part, filter out.
|
|
if (uniqueNodesMap.hasOwnProperty(encoded)) {
|
|
return false;
|
|
}
|
|
|
|
uniqueNodesMap[encoded] = part;
|
|
return true;
|
|
});
|
|
|
|
// Replace with the optimized disjunction.
|
|
path.replace(listToDisjunction(parts));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to replace single char group disjunction to char group
|
|
*
|
|
* a|b|c -> [abc]
|
|
* [12]|3|4 -> [1234]
|
|
* (a|b|c) -> ([abc])
|
|
* (?:a|b|c) -> [abc]
|
|
*/
|
|
|
|
var groupSingleCharsToCharClass = {
|
|
Disjunction: function Disjunction(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
|
|
if (!handlers[parent.type]) {
|
|
return;
|
|
}
|
|
|
|
var charset = new Map();
|
|
|
|
if (!shouldProcess(node, charset) || !charset.size) {
|
|
return;
|
|
}
|
|
|
|
var characterClass = {
|
|
type: 'CharacterClass',
|
|
expressions: Array.from(charset.keys()).sort().map(function (key) {
|
|
return charset.get(key);
|
|
})
|
|
};
|
|
|
|
handlers[parent.type](path.getParent(), characterClass);
|
|
}
|
|
};
|
|
|
|
var handlers = {
|
|
RegExp: function RegExp(path, characterClass) {
|
|
var node = path.node;
|
|
|
|
|
|
node.body = characterClass;
|
|
},
|
|
Group: function Group(path, characterClass) {
|
|
var node = path.node;
|
|
|
|
|
|
if (node.capturing) {
|
|
node.expression = characterClass;
|
|
} else {
|
|
path.replace(characterClass);
|
|
}
|
|
}
|
|
};
|
|
|
|
function shouldProcess(expression, charset) {
|
|
if (!expression) {
|
|
// Abort on empty disjunction part
|
|
return false;
|
|
}
|
|
|
|
var type = expression.type;
|
|
|
|
|
|
if (type === 'Disjunction') {
|
|
var left = expression.left,
|
|
right = expression.right;
|
|
|
|
|
|
return shouldProcess(left, charset) && shouldProcess(right, charset);
|
|
} else if (type === 'Char') {
|
|
if (expression.kind === 'meta' && expression.symbol === '.') {
|
|
return false;
|
|
}
|
|
|
|
var value = expression.value;
|
|
|
|
|
|
charset.set(value, expression);
|
|
|
|
return true;
|
|
} else if (type === 'CharacterClass' && !expression.negative) {
|
|
return expression.expressions.every(function (expression) {
|
|
return shouldProcess(expression, charset);
|
|
});
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to remove non-capturing empty groups.
|
|
*
|
|
* /(?:)a/ -> /a/
|
|
* /a|(?:)/ -> /a|/
|
|
*/
|
|
|
|
var removeEmptyGroupTransform = {
|
|
Group: function Group(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
var childPath = path.getChild();
|
|
|
|
if (node.capturing || childPath) {
|
|
return;
|
|
}
|
|
|
|
if (parent.type === 'Repetition') {
|
|
|
|
path.getParent().replace(node);
|
|
} else if (parent.type !== 'RegExp') {
|
|
|
|
path.remove();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A regexp-tree plugin to remove unnecessary groups.
|
|
*
|
|
* /(?:a)/ -> /a/
|
|
*/
|
|
|
|
function _toConsumableArray$5(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
var ungroupTransform = {
|
|
Group: function Group(path) {
|
|
var node = path.node,
|
|
parent = path.parent;
|
|
|
|
var childPath = path.getChild();
|
|
|
|
if (node.capturing || !childPath) {
|
|
return;
|
|
}
|
|
|
|
// Don't optimize \1(?:0) to \10
|
|
if (!hasAppropriateSiblings(path)) {
|
|
return;
|
|
}
|
|
|
|
// Don't optimize /a(?:b|c)/ to /ab|c/
|
|
// but /(?:b|c)/ to /b|c/ is ok
|
|
if (childPath.node.type === 'Disjunction' && parent.type !== 'RegExp') {
|
|
return;
|
|
}
|
|
|
|
// Don't optimize /(?:ab)+/ to /ab+/
|
|
// but /(?:a)+/ to /a+/ is ok
|
|
// and /(?:[a-d])+/ to /[a-d]+/ is ok too
|
|
if (parent.type === 'Repetition' && childPath.node.type !== 'Char' && childPath.node.type !== 'CharacterClass') {
|
|
return;
|
|
}
|
|
|
|
if (childPath.node.type === 'Alternative') {
|
|
var parentPath = path.getParent();
|
|
if (parentPath.node.type === 'Alternative') {
|
|
// /abc(?:def)ghi/ When (?:def) is ungrouped its content must be merged with parent alternative
|
|
|
|
parentPath.replace({
|
|
type: 'Alternative',
|
|
expressions: [].concat(_toConsumableArray$5(parent.expressions.slice(0, path.index)), _toConsumableArray$5(childPath.node.expressions), _toConsumableArray$5(parent.expressions.slice(path.index + 1)))
|
|
});
|
|
}
|
|
} else {
|
|
path.replace(childPath.node);
|
|
}
|
|
}
|
|
};
|
|
|
|
function hasAppropriateSiblings(path) {
|
|
var parent = path.parent,
|
|
index = path.index;
|
|
|
|
|
|
if (parent.type !== 'Alternative') {
|
|
return true;
|
|
}
|
|
|
|
var previousNode = parent.expressions[index - 1];
|
|
if (previousNode == null) {
|
|
return true;
|
|
}
|
|
|
|
// Don't optimized \1(?:0) to \10
|
|
if (previousNode.type === 'Backreference' && previousNode.kind === 'number') {
|
|
return false;
|
|
}
|
|
|
|
// Don't optimized \2(?:0) to \20
|
|
if (previousNode.type === 'Char' && previousNode.kind === 'decimal') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
function _toConsumableArray$4(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
var NodePath = nodePath;
|
|
|
|
var _require$6 = utils,
|
|
increaseQuantifierByOne = _require$6.increaseQuantifierByOne;
|
|
|
|
/**
|
|
* A regexp-tree plugin to combine repeating patterns.
|
|
*
|
|
* /^abcabcabc/ -> /^abc{3}/
|
|
* /^(?:abc){2}abc/ -> /^(?:abc){3}/
|
|
* /^abc(?:abc){2}/ -> /^(?:abc){3}/
|
|
*/
|
|
|
|
var combineRepeatingPatternsTransform = {
|
|
Alternative: function Alternative(path) {
|
|
var node = path.node;
|
|
|
|
// We can skip the first child
|
|
|
|
var index = 1;
|
|
while (index < node.expressions.length) {
|
|
var child = path.getChild(index);
|
|
index = Math.max(1, combineRepeatingPatternLeft(path, child, index));
|
|
|
|
if (index >= node.expressions.length) {
|
|
break;
|
|
}
|
|
|
|
child = path.getChild(index);
|
|
index = Math.max(1, combineWithPreviousRepetition(path, child, index));
|
|
|
|
if (index >= node.expressions.length) {
|
|
break;
|
|
}
|
|
|
|
child = path.getChild(index);
|
|
index = Math.max(1, combineRepetitionWithPrevious(path, child, index));
|
|
|
|
index++;
|
|
}
|
|
}
|
|
};
|
|
|
|
// abcabc -> (?:abc){2}
|
|
function combineRepeatingPatternLeft(alternative, child, index) {
|
|
var node = alternative.node;
|
|
|
|
|
|
var nbPossibleLengths = Math.ceil(index / 2);
|
|
var i = 0;
|
|
|
|
while (i < nbPossibleLengths) {
|
|
var startIndex = index - 2 * i - 1;
|
|
var right = void 0,
|
|
left = void 0;
|
|
|
|
if (i === 0) {
|
|
right = child;
|
|
left = alternative.getChild(startIndex);
|
|
} else {
|
|
right = NodePath.getForNode({
|
|
type: 'Alternative',
|
|
expressions: [].concat(_toConsumableArray$4(node.expressions.slice(index - i, index)), [child.node])
|
|
});
|
|
|
|
left = NodePath.getForNode({
|
|
type: 'Alternative',
|
|
expressions: [].concat(_toConsumableArray$4(node.expressions.slice(startIndex, index - i)))
|
|
});
|
|
}
|
|
|
|
if (right.hasEqualSource(left)) {
|
|
for (var j = 0; j < 2 * i + 1; j++) {
|
|
alternative.getChild(startIndex).remove();
|
|
}
|
|
|
|
child.replace({
|
|
type: 'Repetition',
|
|
expression: i === 0 && right.node.type !== 'Repetition' ? right.node : {
|
|
type: 'Group',
|
|
capturing: false,
|
|
expression: right.node
|
|
},
|
|
quantifier: {
|
|
type: 'Quantifier',
|
|
kind: 'Range',
|
|
from: 2,
|
|
to: 2,
|
|
greedy: true
|
|
}
|
|
});
|
|
return startIndex;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
// (?:abc){2}abc -> (?:abc){3}
|
|
function combineWithPreviousRepetition(alternative, child, index) {
|
|
var node = alternative.node;
|
|
|
|
|
|
var i = 0;
|
|
while (i < index) {
|
|
var previousChild = alternative.getChild(i);
|
|
|
|
if (previousChild.node.type === 'Repetition' && previousChild.node.quantifier.greedy) {
|
|
var left = previousChild.getChild();
|
|
var right = void 0;
|
|
|
|
if (left.node.type === 'Group' && !left.node.capturing) {
|
|
left = left.getChild();
|
|
}
|
|
|
|
if (i + 1 === index) {
|
|
right = child;
|
|
if (right.node.type === 'Group' && !right.node.capturing) {
|
|
right = right.getChild();
|
|
}
|
|
} else {
|
|
right = NodePath.getForNode({
|
|
type: 'Alternative',
|
|
expressions: [].concat(_toConsumableArray$4(node.expressions.slice(i + 1, index + 1)))
|
|
});
|
|
}
|
|
|
|
if (left.hasEqualSource(right)) {
|
|
for (var j = i; j < index; j++) {
|
|
alternative.getChild(i + 1).remove();
|
|
}
|
|
|
|
increaseQuantifierByOne(previousChild.node.quantifier);
|
|
|
|
return i;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// abc(?:abc){2} -> (?:abc){3}
|
|
function combineRepetitionWithPrevious(alternative, child, index) {
|
|
var node = alternative.node;
|
|
|
|
|
|
if (child.node.type === 'Repetition' && child.node.quantifier.greedy) {
|
|
var right = child.getChild();
|
|
var left = void 0;
|
|
|
|
if (right.node.type === 'Group' && !right.node.capturing) {
|
|
right = right.getChild();
|
|
}
|
|
|
|
var rightLength = void 0;
|
|
if (right.node.type === 'Alternative') {
|
|
rightLength = right.node.expressions.length;
|
|
left = NodePath.getForNode({
|
|
type: 'Alternative',
|
|
expressions: [].concat(_toConsumableArray$4(node.expressions.slice(index - rightLength, index)))
|
|
});
|
|
} else {
|
|
rightLength = 1;
|
|
left = alternative.getChild(index - 1);
|
|
if (left.node.type === 'Group' && !left.node.capturing) {
|
|
left = left.getChild();
|
|
}
|
|
}
|
|
|
|
if (left.hasEqualSource(right)) {
|
|
for (var j = index - rightLength; j < index; j++) {
|
|
alternative.getChild(index - rightLength).remove();
|
|
}
|
|
|
|
increaseQuantifierByOne(child.node.quantifier);
|
|
|
|
return index - rightLength;
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var transforms = new Map([
|
|
// \ud83d\ude80 -> \u{1f680}
|
|
['charSurrogatePairToSingleUnicode', charSurrogatePairToSingleUnicodeTransform],
|
|
|
|
// \u0061 -> a
|
|
['charCodeToSimpleChar', charCodeToSimpleCharTransform],
|
|
|
|
// /Aa/i -> /aa/i
|
|
['charCaseInsensitiveLowerCaseTransform', charCaseInsensitiveLowercaseTransform],
|
|
|
|
// [\d\d] -> [\d]
|
|
['charClassRemoveDuplicates', charClassRemoveDuplicatesTransform],
|
|
|
|
// a{1,2}a{2,3} -> a{3,5}
|
|
['quantifiersMerge', quantifiersMergeTransform],
|
|
|
|
// a{1,} -> a+, a{3,3} -> a{3}, a{1} -> a
|
|
['quantifierRangeToSymbol', quantifierRangeToSymbolTransform],
|
|
|
|
// [a-a] -> [a], [a-b] -> [ab]
|
|
['charClassClassrangesToChars', charClassClassrangesToCharsTransform],
|
|
|
|
// [0-9] -> [\d]
|
|
['charClassToMeta', charClassToMetaTransform],
|
|
|
|
// [\d] -> \d, [^\w] -> \W
|
|
['charClassToSingleChar', charClassToSingleCharTransform],
|
|
|
|
// \e -> e
|
|
['charEscapeUnescape', charEscapeUnescapeTransform],
|
|
|
|
// [a-de-f] -> [a-f]
|
|
['charClassClassrangesMerge', charClassClassrangesMergeTransform],
|
|
|
|
// (ab|ab) -> (ab)
|
|
['disjunctionRemoveDuplicates', disjunctionRemoveDuplicatesTransform],
|
|
|
|
// (a|b|c) -> [abc]
|
|
['groupSingleCharsToCharClass', groupSingleCharsToCharClass],
|
|
|
|
// (?:)a -> a
|
|
['removeEmptyGroup', removeEmptyGroupTransform],
|
|
|
|
// (?:a) -> a
|
|
['ungroup', ungroupTransform],
|
|
|
|
// abcabcabc -> (?:abc){3}
|
|
['combineRepeatingPatterns', combineRepeatingPatternsTransform]]);
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var clone = clone$1;
|
|
var parser$2 = parser$4;
|
|
var transform = transform$1;
|
|
var optimizationTransforms = transforms;
|
|
|
|
var optimizer$1 = {
|
|
/**
|
|
* Optimizer transforms a regular expression into an optimized version,
|
|
* replacing some sub-expressions with their idiomatic patterns.
|
|
*
|
|
* @param string | RegExp | AST - a regexp to optimize.
|
|
*
|
|
* @return TransformResult - an optimized regexp.
|
|
*
|
|
* Example:
|
|
*
|
|
* /[a-zA-Z_0-9][a-zA-Z_0-9]*\e{1,}/
|
|
*
|
|
* Optimized to:
|
|
*
|
|
* /\w+e+/
|
|
*/
|
|
optimize: function optimize(regexp) {
|
|
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
_ref$whitelist = _ref.whitelist,
|
|
whitelist = _ref$whitelist === undefined ? [] : _ref$whitelist,
|
|
_ref$blacklist = _ref.blacklist,
|
|
blacklist = _ref$blacklist === undefined ? [] : _ref$blacklist;
|
|
|
|
var transformsRaw = whitelist.length > 0 ? whitelist : Array.from(optimizationTransforms.keys());
|
|
|
|
var transformToApply = transformsRaw.filter(function (transform) {
|
|
return !blacklist.includes(transform);
|
|
});
|
|
|
|
var ast = regexp;
|
|
if (regexp instanceof RegExp) {
|
|
regexp = '' + regexp;
|
|
}
|
|
|
|
if (typeof regexp === 'string') {
|
|
ast = parser$2.parse(regexp);
|
|
}
|
|
|
|
var result = new transform.TransformResult(ast);
|
|
var prevResultString = void 0;
|
|
|
|
do {
|
|
// Get a copy of the current state here so
|
|
// we can compare it with the state at the
|
|
// end of the loop.
|
|
prevResultString = result.toString();
|
|
ast = clone(result.getAST());
|
|
|
|
transformToApply.forEach(function (transformName) {
|
|
if (!optimizationTransforms.has(transformName)) {
|
|
throw new Error('Unknown optimization-transform: ' + transformName + '. ' + 'Available transforms are: ' + Array.from(optimizationTransforms.keys()).join(', '));
|
|
}
|
|
|
|
var transformer = optimizationTransforms.get(transformName);
|
|
|
|
// Don't override result just yet since we
|
|
// might want to rollback the transform
|
|
var newResult = transform.transform(ast, transformer);
|
|
|
|
if (newResult.toString() !== result.toString()) {
|
|
if (newResult.toString().length <= result.toString().length) {
|
|
result = newResult;
|
|
} else {
|
|
// Result has changed but is not shorter:
|
|
// restore ast to its previous state.
|
|
|
|
ast = clone(result.getAST());
|
|
}
|
|
}
|
|
});
|
|
|
|
// Keep running the optimizer until it stops
|
|
// making any change to the regexp.
|
|
} while (result.toString() !== prevResultString);
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* Epsilon, the empty string.
|
|
*/
|
|
|
|
var EPSILON$3 = 'ε';
|
|
|
|
/**
|
|
* Epsilon-closure.
|
|
*/
|
|
var EPSILON_CLOSURE$2 = EPSILON$3 + '*';
|
|
|
|
var specialSymbols = {
|
|
EPSILON: EPSILON$3,
|
|
EPSILON_CLOSURE: EPSILON_CLOSURE$2
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var _slicedToArray$1 = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
|
|
var _createClass$4 = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _toConsumableArray$3(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
function _classCallCheck$4(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var _require$5 = specialSymbols,
|
|
EPSILON$2 = _require$5.EPSILON,
|
|
EPSILON_CLOSURE$1 = _require$5.EPSILON_CLOSURE;
|
|
|
|
/**
|
|
* NFA fragment.
|
|
*
|
|
* NFA sub-fragments can be combined to a larger NFAs building
|
|
* the resulting machine. Combining the fragments is done by patching
|
|
* edges of the in- and out-states.
|
|
*
|
|
* 2-states implementation, `in`, and `out`. Eventually all transitions
|
|
* go to the same `out`, which can further be connected via ε-transition
|
|
* with other fragment.
|
|
*/
|
|
|
|
|
|
var NFA$2 = function () {
|
|
function NFA(inState, outState) {
|
|
_classCallCheck$4(this, NFA);
|
|
|
|
this.in = inState;
|
|
this.out = outState;
|
|
}
|
|
|
|
/**
|
|
* Tries to recognize a string based on this NFA fragment.
|
|
*/
|
|
|
|
|
|
_createClass$4(NFA, [{
|
|
key: 'matches',
|
|
value: function matches(string) {
|
|
return this.in.matches(string);
|
|
}
|
|
|
|
/**
|
|
* Returns an alphabet for this NFA.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getAlphabet',
|
|
value: function getAlphabet() {
|
|
if (!this._alphabet) {
|
|
this._alphabet = new Set();
|
|
var table = this.getTransitionTable();
|
|
for (var state in table) {
|
|
var transitions = table[state];
|
|
for (var symbol in transitions) {
|
|
if (symbol !== EPSILON_CLOSURE$1) {
|
|
this._alphabet.add(symbol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return this._alphabet;
|
|
}
|
|
|
|
/**
|
|
* Returns set of accepting states.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getAcceptingStates',
|
|
value: function getAcceptingStates() {
|
|
if (!this._acceptingStates) {
|
|
// States are determined during table construction.
|
|
this.getTransitionTable();
|
|
}
|
|
return this._acceptingStates;
|
|
}
|
|
|
|
/**
|
|
* Returns accepting state numbers.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getAcceptingStateNumbers',
|
|
value: function getAcceptingStateNumbers() {
|
|
if (!this._acceptingStateNumbers) {
|
|
this._acceptingStateNumbers = new Set();
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = this.getAcceptingStates()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var acceptingState = _step.value;
|
|
|
|
this._acceptingStateNumbers.add(acceptingState.number);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return this._acceptingStateNumbers;
|
|
}
|
|
|
|
/**
|
|
* Builds and returns transition table.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getTransitionTable',
|
|
value: function getTransitionTable() {
|
|
var _this = this;
|
|
|
|
if (!this._transitionTable) {
|
|
this._transitionTable = {};
|
|
this._acceptingStates = new Set();
|
|
|
|
var visited = new Set();
|
|
var symbols = new Set();
|
|
|
|
var visitState = function visitState(state) {
|
|
if (visited.has(state)) {
|
|
return;
|
|
}
|
|
|
|
visited.add(state);
|
|
state.number = visited.size;
|
|
_this._transitionTable[state.number] = {};
|
|
|
|
if (state.accepting) {
|
|
_this._acceptingStates.add(state);
|
|
}
|
|
|
|
var transitions = state.getTransitions();
|
|
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = transitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var _ref = _step2.value;
|
|
|
|
var _ref2 = _slicedToArray$1(_ref, 2);
|
|
|
|
var symbol = _ref2[0];
|
|
var symbolTransitions = _ref2[1];
|
|
|
|
var combinedState = [];
|
|
symbols.add(symbol);
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = symbolTransitions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var nextState = _step3.value;
|
|
|
|
visitState(nextState);
|
|
combinedState.push(nextState.number);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
_this._transitionTable[state.number][symbol] = combinedState;
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Traverse the graph starting from the `in`.
|
|
visitState(this.in);
|
|
|
|
// Append epsilon-closure column.
|
|
visited.forEach(function (state) {
|
|
delete _this._transitionTable[state.number][EPSILON$2];
|
|
_this._transitionTable[state.number][EPSILON_CLOSURE$1] = [].concat(_toConsumableArray$3(state.getEpsilonClosure())).map(function (s) {
|
|
return s.number;
|
|
});
|
|
});
|
|
}
|
|
|
|
return this._transitionTable;
|
|
}
|
|
}]);
|
|
|
|
return NFA;
|
|
}();
|
|
|
|
var nfa = NFA$2;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
// DFA minization.
|
|
|
|
/**
|
|
* Map from state to current set it goes.
|
|
*/
|
|
|
|
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
|
|
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
|
|
|
|
function _toConsumableArray$2(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
var currentTransitionMap = null;
|
|
|
|
/**
|
|
* Takes a DFA, and returns a minimized version of it
|
|
* compressing some states to groups (using standard, 0-, 1-,
|
|
* 2-, ... N-equivalence algorithm).
|
|
*/
|
|
function minimize(dfa) {
|
|
var table = dfa.getTransitionTable();
|
|
var allStates = Object.keys(table);
|
|
var alphabet = dfa.getAlphabet();
|
|
var accepting = dfa.getAcceptingStateNumbers();
|
|
|
|
currentTransitionMap = {};
|
|
|
|
var nonAccepting = new Set();
|
|
|
|
allStates.forEach(function (state) {
|
|
state = Number(state);
|
|
var isAccepting = accepting.has(state);
|
|
|
|
if (isAccepting) {
|
|
currentTransitionMap[state] = accepting;
|
|
} else {
|
|
nonAccepting.add(state);
|
|
currentTransitionMap[state] = nonAccepting;
|
|
}
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Step 1: build equivalent sets.
|
|
|
|
// All [1..N] equivalent sets.
|
|
var all = [
|
|
// 0-equivalent sets.
|
|
[nonAccepting, accepting].filter(function (set) {
|
|
return set.size > 0;
|
|
})];
|
|
|
|
var current = void 0;
|
|
var previous = void 0;
|
|
|
|
// Top of the stack is the current list of sets to analyze.
|
|
current = all[all.length - 1];
|
|
|
|
// Previous set (to check whether we need to stop).
|
|
previous = all[all.length - 2];
|
|
|
|
// Until we'll not have the same N and N-1 equivalent rows.
|
|
|
|
var _loop = function _loop() {
|
|
var newTransitionMap = {};
|
|
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = current[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var _set = _step3.value;
|
|
|
|
// Handled states for this set.
|
|
var handledStates = {};
|
|
|
|
var _set2 = _toArray(_set),
|
|
first = _set2[0],
|
|
rest = _set2.slice(1);
|
|
|
|
handledStates[first] = new Set([first]);
|
|
|
|
// Have to compare each from the rest states with
|
|
// the already handled states, and see if they are equivalent.
|
|
var _iteratorNormalCompletion4 = true;
|
|
var _didIteratorError4 = false;
|
|
var _iteratorError4 = undefined;
|
|
|
|
try {
|
|
restSets: for (var _iterator4 = rest[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
|
var state = _step4.value;
|
|
var _iteratorNormalCompletion5 = true;
|
|
var _didIteratorError5 = false;
|
|
var _iteratorError5 = undefined;
|
|
|
|
try {
|
|
for (var _iterator5 = Object.keys(handledStates)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
|
var handledState = _step5.value;
|
|
|
|
// This and some previously handled state are equivalent --
|
|
// just append this state to the same set.
|
|
if (areEquivalent(state, handledState, table, alphabet)) {
|
|
handledStates[handledState].add(state);
|
|
handledStates[state] = handledStates[handledState];
|
|
continue restSets;
|
|
}
|
|
}
|
|
// Else, this state is not equivalent to any of the
|
|
// handled states -- allocate a new set for it.
|
|
} catch (err) {
|
|
_didIteratorError5 = true;
|
|
_iteratorError5 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
|
_iterator5.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError5) {
|
|
throw _iteratorError5;
|
|
}
|
|
}
|
|
}
|
|
|
|
handledStates[state] = new Set([state]);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError4 = true;
|
|
_iteratorError4 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
|
_iterator4.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError4) {
|
|
throw _iteratorError4;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add these handled states to all states map.
|
|
|
|
|
|
Object.assign(newTransitionMap, handledStates);
|
|
}
|
|
|
|
// Update current transition map for the handled row.
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
currentTransitionMap = newTransitionMap;
|
|
|
|
var newSets = new Set(Object.keys(newTransitionMap).map(function (state) {
|
|
return newTransitionMap[state];
|
|
}));
|
|
|
|
all.push([].concat(_toConsumableArray$2(newSets)));
|
|
|
|
// Top of the stack is the current.
|
|
current = all[all.length - 1];
|
|
|
|
// Previous set.
|
|
previous = all[all.length - 2];
|
|
};
|
|
|
|
while (!sameRow(current, previous)) {
|
|
_loop();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Step 2: build minimized table from the equivalent sets.
|
|
|
|
// Remap state numbers from sets to index-based.
|
|
var remaped = new Map();
|
|
var idx = 1;
|
|
current.forEach(function (set) {
|
|
return remaped.set(set, idx++);
|
|
});
|
|
|
|
// Build the minimized table from the calculated equivalent sets.
|
|
var minimizedTable = {};
|
|
|
|
var minimizedAcceptingStates = new Set();
|
|
|
|
var updateAcceptingStates = function updateAcceptingStates(set, idx) {
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = set[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var state = _step.value;
|
|
|
|
if (accepting.has(state)) {
|
|
minimizedAcceptingStates.add(idx);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = remaped.entries()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var _ref = _step2.value;
|
|
|
|
var _ref2 = _slicedToArray(_ref, 2);
|
|
|
|
var set = _ref2[0];
|
|
var _idx = _ref2[1];
|
|
|
|
minimizedTable[_idx] = {};
|
|
var _iteratorNormalCompletion6 = true;
|
|
var _didIteratorError6 = false;
|
|
var _iteratorError6 = undefined;
|
|
|
|
try {
|
|
for (var _iterator6 = alphabet[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
|
|
var symbol = _step6.value;
|
|
|
|
updateAcceptingStates(set, _idx);
|
|
|
|
// Determine original transition for this symbol from the set.
|
|
var originalTransition = void 0;
|
|
var _iteratorNormalCompletion7 = true;
|
|
var _didIteratorError7 = false;
|
|
var _iteratorError7 = undefined;
|
|
|
|
try {
|
|
for (var _iterator7 = set[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
|
|
var originalState = _step7.value;
|
|
|
|
originalTransition = table[originalState][symbol];
|
|
if (originalTransition) {
|
|
break;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError7 = true;
|
|
_iteratorError7 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion7 && _iterator7.return) {
|
|
_iterator7.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError7) {
|
|
throw _iteratorError7;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (originalTransition) {
|
|
minimizedTable[_idx][symbol] = remaped.get(currentTransitionMap[originalTransition]);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError6 = true;
|
|
_iteratorError6 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion6 && _iterator6.return) {
|
|
_iterator6.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError6) {
|
|
throw _iteratorError6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the table, and accepting states on the original DFA.
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
dfa.setTransitionTable(minimizedTable);
|
|
dfa.setAcceptingStateNumbers(minimizedAcceptingStates);
|
|
|
|
return dfa;
|
|
}
|
|
|
|
function sameRow(r1, r2) {
|
|
if (!r2) {
|
|
return false;
|
|
}
|
|
|
|
if (r1.length !== r2.length) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < r1.length; i++) {
|
|
var s1 = r1[i];
|
|
var s2 = r2[i];
|
|
|
|
if (s1.size !== s2.size) {
|
|
return false;
|
|
}
|
|
|
|
if ([].concat(_toConsumableArray$2(s1)).sort().join(',') !== [].concat(_toConsumableArray$2(s2)).sort().join(',')) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks whether two states are N-equivalent, i.e. whether they go
|
|
* to the same set on a symbol.
|
|
*/
|
|
function areEquivalent(s1, s2, table, alphabet) {
|
|
var _iteratorNormalCompletion8 = true;
|
|
var _didIteratorError8 = false;
|
|
var _iteratorError8 = undefined;
|
|
|
|
try {
|
|
for (var _iterator8 = alphabet[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
|
|
var symbol = _step8.value;
|
|
|
|
if (!goToSameSet(s1, s2, table, symbol)) {
|
|
return false;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError8 = true;
|
|
_iteratorError8 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion8 && _iterator8.return) {
|
|
_iterator8.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError8) {
|
|
throw _iteratorError8;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks whether states go to the same set.
|
|
*/
|
|
function goToSameSet(s1, s2, table, symbol) {
|
|
if (!currentTransitionMap[s1] || !currentTransitionMap[s2]) {
|
|
return false;
|
|
}
|
|
|
|
var originalTransitionS1 = table[s1][symbol];
|
|
var originalTransitionS2 = table[s2][symbol];
|
|
|
|
// If no actual transition on this symbol, treat it as positive.
|
|
if (!originalTransitionS1 && !originalTransitionS2) {
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, check if they are in the same sets.
|
|
return currentTransitionMap[s1].has(originalTransitionS1) && currentTransitionMap[s2].has(originalTransitionS2);
|
|
}
|
|
|
|
var dfaMinimizer = {
|
|
minimize: minimize
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var _createClass$3 = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
function _classCallCheck$3(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var DFAMinimizer = dfaMinimizer;
|
|
|
|
var _require$4 = specialSymbols,
|
|
EPSILON_CLOSURE = _require$4.EPSILON_CLOSURE;
|
|
|
|
/**
|
|
* DFA is build by converting from NFA (subset construction).
|
|
*/
|
|
|
|
|
|
var DFA$1 = function () {
|
|
function DFA(nfa) {
|
|
_classCallCheck$3(this, DFA);
|
|
|
|
this._nfa = nfa;
|
|
}
|
|
|
|
/**
|
|
* Minimizes DFA.
|
|
*/
|
|
|
|
|
|
_createClass$3(DFA, [{
|
|
key: 'minimize',
|
|
value: function minimize() {
|
|
this.getTransitionTable();
|
|
|
|
this._originalAcceptingStateNumbers = this._acceptingStateNumbers;
|
|
this._originalTransitionTable = this._transitionTable;
|
|
|
|
DFAMinimizer.minimize(this);
|
|
}
|
|
|
|
/**
|
|
* Returns alphabet for this DFA.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getAlphabet',
|
|
value: function getAlphabet() {
|
|
return this._nfa.getAlphabet();
|
|
}
|
|
|
|
/**
|
|
* Returns accepting states.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getAcceptingStateNumbers',
|
|
value: function getAcceptingStateNumbers() {
|
|
if (!this._acceptingStateNumbers) {
|
|
// Accepting states are determined during table construction.
|
|
this.getTransitionTable();
|
|
}
|
|
|
|
return this._acceptingStateNumbers;
|
|
}
|
|
|
|
/**
|
|
* Returns original accepting states.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getOriginaAcceptingStateNumbers',
|
|
value: function getOriginaAcceptingStateNumbers() {
|
|
if (!this._originalAcceptingStateNumbers) {
|
|
// Accepting states are determined during table construction.
|
|
this.getTransitionTable();
|
|
}
|
|
|
|
return this._originalAcceptingStateNumbers;
|
|
}
|
|
|
|
/**
|
|
* Sets transition table.
|
|
*/
|
|
|
|
}, {
|
|
key: 'setTransitionTable',
|
|
value: function setTransitionTable(table) {
|
|
this._transitionTable = table;
|
|
}
|
|
|
|
/**
|
|
* Sets accepting states.
|
|
*/
|
|
|
|
}, {
|
|
key: 'setAcceptingStateNumbers',
|
|
value: function setAcceptingStateNumbers(stateNumbers) {
|
|
this._acceptingStateNumbers = stateNumbers;
|
|
}
|
|
|
|
/**
|
|
* DFA transition table is built from NFA table.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getTransitionTable',
|
|
value: function getTransitionTable() {
|
|
var _this = this;
|
|
|
|
if (this._transitionTable) {
|
|
return this._transitionTable;
|
|
}
|
|
|
|
// Calculate from NFA transition table.
|
|
var nfaTable = this._nfa.getTransitionTable();
|
|
var nfaStates = Object.keys(nfaTable);
|
|
|
|
this._acceptingStateNumbers = new Set();
|
|
|
|
// Start state of DFA is E(S[nfa])
|
|
var startState = nfaTable[nfaStates[0]][EPSILON_CLOSURE];
|
|
|
|
// Init the worklist (states which should be in the DFA).
|
|
var worklist = [startState];
|
|
|
|
var alphabet = this.getAlphabet();
|
|
var nfaAcceptingStates = this._nfa.getAcceptingStateNumbers();
|
|
|
|
var dfaTable = {};
|
|
|
|
// Determine whether the combined DFA state is accepting.
|
|
var updateAcceptingStates = function updateAcceptingStates(states) {
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = nfaAcceptingStates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var nfaAcceptingState = _step.value;
|
|
|
|
// If any of the states from NFA is accepting, DFA's
|
|
// state is accepting as well.
|
|
if (states.indexOf(nfaAcceptingState) !== -1) {
|
|
_this._acceptingStateNumbers.add(states.join(','));
|
|
break;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
while (worklist.length > 0) {
|
|
var states = worklist.shift();
|
|
var dfaStateLabel = states.join(',');
|
|
dfaTable[dfaStateLabel] = {};
|
|
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = alphabet[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var symbol = _step2.value;
|
|
|
|
var onSymbol = [];
|
|
|
|
// Determine whether the combined state is accepting.
|
|
updateAcceptingStates(states);
|
|
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = states[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var state = _step3.value;
|
|
|
|
var nfaStatesOnSymbol = nfaTable[state][symbol];
|
|
if (!nfaStatesOnSymbol) {
|
|
continue;
|
|
}
|
|
|
|
var _iteratorNormalCompletion4 = true;
|
|
var _didIteratorError4 = false;
|
|
var _iteratorError4 = undefined;
|
|
|
|
try {
|
|
for (var _iterator4 = nfaStatesOnSymbol[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
|
var nfaStateOnSymbol = _step4.value;
|
|
|
|
if (!nfaTable[nfaStateOnSymbol]) {
|
|
continue;
|
|
}
|
|
onSymbol.push.apply(onSymbol, _toConsumableArray$1(nfaTable[nfaStateOnSymbol][EPSILON_CLOSURE]));
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError4 = true;
|
|
_iteratorError4 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
|
_iterator4.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError4) {
|
|
throw _iteratorError4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
var dfaStatesOnSymbolSet = new Set(onSymbol);
|
|
var dfaStatesOnSymbol = [].concat(_toConsumableArray$1(dfaStatesOnSymbolSet));
|
|
|
|
if (dfaStatesOnSymbol.length > 0) {
|
|
var dfaOnSymbolStr = dfaStatesOnSymbol.join(',');
|
|
|
|
dfaTable[dfaStateLabel][symbol] = dfaOnSymbolStr;
|
|
|
|
if (!dfaTable.hasOwnProperty(dfaOnSymbolStr)) {
|
|
worklist.unshift(dfaStatesOnSymbol);
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this._transitionTable = this._remapStateNumbers(dfaTable);
|
|
}
|
|
|
|
/**
|
|
* Remaps state numbers in the resulting table:
|
|
* combined states '1,2,3' -> 1, '3,4' -> 2, etc.
|
|
*/
|
|
|
|
}, {
|
|
key: '_remapStateNumbers',
|
|
value: function _remapStateNumbers(calculatedDFATable) {
|
|
var newStatesMap = {};
|
|
|
|
this._originalTransitionTable = calculatedDFATable;
|
|
var transitionTable = {};
|
|
|
|
Object.keys(calculatedDFATable).forEach(function (originalNumber, newNumber) {
|
|
newStatesMap[originalNumber] = newNumber + 1;
|
|
});
|
|
|
|
for (var originalNumber in calculatedDFATable) {
|
|
var originalRow = calculatedDFATable[originalNumber];
|
|
var row = {};
|
|
|
|
for (var symbol in originalRow) {
|
|
row[symbol] = newStatesMap[originalRow[symbol]];
|
|
}
|
|
|
|
transitionTable[newStatesMap[originalNumber]] = row;
|
|
}
|
|
|
|
// Remap accepting states.
|
|
this._originalAcceptingStateNumbers = this._acceptingStateNumbers;
|
|
this._acceptingStateNumbers = new Set();
|
|
|
|
var _iteratorNormalCompletion5 = true;
|
|
var _didIteratorError5 = false;
|
|
var _iteratorError5 = undefined;
|
|
|
|
try {
|
|
for (var _iterator5 = this._originalAcceptingStateNumbers[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
|
var _originalNumber = _step5.value;
|
|
|
|
this._acceptingStateNumbers.add(newStatesMap[_originalNumber]);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError5 = true;
|
|
_iteratorError5 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
|
_iterator5.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError5) {
|
|
throw _iteratorError5;
|
|
}
|
|
}
|
|
}
|
|
|
|
return transitionTable;
|
|
}
|
|
|
|
/**
|
|
* Returns original DFA table, where state numbers
|
|
* are combined numbers from NFA.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getOriginalTransitionTable',
|
|
value: function getOriginalTransitionTable() {
|
|
if (!this._originalTransitionTable) {
|
|
// Original table is determined during table construction.
|
|
this.getTransitionTable();
|
|
}
|
|
return this._originalTransitionTable;
|
|
}
|
|
|
|
/**
|
|
* Checks whether this DFA accepts a string.
|
|
*/
|
|
|
|
}, {
|
|
key: 'matches',
|
|
value: function matches(string) {
|
|
var state = 1;
|
|
var i = 0;
|
|
var table = this.getTransitionTable();
|
|
|
|
while (string[i]) {
|
|
state = table[state][string[i++]];
|
|
if (!state) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!this.getAcceptingStateNumbers().has(state)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}]);
|
|
|
|
return DFA;
|
|
}();
|
|
|
|
var dfa = DFA$1;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* A generic FA State class (base for NFA and DFA).
|
|
*
|
|
* Maintains the transition map, and the flag whether
|
|
* the state is accepting.
|
|
*/
|
|
|
|
var _createClass$2 = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _classCallCheck$2(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var State$1 = function () {
|
|
function State() {
|
|
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
_ref$accepting = _ref.accepting,
|
|
accepting = _ref$accepting === undefined ? false : _ref$accepting;
|
|
|
|
_classCallCheck$2(this, State);
|
|
|
|
/**
|
|
* Outgoing transitions to other states.
|
|
*/
|
|
this._transitions = new Map();
|
|
|
|
/**
|
|
* Whether the state is accepting.
|
|
*/
|
|
this.accepting = accepting;
|
|
}
|
|
|
|
/**
|
|
* Returns transitions for this state.
|
|
*/
|
|
|
|
|
|
_createClass$2(State, [{
|
|
key: 'getTransitions',
|
|
value: function getTransitions() {
|
|
return this._transitions;
|
|
}
|
|
|
|
/**
|
|
* Creates a transition on symbol.
|
|
*/
|
|
|
|
}, {
|
|
key: 'addTransition',
|
|
value: function addTransition(symbol, toState) {
|
|
this.getTransitionsOnSymbol(symbol).add(toState);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Returns transitions set on symbol.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getTransitionsOnSymbol',
|
|
value: function getTransitionsOnSymbol(symbol) {
|
|
var transitions = this._transitions.get(symbol);
|
|
|
|
if (!transitions) {
|
|
transitions = new Set();
|
|
this._transitions.set(symbol, transitions);
|
|
}
|
|
|
|
return transitions;
|
|
}
|
|
}]);
|
|
|
|
return State;
|
|
}();
|
|
|
|
var state = State$1;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var _createClass$1 = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _classCallCheck$1(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
|
|
|
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
|
|
|
var State = state;
|
|
|
|
var _require$3 = specialSymbols,
|
|
EPSILON$1 = _require$3.EPSILON;
|
|
|
|
/**
|
|
* NFA state.
|
|
*
|
|
* Allows nondeterministic transitions to several states on the
|
|
* same symbol, and also epsilon-transitions.
|
|
*/
|
|
|
|
|
|
var NFAState$1 = function (_State) {
|
|
_inherits(NFAState, _State);
|
|
|
|
function NFAState() {
|
|
_classCallCheck$1(this, NFAState);
|
|
|
|
return _possibleConstructorReturn(this, (NFAState.__proto__ || Object.getPrototypeOf(NFAState)).apply(this, arguments));
|
|
}
|
|
|
|
_createClass$1(NFAState, [{
|
|
key: 'matches',
|
|
|
|
|
|
/**
|
|
* Whether this state matches a string.
|
|
*
|
|
* We maintain set of visited epsilon-states to avoid infinite loops
|
|
* when an epsilon-transition goes eventually to itself.
|
|
*
|
|
* NOTE: this function is rather "educational", since we use DFA for strings
|
|
* matching. DFA is built on top of NFA, and uses fast transition table.
|
|
*/
|
|
value: function matches(string) {
|
|
var visited = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Set();
|
|
|
|
// An epsilon-state has been visited, stop to avoid infinite loop.
|
|
if (visited.has(this)) {
|
|
return false;
|
|
}
|
|
|
|
visited.add(this);
|
|
|
|
// No symbols left..
|
|
if (string.length === 0) {
|
|
// .. and we're in the accepting state.
|
|
if (this.accepting) {
|
|
return true;
|
|
}
|
|
|
|
// Check if we can reach any accepting state from
|
|
// on the epsilon transitions.
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = this.getTransitionsOnSymbol(EPSILON$1)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var nextState = _step.value;
|
|
|
|
if (nextState.matches('', visited)) {
|
|
return true;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Else, we get some symbols.
|
|
var symbol = string[0];
|
|
var rest = string.slice(1);
|
|
|
|
var symbolTransitions = this.getTransitionsOnSymbol(symbol);
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = symbolTransitions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var _nextState = _step2.value;
|
|
|
|
if (_nextState.matches(rest)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If we couldn't match on symbol, check still epsilon-transitions
|
|
// without consuming the symbol (i.e. continue from `string`, not `rest`).
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = this.getTransitionsOnSymbol(EPSILON$1)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var _nextState2 = _step3.value;
|
|
|
|
if (_nextState2.matches(string, visited)) {
|
|
return true;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns an ε-closure for this state:
|
|
* self + all states following ε-transitions.
|
|
*/
|
|
|
|
}, {
|
|
key: 'getEpsilonClosure',
|
|
value: function getEpsilonClosure() {
|
|
var _this2 = this;
|
|
|
|
if (!this._epsilonClosure) {
|
|
(function () {
|
|
var epsilonTransitions = _this2.getTransitionsOnSymbol(EPSILON$1);
|
|
var closure = _this2._epsilonClosure = new Set();
|
|
closure.add(_this2);
|
|
var _iteratorNormalCompletion4 = true;
|
|
var _didIteratorError4 = false;
|
|
var _iteratorError4 = undefined;
|
|
|
|
try {
|
|
for (var _iterator4 = epsilonTransitions[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
|
var nextState = _step4.value;
|
|
|
|
if (!closure.has(nextState)) {
|
|
closure.add(nextState);
|
|
var nextClosure = nextState.getEpsilonClosure();
|
|
nextClosure.forEach(function (state) {
|
|
return closure.add(state);
|
|
});
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError4 = true;
|
|
_iteratorError4 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
|
_iterator4.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError4) {
|
|
throw _iteratorError4;
|
|
}
|
|
}
|
|
}
|
|
})();
|
|
}
|
|
|
|
return this._epsilonClosure;
|
|
}
|
|
}]);
|
|
|
|
return NFAState;
|
|
}(State);
|
|
|
|
var nfaState = NFAState$1;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var NFA$1 = nfa;
|
|
var NFAState = nfaState;
|
|
|
|
var _require$2 = specialSymbols,
|
|
EPSILON = _require$2.EPSILON;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Char NFA fragment: `c`
|
|
|
|
/**
|
|
* Char factory.
|
|
*
|
|
* Creates an NFA fragment for a single char.
|
|
*
|
|
* [in] --c--> [out]
|
|
*/
|
|
|
|
|
|
function char$1(c) {
|
|
var inState = new NFAState();
|
|
var outState = new NFAState({
|
|
accepting: true
|
|
});
|
|
|
|
return new NFA$1(inState.addTransition(c, outState), outState);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Epsilon NFA fragment
|
|
|
|
/**
|
|
* Epsilon factory.
|
|
*
|
|
* Creates an NFA fragment for ε (recognizes an empty string).
|
|
*
|
|
* [in] --ε--> [out]
|
|
*/
|
|
function e() {
|
|
return char$1(EPSILON);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Alteration NFA fragment: `abc`
|
|
|
|
/**
|
|
* Creates a connection between two NFA fragments on epsilon transition.
|
|
*
|
|
* [in-a] --a--> [out-a] --ε--> [in-b] --b--> [out-b]
|
|
*/
|
|
function altPair(first, second) {
|
|
first.out.accepting = false;
|
|
second.out.accepting = true;
|
|
|
|
first.out.addTransition(EPSILON, second.in);
|
|
|
|
return new NFA$1(first.in, second.out);
|
|
}
|
|
|
|
/**
|
|
* Alteration factory.
|
|
*
|
|
* Creates a alteration NFA for (at least) two NFA-fragments.
|
|
*/
|
|
function alt$1(first) {
|
|
for (var _len = arguments.length, fragments = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
fragments[_key - 1] = arguments[_key];
|
|
}
|
|
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = fragments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var fragment = _step.value;
|
|
|
|
first = altPair(first, fragment);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Disjunction NFA fragment: `a|b`
|
|
|
|
/**
|
|
* Creates a disjunction choice between two fragments.
|
|
*/
|
|
function orPair(first, second) {
|
|
var inState = new NFAState();
|
|
var outState = new NFAState();
|
|
|
|
inState.addTransition(EPSILON, first.in);
|
|
inState.addTransition(EPSILON, second.in);
|
|
|
|
outState.accepting = true;
|
|
first.out.accepting = false;
|
|
second.out.accepting = false;
|
|
|
|
first.out.addTransition(EPSILON, outState);
|
|
second.out.addTransition(EPSILON, outState);
|
|
|
|
return new NFA$1(inState, outState);
|
|
}
|
|
|
|
/**
|
|
* Disjunction factory.
|
|
*
|
|
* Creates a disjunction NFA for (at least) two NFA-fragments.
|
|
*/
|
|
function or$1(first) {
|
|
for (var _len2 = arguments.length, fragments = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
|
fragments[_key2 - 1] = arguments[_key2];
|
|
}
|
|
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = fragments[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var fragment = _step2.value;
|
|
|
|
first = orPair(first, fragment);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Kleene-closure
|
|
|
|
/**
|
|
* Kleene star/closure.
|
|
*
|
|
* a*
|
|
*/
|
|
function repExplicit(fragment) {
|
|
var inState = new NFAState();
|
|
var outState = new NFAState({
|
|
accepting: true
|
|
});
|
|
|
|
// 0 or more.
|
|
inState.addTransition(EPSILON, fragment.in);
|
|
inState.addTransition(EPSILON, outState);
|
|
|
|
fragment.out.accepting = false;
|
|
fragment.out.addTransition(EPSILON, outState);
|
|
outState.addTransition(EPSILON, fragment.in);
|
|
|
|
return new NFA$1(inState, outState);
|
|
}
|
|
|
|
/**
|
|
* Optimized Kleene-star: just adds ε-transitions from
|
|
* input to the output, and back.
|
|
*/
|
|
function rep$1(fragment) {
|
|
fragment.in.addTransition(EPSILON, fragment.out);
|
|
fragment.out.addTransition(EPSILON, fragment.in);
|
|
return fragment;
|
|
}
|
|
|
|
/**
|
|
* Optimized Plus: just adds ε-transitions from
|
|
* the output to the input.
|
|
*/
|
|
function plusRep$1(fragment) {
|
|
fragment.out.addTransition(EPSILON, fragment.in);
|
|
return fragment;
|
|
}
|
|
|
|
/**
|
|
* Optimized ? repetition: just adds ε-transitions from
|
|
* the input to the output.
|
|
*/
|
|
function questionRep$1(fragment) {
|
|
fragment.in.addTransition(EPSILON, fragment.out);
|
|
return fragment;
|
|
}
|
|
|
|
var builders$1 = {
|
|
alt: alt$1,
|
|
char: char$1,
|
|
e: e,
|
|
or: or$1,
|
|
rep: rep$1,
|
|
repExplicit: repExplicit,
|
|
plusRep: plusRep$1,
|
|
questionRep: questionRep$1
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
|
|
var parser$1 = parser$4;
|
|
|
|
var _require$1 = builders$1,
|
|
alt = _require$1.alt,
|
|
char = _require$1.char,
|
|
or = _require$1.or,
|
|
rep = _require$1.rep,
|
|
plusRep = _require$1.plusRep,
|
|
questionRep = _require$1.questionRep;
|
|
|
|
/**
|
|
* Helper `gen` function calls node type handler.
|
|
*/
|
|
|
|
|
|
function gen(node) {
|
|
if (node && !generator$1[node.type]) {
|
|
throw new Error(node.type + ' is not supported in NFA/DFA interpreter.');
|
|
}
|
|
|
|
return node ? generator$1[node.type](node) : '';
|
|
}
|
|
|
|
/**
|
|
* AST handler.
|
|
*/
|
|
var generator$1 = {
|
|
RegExp: function RegExp(node) {
|
|
if (node.flags !== '') {
|
|
throw new Error('NFA/DFA: Flags are not supported yet.');
|
|
}
|
|
|
|
return gen(node.body);
|
|
},
|
|
Alternative: function Alternative(node) {
|
|
var fragments = (node.expressions || []).map(gen);
|
|
return alt.apply(undefined, _toConsumableArray(fragments));
|
|
},
|
|
Disjunction: function Disjunction(node) {
|
|
return or(gen(node.left), gen(node.right));
|
|
},
|
|
Repetition: function Repetition(node) {
|
|
switch (node.quantifier.kind) {
|
|
case '*':
|
|
return rep(gen(node.expression));
|
|
case '+':
|
|
return plusRep(gen(node.expression));
|
|
case '?':
|
|
return questionRep(gen(node.expression));
|
|
default:
|
|
throw new Error('Unknown repeatition: ' + node.quantifier.kind + '.');
|
|
}
|
|
},
|
|
Char: function Char(node) {
|
|
if (node.kind !== 'simple') {
|
|
throw new Error('NFA/DFA: Only simple chars are supported yet.');
|
|
}
|
|
|
|
return char(node.value);
|
|
},
|
|
Group: function Group(node) {
|
|
return gen(node.expression);
|
|
}
|
|
};
|
|
|
|
var nfaFromRegexp = {
|
|
/**
|
|
* Builds an NFA from the passed regexp.
|
|
*/
|
|
build: function build(regexp) {
|
|
var ast = regexp;
|
|
|
|
if (regexp instanceof RegExp) {
|
|
regexp = '' + regexp;
|
|
}
|
|
|
|
if (typeof regexp === 'string') {
|
|
ast = parser$1.parse(regexp, {
|
|
captureLocations: true
|
|
});
|
|
}
|
|
|
|
return gen(ast);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var NFA = nfa;
|
|
var DFA = dfa;
|
|
|
|
var nfaFromRegExp = nfaFromRegexp;
|
|
var builders = builders$1;
|
|
|
|
var finiteAutomaton = {
|
|
|
|
/**
|
|
* Export NFA and DFA classes.
|
|
*/
|
|
NFA: NFA,
|
|
DFA: DFA,
|
|
|
|
/**
|
|
* Expose builders.
|
|
*/
|
|
builders: builders,
|
|
|
|
/**
|
|
* Builds an NFA for the passed regexp.
|
|
*
|
|
* @param string | AST | RegExp:
|
|
*
|
|
* a regular expression in different representations: a string,
|
|
* a RegExp object, or an AST.
|
|
*/
|
|
toNFA: function toNFA(regexp) {
|
|
return nfaFromRegExp.build(regexp);
|
|
},
|
|
|
|
|
|
/**
|
|
* Builds DFA for the passed regexp.
|
|
*
|
|
* @param string | AST | RegExp:
|
|
*
|
|
* a regular expression in different representations: a string,
|
|
* a RegExp object, or an AST.
|
|
*/
|
|
toDFA: function toDFA(regexp) {
|
|
return new DFA(this.toNFA(regexp));
|
|
},
|
|
|
|
|
|
/**
|
|
* Returns true if regexp accepts the string.
|
|
*/
|
|
test: function test(regexp, string) {
|
|
return this.toDFA(regexp).matches(string);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
/**
|
|
* The `RegExpTree` class provides runtime support for `compat-transpiler`
|
|
* module from `regexp-tree`.
|
|
*
|
|
* E.g. it tracks names of the capturing groups, in order to access the
|
|
* names on the matched result.
|
|
*
|
|
* It's a thin-wrapper on top of original regexp.
|
|
*/
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var RegExpTree$1 = function () {
|
|
/**
|
|
* Initializes a `RegExpTree` instance.
|
|
*
|
|
* @param RegExp - a regular expression
|
|
*
|
|
* @param Object state:
|
|
*
|
|
* An extra state which may store any related to transformation
|
|
* data, for example, names of the groups.
|
|
*
|
|
* - flags - original flags
|
|
* - groups - names of the groups, and their indices
|
|
* - source - original source
|
|
*/
|
|
function RegExpTree(re, _ref) {
|
|
var flags = _ref.flags,
|
|
groups = _ref.groups,
|
|
source = _ref.source;
|
|
|
|
_classCallCheck(this, RegExpTree);
|
|
|
|
this._re = re;
|
|
this._groups = groups;
|
|
|
|
// Original props.
|
|
this.flags = flags;
|
|
this.source = source || re.source;
|
|
this.dotAll = flags.includes('s');
|
|
|
|
// Inherited directly from `re`.
|
|
this.global = re.global;
|
|
this.ignoreCase = re.ignoreCase;
|
|
this.multiline = re.multiline;
|
|
this.sticky = re.sticky;
|
|
this.unicode = re.unicode;
|
|
}
|
|
|
|
/**
|
|
* Facade wrapper for RegExp `test` method.
|
|
*/
|
|
|
|
|
|
_createClass(RegExpTree, [{
|
|
key: 'test',
|
|
value: function test(string) {
|
|
return this._re.test(string);
|
|
}
|
|
|
|
/**
|
|
* Facade wrapper for RegExp `compile` method.
|
|
*/
|
|
|
|
}, {
|
|
key: 'compile',
|
|
value: function compile(string) {
|
|
return this._re.compile(string);
|
|
}
|
|
|
|
/**
|
|
* Facade wrapper for RegExp `toString` method.
|
|
*/
|
|
|
|
}, {
|
|
key: 'toString',
|
|
value: function toString() {
|
|
if (!this._toStringResult) {
|
|
this._toStringResult = '/' + this.source + '/' + this.flags;
|
|
}
|
|
return this._toStringResult;
|
|
}
|
|
|
|
/**
|
|
* Facade wrapper for RegExp `exec` method.
|
|
*/
|
|
|
|
}, {
|
|
key: 'exec',
|
|
value: function exec(string) {
|
|
var result = this._re.exec(string);
|
|
|
|
if (!this._groups || !result) {
|
|
return result;
|
|
}
|
|
|
|
result.groups = {};
|
|
|
|
for (var group in this._groups) {
|
|
var groupNumber = this._groups[group];
|
|
result.groups[group] = result[groupNumber];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}]);
|
|
|
|
return RegExpTree;
|
|
}();
|
|
|
|
var runtime = {
|
|
RegExpTree: RegExpTree$1
|
|
};
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var compatTranspiler = compatTranspiler$1;
|
|
var generator = generator_1;
|
|
var optimizer = optimizer$1;
|
|
var parser = parser$4;
|
|
var _transform = transform$1;
|
|
var _traverse = traverse$1;
|
|
var fa = finiteAutomaton;
|
|
|
|
var _require = runtime,
|
|
RegExpTree = _require.RegExpTree;
|
|
|
|
/**
|
|
* An API object for RegExp processing (parsing/transform/generation).
|
|
*/
|
|
|
|
|
|
var regexpTree$3 = {
|
|
/**
|
|
* Parser module exposed.
|
|
*/
|
|
parser: parser,
|
|
|
|
/**
|
|
* Expose finite-automaton module.
|
|
*/
|
|
fa: fa,
|
|
|
|
/**
|
|
* `TransformResult` exposed.
|
|
*/
|
|
TransformResult: _transform.TransformResult,
|
|
|
|
/**
|
|
* Parses a regexp string, producing an AST.
|
|
*
|
|
* @param string regexp
|
|
*
|
|
* a regular expression in different formats: string, AST, RegExp.
|
|
*
|
|
* @param Object options
|
|
*
|
|
* parsing options for this parse call. Default are:
|
|
*
|
|
* - captureLocations: boolean
|
|
* - any other custom options
|
|
*
|
|
* @return Object AST
|
|
*/
|
|
parse: function parse(regexp, options) {
|
|
return parser.parse('' + regexp, options);
|
|
},
|
|
|
|
|
|
/**
|
|
* Traverses a RegExp AST.
|
|
*
|
|
* @param Object ast
|
|
* @param Object | Array<Object> handlers
|
|
*
|
|
* Each `handler` is an object containing handler function for needed
|
|
* node types. Example:
|
|
*
|
|
* regexpTree.traverse(ast, {
|
|
* onChar(node) {
|
|
* ...
|
|
* },
|
|
* });
|
|
*
|
|
* The value for a node type may also be an object with functions pre and post.
|
|
* This enables more context-aware analyses, e.g. measuring star height.
|
|
*/
|
|
traverse: function traverse(ast, handlers, options) {
|
|
return _traverse.traverse(ast, handlers, options);
|
|
},
|
|
|
|
|
|
/**
|
|
* Transforms a regular expression.
|
|
*
|
|
* A regexp can be passed in different formats (string, regexp or AST),
|
|
* applying a set of transformations. It is a convenient wrapper
|
|
* on top of "parse-traverse-generate" tool chain.
|
|
*
|
|
* @param string | AST | RegExp regexp - a regular expression;
|
|
* @param Object | Array<Object> handlers - a list of handlers.
|
|
*
|
|
* @return TransformResult - a transformation result.
|
|
*/
|
|
transform: function transform(regexp, handlers) {
|
|
return _transform.transform(regexp, handlers);
|
|
},
|
|
|
|
|
|
/**
|
|
* Generates a RegExp string from an AST.
|
|
*
|
|
* @param Object ast
|
|
*
|
|
* Invariant:
|
|
*
|
|
* regexpTree.generate(regexpTree.parse('/[a-z]+/i')); // '/[a-z]+/i'
|
|
*/
|
|
generate: function generate(ast) {
|
|
return generator.generate(ast);
|
|
},
|
|
|
|
|
|
/**
|
|
* Creates a RegExp object from a regexp string.
|
|
*
|
|
* @param string regexp
|
|
*/
|
|
toRegExp: function toRegExp(regexp) {
|
|
var compat = this.compatTranspile(regexp);
|
|
return new RegExp(compat.getSource(), compat.getFlags());
|
|
},
|
|
|
|
|
|
/**
|
|
* Optimizes a regular expression by replacing some
|
|
* sub-expressions with their idiomatic patterns.
|
|
*
|
|
* @param string regexp
|
|
*
|
|
* @return TransformResult object
|
|
*/
|
|
optimize: function optimize(regexp, whitelist) {
|
|
var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
|
|
blacklist = _ref.blacklist;
|
|
|
|
return optimizer.optimize(regexp, { whitelist: whitelist, blacklist: blacklist });
|
|
},
|
|
|
|
|
|
/**
|
|
* Translates a regular expression in new syntax or in new format
|
|
* into equivalent expressions in old syntax.
|
|
*
|
|
* @param string regexp
|
|
*
|
|
* @return TransformResult object
|
|
*/
|
|
compatTranspile: function compatTranspile(regexp, whitelist) {
|
|
return compatTranspiler.transform(regexp, whitelist);
|
|
},
|
|
|
|
|
|
/**
|
|
* Executes a regular expression on a string.
|
|
*
|
|
* @param RegExp|string re - a regular expression.
|
|
* @param string string - a testing string.
|
|
*/
|
|
exec: function exec(re, string) {
|
|
if (typeof re === 'string') {
|
|
var compat = this.compatTranspile(re);
|
|
var extra = compat.getExtra();
|
|
|
|
if (extra.namedCapturingGroups) {
|
|
re = new RegExpTree(compat.toRegExp(), {
|
|
flags: compat.getFlags(),
|
|
source: compat.getSource(),
|
|
groups: extra.namedCapturingGroups
|
|
});
|
|
} else {
|
|
re = compat.toRegExp();
|
|
}
|
|
}
|
|
|
|
return re.exec(string);
|
|
}
|
|
};
|
|
|
|
var regexpTree_1 = regexpTree$3;
|
|
|
|
/**
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
|
|
*/
|
|
|
|
var regexpTree$2 = regexpTree_1;
|
|
|
|
// Exports an Analyzer subclass
|
|
|
|
const regexpTree$1 = regexpTree$2;
|
|
const analyzer$1 = analyzer$2;
|
|
|
|
class HeuristicAnalyzer extends analyzer$1.Analyzer {
|
|
constructor(analyzerOptions) {
|
|
super(analyzerOptions);
|
|
}
|
|
|
|
isVulnerable(regExp) {
|
|
// Heuristic #1: Star height > 1
|
|
const starHeight = this._measureStarHeight(regExp);
|
|
if (starHeight > 1) {
|
|
return true;
|
|
}
|
|
|
|
// Heuristic #2: # repetitions > limit
|
|
// TODO This is a poor heuristic
|
|
const nRepetitions = this._measureRepetitions(regExp);
|
|
if (nRepetitions > this.options.heuristic_replimit) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
genAttackString(regExp) {
|
|
return null;
|
|
}
|
|
|
|
_measureStarHeight(regExp) {
|
|
let currentStarHeight = 0;
|
|
let maxObservedStarHeight = 0;
|
|
|
|
const ast = regexpTree$1.parse(regExp);
|
|
|
|
regexpTree$1.traverse(ast, {
|
|
Repetition: {
|
|
pre({ node }) {
|
|
currentStarHeight++;
|
|
if (maxObservedStarHeight < currentStarHeight) {
|
|
maxObservedStarHeight = currentStarHeight;
|
|
}
|
|
},
|
|
|
|
post({ node }) {
|
|
currentStarHeight--;
|
|
}
|
|
}
|
|
});
|
|
|
|
return maxObservedStarHeight;
|
|
}
|
|
|
|
_measureRepetitions(regExp) {
|
|
let nRepetitions = 0;
|
|
|
|
const ast = regexpTree$1.parse(regExp);
|
|
regexpTree$1.traverse(ast, {
|
|
Repetition: {
|
|
pre({ node }) {
|
|
nRepetitions++;
|
|
}
|
|
}
|
|
});
|
|
|
|
return nRepetitions;
|
|
}
|
|
}
|
|
|
|
var heuristicAnalyzer$1 = HeuristicAnalyzer;
|
|
|
|
// Load the analyzers
|
|
const heuristicAnalyzer = heuristicAnalyzer$1;
|
|
|
|
var analyzerFamily$1 = [heuristicAnalyzer];
|
|
|
|
const analyzer = analyzer$2;
|
|
const analyzerFamily = analyzerFamily$1;
|
|
|
|
const DEFAULT_SAFE_REP_LIMIT = 25;
|
|
const RET_IS_SAFE = true;
|
|
const RET_IS_VULNERABLE = false;
|
|
|
|
class Args {
|
|
constructor(regExp, analyzerOptions) {
|
|
this.regExp = regExp;
|
|
this.analyzerOptions = analyzerOptions;
|
|
}
|
|
}
|
|
|
|
function safeRegex(re, opts) {
|
|
try {
|
|
const args = buildArgs(re, opts);
|
|
const analyzerResponses = askAnalyzersIfVulnerable(args);
|
|
|
|
// Did any analyzer say true?
|
|
if (analyzerResponses.find((isVulnerable) => isVulnerable)) {
|
|
return RET_IS_VULNERABLE;
|
|
} else {
|
|
return RET_IS_SAFE;
|
|
}
|
|
} catch (err) {
|
|
// Invalid or unparseable input
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function buildArgs(re, opts) {
|
|
// Build AnalyzerOptions
|
|
if (!opts) opts = {};
|
|
const heuristic_replimit = opts.limit === undefined ? DEFAULT_SAFE_REP_LIMIT : opts.limit;
|
|
|
|
const analyzerOptions = new analyzer.AnalyzerOptions(heuristic_replimit);
|
|
|
|
// Build RegExp
|
|
let regExp = null;
|
|
// Construct a RegExp object
|
|
if (re instanceof RegExp) {
|
|
regExp = re;
|
|
} else if (typeof re === 'string') {
|
|
regExp = new RegExp(re);
|
|
} else {
|
|
regExp = new RegExp(String(re));
|
|
}
|
|
|
|
return new Args(regExp, analyzerOptions);
|
|
}
|
|
|
|
function askAnalyzersIfVulnerable(args) {
|
|
let analyzerSaysVulnerable = [];
|
|
|
|
// Query the Analyzers
|
|
let Analyzer;
|
|
for (Analyzer of analyzerFamily) {
|
|
try {
|
|
const analyzer = new Analyzer(args.analyzerOptions);
|
|
analyzerSaysVulnerable.push(analyzer.isVulnerable(args.regExp));
|
|
} catch (err) {
|
|
/* istanbul ignore next */ // No need to worry about code coverage here.
|
|
analyzerSaysVulnerable.push(false);
|
|
}
|
|
}
|
|
|
|
return analyzerSaysVulnerable;
|
|
}
|
|
|
|
// Export
|
|
|
|
var safeRegex_1 = safeRegex;
|
|
|
|
var safeRegex$1 = /*@__PURE__*/getDefaultExportFromCjs(safeRegex_1);
|
|
|
|
class SettingTab extends obsidian.PluginSettingTab {
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
displSw(cont) {
|
|
cont.findAll(".setting-item").forEach((el) => {
|
|
if (el.getAttr("class").includes("media_folder_set")) {
|
|
if (this.plugin.settings.saveAttE === "obsFolder" ||
|
|
this.plugin.settings.saveAttE === "nextToNote") {
|
|
el.hide();
|
|
}
|
|
else {
|
|
el.show();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
display() {
|
|
let { containerEl } = this;
|
|
containerEl.empty();
|
|
containerEl.createEl("h1", { text: APP_TITLE });
|
|
containerEl.createEl("div");
|
|
// donheader.createEl("a", { text: "Support the project! ", href: "https://www.buymeacoffee.com/sergeikorneev", cls: "donheader_txt" })
|
|
containerEl.createEl("h3", { text: "Interface settings" });
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Show notifications")
|
|
.setDesc("Show notifications when pages were processed.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.showNotifications)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.showNotifications = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Disable additional commands")
|
|
.setDesc("Do not show additional commands in command palette. Reload the plugin in settings to take effect (turn off/on).")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.disAddCom)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.disAddCom = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
containerEl.createEl("h3", { text: "Processing settings" });
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Automatic processing")
|
|
.setDesc("Process notes on create/copy/paste.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.realTimeUpdate)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.realTimeUpdate = value;
|
|
yield this.plugin.saveSettings();
|
|
this.plugin.setupQueueInterval();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Automatic processing interval")
|
|
.setDesc("Interval in seconds for processing update. It takes some time to reveal changed content of a note to plugins.")
|
|
.addText((text) => text
|
|
.setValue(String(this.plugin.settings.realTimeUpdateInterval))
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
let numberValue = Number(value);
|
|
if (isNaN(numberValue) ||
|
|
!Number.isInteger(numberValue) ||
|
|
numberValue <= 5 ||
|
|
numberValue > 3600) {
|
|
displayError("The value should be a positive integer number between 5 and 3600!");
|
|
return;
|
|
}
|
|
if (numberValue < 5) {
|
|
numberValue = 5;
|
|
}
|
|
this.plugin.settings.realTimeUpdateInterval = numberValue;
|
|
yield this.plugin.saveSettings();
|
|
this.plugin.setupQueueInterval();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Number of retries for every single attachment")
|
|
.setDesc("If an error occurs during downloading (network etc.) try to re-download several times.")
|
|
.addText((text) => text
|
|
.setValue(String(this.plugin.settings.tryCount))
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
let numberValue = Number(value);
|
|
if (isNaN(numberValue) ||
|
|
!Number.isInteger(numberValue) ||
|
|
numberValue < 1 ||
|
|
numberValue > 6) {
|
|
displayError("The value should be a positive integer number between 1 and 6!");
|
|
return;
|
|
}
|
|
this.plugin.settings.tryCount = numberValue;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Process all new markdown files")
|
|
.setDesc("Process all new created/cloud-synced files with corresponding extensions.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.processCreated)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.processCreated = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Process all new attachments")
|
|
.setDesc("The plugin will also move all attachments from obsidian default folder to plugin folder.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.processAll)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.processAll = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Use MD5 for new attachments (Pasted images and files)")
|
|
.setDesc("The plugin will use MD5 when renaming all new attachments.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.useMD5ForNewAtt)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.useMD5ForNewAtt = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Download unknown filetypes")
|
|
.setDesc("Download unknown filetypes and save them with .unknown extension.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.downUnknown)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.downUnknown = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Convert PNG to JPEG (Web Images)")
|
|
.setDesc("Convert all downloaded PNG files to JPEG. May reduce file size by several times, but can also affect performance.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.PngToJpeg)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.PngToJpeg = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Convert PNG to JPEG (Pasted Images)")
|
|
.setDesc("Convert all pasted PNG files to JPEG. May reduce file size by several times, but can also affect performance.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.PngToJpegLocal)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.PngToJpegLocal = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Jpeg Quality")
|
|
.setDesc("Jpeg quality selection (30 to 100).")
|
|
.addText((text) => text
|
|
.setValue(String(this.plugin.settings.JpegQuality))
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
let numberValue = Number(value);
|
|
if (isNaN(numberValue) ||
|
|
!Number.isInteger(numberValue) ||
|
|
numberValue < 10 ||
|
|
numberValue > 100) {
|
|
displayError("The value should be a positive integer number between 10 and 100!");
|
|
return;
|
|
}
|
|
this.plugin.settings.JpegQuality = numberValue;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("File size lower limit in Kb")
|
|
.setDesc("Do not download files with size less than this value. Set 0 for no limit.")
|
|
.addText((text) => text
|
|
.setValue(String(this.plugin.settings.filesizeLimit))
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
let numberValue = Number(value);
|
|
if (isNaN(numberValue) ||
|
|
!Number.isInteger(numberValue) ||
|
|
numberValue < 0) {
|
|
displayError("The value should be a positive integer!");
|
|
return;
|
|
}
|
|
if (numberValue < 0) {
|
|
numberValue = 0;
|
|
}
|
|
this.plugin.settings.filesizeLimit = numberValue;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Exclusions")
|
|
.setDesc("The plugin will not download attachments with these extensions.")
|
|
.addText((text) => text
|
|
.setValue(this.plugin.settings.ignoredExt)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.ignoredExt = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Do not create Obsidian attachment folder (For compatibility with other plugins)")
|
|
.setDesc("The plugin will not create an Obsidian attachments folder. This may cause the plugin to behave incorrectly. ")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.DoNotCreateObsFolder)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.DoNotCreateObsFolder = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
containerEl.createEl("h3", { text: "Note settings" });
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Preserve link captions")
|
|
.setDesc("Add media links captions to converted tags.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.useCaptions)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.useCaptions = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Add original filename or 'Open file' tag")
|
|
.setDesc("Add [[original filename]] or [original filename](link to attachment) after replaced tag (only for file:// protocol or dropped/pasted files ).")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.addNameOfFile)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.addNameOfFile = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Include pattern")
|
|
.setDesc("Include only files with extensions only matching this pattern. Example: md|canvas")
|
|
.addText((text) => text.setValue(this.plugin.settings.includeps).onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
//Transform string to regex
|
|
let ExtArray = value.split("|");
|
|
if (ExtArray.length >= 1) {
|
|
let regexconverted = trimAny(ExtArray.map((extension) => { if (trimAny(extension, [" ", "|"]) !== "") {
|
|
return "(?<" + trimAny(extension, [" ", "|"]) + ">.*\\." + trimAny(extension, [" ", "|"]) + ")";
|
|
} }).join("|"), [" ", "|"]);
|
|
if (!safeRegex$1(value)) {
|
|
displayError("Unsafe regex! https://www.npmjs.com/package/safe-regex");
|
|
return;
|
|
}
|
|
this.plugin.settings.includepattern = regexconverted;
|
|
logError(regexconverted);
|
|
yield this.plugin.saveSettings();
|
|
}
|
|
})));
|
|
containerEl.createEl("h3", { text: "Orphaned attachments" });
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Remove files completely")
|
|
.setDesc("Do not move orphaned files into the garbage can.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.removeOrphansCompl)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.removeOrphansCompl = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
containerEl.createEl("h3", { text: "Media folder settings" });
|
|
new obsidian.Setting(containerEl)
|
|
.setName("How to write paths in tags")
|
|
.setDesc("Select whether to write full paths in tags or not.")
|
|
.addDropdown((text) => text
|
|
.addOption("fullDirPath", "Full path")
|
|
.addOption("onlyRelative", "Relative to note")
|
|
.addOption("baseFileName", "Only filename")
|
|
.setValue(this.plugin.settings.pathInTags)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.pathInTags = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Date format")
|
|
.setDesc("Date format for ${date} variable. E.g. \
|
|
| MMMM Do YYYY, h:mm:ss a (March 20th 2024, 10:54:46 am) \
|
|
| dddd (Wednesday)\
|
|
| MMM Do YY (Mar 20th 24)")
|
|
.addText((text) => text.setValue(this.plugin.settings.DateFormat).onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
if (value.match(/(\)|\(|\"|\'|\#|\]|\[|\:|\>|\<|\*|\|)/g) !== null) {
|
|
displayError("Unsafe folder name! Some chars are forbidden in some filesystems.");
|
|
return;
|
|
}
|
|
this.plugin.settings.DateFormat = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Folder to save new attachments")
|
|
.setDesc("Select where all new attachments will be saved.\nYou can use templates e.g. _resouces/${date}/${notename}")
|
|
.addDropdown((text) => text
|
|
.addOption("obsFolder", "Copy Obsidian settings")
|
|
.addOption("inFolderBelow", "In the root folder specified below")
|
|
.addOption("nextToNoteS", "Next to note in the folder specified below")
|
|
.setValue(this.plugin.settings.saveAttE)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.saveAttE = value;
|
|
this.displSw(containerEl);
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Move/delete/rename media folder")
|
|
.setDesc("Rename or move this folder to the obsidian or system garbage can when the associated note is deleted/renamed/moved. \
|
|
This setting takes effect only if the path contains ${notename} template at the end\
|
|
and the options 'Next to note in the folder specified below' / 'Relative to note' are selected.\
|
|
Use this setting at your own risk.")
|
|
.setClass("media_folder_set")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.removeMediaFolder)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
this.plugin.settings.removeMediaFolder = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Media folder")
|
|
.setDesc("Folder to keep all downloaded media files.")
|
|
.setClass("media_folder_set")
|
|
.addText((text) => text
|
|
.setValue(this.plugin.settings.mediaRootDir)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
if (value.match(/(\)|\(|\"|\'|\#|\]|\[|\:|\>|\<|\*|\|)/g) !== null) {
|
|
displayError("Unsafe folder name! Some chars are forbidden in some filesystems.");
|
|
return;
|
|
}
|
|
this.plugin.settings.mediaRootDir = value;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
containerEl.createEl("h3", { text: "Troubleshooting" });
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Debug")
|
|
.setDesc("Enable debug output to console.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(VERBOSE)
|
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
|
setDebug(value);
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
this.displSw(containerEl);
|
|
}
|
|
}
|
|
|
|
var AsyncLock$1 = function (opts) {
|
|
opts = opts || {};
|
|
|
|
this.Promise = opts.Promise || Promise;
|
|
|
|
// format: {key : [fn, fn]}
|
|
// queues[key] = null indicates no job running for key
|
|
this.queues = Object.create(null);
|
|
|
|
// lock is reentrant for same domain
|
|
this.domainReentrant = opts.domainReentrant || false;
|
|
if (this.domainReentrant) {
|
|
if (typeof process === 'undefined' || typeof process.domain === 'undefined') {
|
|
throw new Error(
|
|
'Domain-reentrant locks require `process.domain` to exist. Please flip `opts.domainReentrant = false`, ' +
|
|
'use a NodeJS version that still implements Domain, or install a browser polyfill.');
|
|
}
|
|
// domain of current running func {key : fn}
|
|
this.domains = Object.create(null);
|
|
}
|
|
|
|
this.timeout = opts.timeout || AsyncLock$1.DEFAULT_TIMEOUT;
|
|
this.maxOccupationTime = opts.maxOccupationTime || AsyncLock$1.DEFAULT_MAX_OCCUPATION_TIME;
|
|
this.maxExecutionTime = opts.maxExecutionTime || AsyncLock$1.DEFAULT_MAX_EXECUTION_TIME;
|
|
if (opts.maxPending === Infinity || (Number.isInteger(opts.maxPending) && opts.maxPending >= 0)) {
|
|
this.maxPending = opts.maxPending;
|
|
} else {
|
|
this.maxPending = AsyncLock$1.DEFAULT_MAX_PENDING;
|
|
}
|
|
};
|
|
|
|
AsyncLock$1.DEFAULT_TIMEOUT = 0; //Never
|
|
AsyncLock$1.DEFAULT_MAX_OCCUPATION_TIME = 0; //Never
|
|
AsyncLock$1.DEFAULT_MAX_EXECUTION_TIME = 0; //Never
|
|
AsyncLock$1.DEFAULT_MAX_PENDING = 1000;
|
|
|
|
/**
|
|
* Acquire Locks
|
|
*
|
|
* @param {String|Array} key resource key or keys to lock
|
|
* @param {function} fn async function
|
|
* @param {function} cb callback function, otherwise will return a promise
|
|
* @param {Object} opts options
|
|
*/
|
|
AsyncLock$1.prototype.acquire = function (key, fn, cb, opts) {
|
|
if (Array.isArray(key)) {
|
|
return this._acquireBatch(key, fn, cb, opts);
|
|
}
|
|
|
|
if (typeof (fn) !== 'function') {
|
|
throw new Error('You must pass a function to execute');
|
|
}
|
|
|
|
// faux-deferred promise using new Promise() (as Promise.defer is deprecated)
|
|
var deferredResolve = null;
|
|
var deferredReject = null;
|
|
var deferred = null;
|
|
|
|
if (typeof (cb) !== 'function') {
|
|
opts = cb;
|
|
cb = null;
|
|
|
|
// will return a promise
|
|
deferred = new this.Promise(function(resolve, reject) {
|
|
deferredResolve = resolve;
|
|
deferredReject = reject;
|
|
});
|
|
}
|
|
|
|
opts = opts || {};
|
|
|
|
var resolved = false;
|
|
var timer = null;
|
|
var occupationTimer = null;
|
|
var executionTimer = null;
|
|
var self = this;
|
|
|
|
var done = function (locked, err, ret) {
|
|
|
|
if (occupationTimer) {
|
|
clearTimeout(occupationTimer);
|
|
occupationTimer = null;
|
|
}
|
|
|
|
if (executionTimer) {
|
|
clearTimeout(executionTimer);
|
|
executionTimer = null;
|
|
}
|
|
|
|
if (locked) {
|
|
if (!!self.queues[key] && self.queues[key].length === 0) {
|
|
delete self.queues[key];
|
|
}
|
|
if (self.domainReentrant) {
|
|
delete self.domains[key];
|
|
}
|
|
}
|
|
|
|
if (!resolved) {
|
|
if (!deferred) {
|
|
if (typeof (cb) === 'function') {
|
|
cb(err, ret);
|
|
}
|
|
}
|
|
else {
|
|
//promise mode
|
|
if (err) {
|
|
deferredReject(err);
|
|
}
|
|
else {
|
|
deferredResolve(ret);
|
|
}
|
|
}
|
|
resolved = true;
|
|
}
|
|
|
|
if (locked) {
|
|
//run next func
|
|
if (!!self.queues[key] && self.queues[key].length > 0) {
|
|
self.queues[key].shift()();
|
|
}
|
|
}
|
|
};
|
|
|
|
var exec = function (locked) {
|
|
if (resolved) { // may due to timed out
|
|
return done(locked);
|
|
}
|
|
|
|
if (timer) {
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
|
|
if (self.domainReentrant && locked) {
|
|
self.domains[key] = process.domain;
|
|
}
|
|
|
|
var maxExecutionTime = opts.maxExecutionTime || self.maxExecutionTime;
|
|
if (maxExecutionTime) {
|
|
executionTimer = setTimeout(function () {
|
|
if (!!self.queues[key]) {
|
|
done(locked, new Error('Maximum execution time is exceeded ' + key));
|
|
}
|
|
}, maxExecutionTime);
|
|
}
|
|
|
|
// Callback mode
|
|
if (fn.length === 1) {
|
|
var called = false;
|
|
try {
|
|
fn(function (err, ret) {
|
|
if (!called) {
|
|
called = true;
|
|
done(locked, err, ret);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
// catching error thrown in user function fn
|
|
if (!called) {
|
|
called = true;
|
|
done(locked, err);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Promise mode
|
|
self._promiseTry(function () {
|
|
return fn();
|
|
})
|
|
.then(function(ret){
|
|
done(locked, undefined, ret);
|
|
}, function(error){
|
|
done(locked, error);
|
|
});
|
|
}
|
|
};
|
|
|
|
if (self.domainReentrant && !!process.domain) {
|
|
exec = process.domain.bind(exec);
|
|
}
|
|
|
|
var maxPending = opts.maxPending || self.maxPending;
|
|
|
|
if (!self.queues[key]) {
|
|
self.queues[key] = [];
|
|
exec(true);
|
|
}
|
|
else if (self.domainReentrant && !!process.domain && process.domain === self.domains[key]) {
|
|
// If code is in the same domain of current running task, run it directly
|
|
// Since lock is re-enterable
|
|
exec(false);
|
|
}
|
|
else if (self.queues[key].length >= maxPending) {
|
|
done(false, new Error('Too many pending tasks in queue ' + key));
|
|
}
|
|
else {
|
|
var taskFn = function () {
|
|
exec(true);
|
|
};
|
|
if (opts.skipQueue) {
|
|
self.queues[key].unshift(taskFn);
|
|
} else {
|
|
self.queues[key].push(taskFn);
|
|
}
|
|
|
|
var timeout = opts.timeout || self.timeout;
|
|
if (timeout) {
|
|
timer = setTimeout(function () {
|
|
timer = null;
|
|
done(false, new Error('async-lock timed out in queue ' + key));
|
|
}, timeout);
|
|
}
|
|
}
|
|
|
|
var maxOccupationTime = opts.maxOccupationTime || self.maxOccupationTime;
|
|
if (maxOccupationTime) {
|
|
occupationTimer = setTimeout(function () {
|
|
if (!!self.queues[key]) {
|
|
done(false, new Error('Maximum occupation time is exceeded in queue ' + key));
|
|
}
|
|
}, maxOccupationTime);
|
|
}
|
|
|
|
if (deferred) {
|
|
return deferred;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Below is how this function works:
|
|
*
|
|
* Equivalent code:
|
|
* self.acquire(key1, function(cb){
|
|
* self.acquire(key2, function(cb){
|
|
* self.acquire(key3, fn, cb);
|
|
* }, cb);
|
|
* }, cb);
|
|
*
|
|
* Equivalent code:
|
|
* var fn3 = getFn(key3, fn);
|
|
* var fn2 = getFn(key2, fn3);
|
|
* var fn1 = getFn(key1, fn2);
|
|
* fn1(cb);
|
|
*/
|
|
AsyncLock$1.prototype._acquireBatch = function (keys, fn, cb, opts) {
|
|
if (typeof (cb) !== 'function') {
|
|
opts = cb;
|
|
cb = null;
|
|
}
|
|
|
|
var self = this;
|
|
var getFn = function (key, fn) {
|
|
return function (cb) {
|
|
self.acquire(key, fn, cb, opts);
|
|
};
|
|
};
|
|
|
|
var fnx = keys.reduceRight(function (prev, key) {
|
|
return getFn(key, prev);
|
|
}, fn);
|
|
|
|
if (typeof (cb) === 'function') {
|
|
fnx(cb);
|
|
}
|
|
else {
|
|
return new this.Promise(function (resolve, reject) {
|
|
// check for promise mode in case keys is empty array
|
|
if (fnx.length === 1) {
|
|
fnx(function (err, ret) {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
else {
|
|
resolve(ret);
|
|
}
|
|
});
|
|
} else {
|
|
resolve(fnx());
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Whether there is any running or pending asyncFunc
|
|
*
|
|
* @param {String} key
|
|
*/
|
|
AsyncLock$1.prototype.isBusy = function (key) {
|
|
if (!key) {
|
|
return Object.keys(this.queues).length > 0;
|
|
}
|
|
else {
|
|
return !!this.queues[key];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Promise.try() implementation to become independent of Q-specific methods
|
|
*/
|
|
AsyncLock$1.prototype._promiseTry = function(fn) {
|
|
try {
|
|
return this.Promise.resolve(fn());
|
|
} catch (e) {
|
|
return this.Promise.reject(e);
|
|
}
|
|
};
|
|
|
|
var lib = AsyncLock$1;
|
|
|
|
var asyncLock = lib;
|
|
|
|
var AsyncLock = /*@__PURE__*/getDefaultExportFromCjs(asyncLock);
|
|
|
|
var moment$1 = {exports: {}};
|
|
|
|
moment$1.exports;
|
|
|
|
(function (module, exports) {
|
|
(function (global, factory) {
|
|
module.exports = factory() ;
|
|
}(commonjsGlobal, (function () {
|
|
var hookCallback;
|
|
|
|
function hooks() {
|
|
return hookCallback.apply(null, arguments);
|
|
}
|
|
|
|
// This is done to register the method called with moment()
|
|
// without creating circular dependencies.
|
|
function setHookCallback(callback) {
|
|
hookCallback = callback;
|
|
}
|
|
|
|
function isArray(input) {
|
|
return (
|
|
input instanceof Array ||
|
|
Object.prototype.toString.call(input) === '[object Array]'
|
|
);
|
|
}
|
|
|
|
function isObject(input) {
|
|
// IE8 will treat undefined and null as object if it wasn't for
|
|
// input != null
|
|
return (
|
|
input != null &&
|
|
Object.prototype.toString.call(input) === '[object Object]'
|
|
);
|
|
}
|
|
|
|
function hasOwnProp(a, b) {
|
|
return Object.prototype.hasOwnProperty.call(a, b);
|
|
}
|
|
|
|
function isObjectEmpty(obj) {
|
|
if (Object.getOwnPropertyNames) {
|
|
return Object.getOwnPropertyNames(obj).length === 0;
|
|
} else {
|
|
var k;
|
|
for (k in obj) {
|
|
if (hasOwnProp(obj, k)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function isUndefined(input) {
|
|
return input === void 0;
|
|
}
|
|
|
|
function isNumber(input) {
|
|
return (
|
|
typeof input === 'number' ||
|
|
Object.prototype.toString.call(input) === '[object Number]'
|
|
);
|
|
}
|
|
|
|
function isDate(input) {
|
|
return (
|
|
input instanceof Date ||
|
|
Object.prototype.toString.call(input) === '[object Date]'
|
|
);
|
|
}
|
|
|
|
function map(arr, fn) {
|
|
var res = [],
|
|
i,
|
|
arrLen = arr.length;
|
|
for (i = 0; i < arrLen; ++i) {
|
|
res.push(fn(arr[i], i));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function extend(a, b) {
|
|
for (var i in b) {
|
|
if (hasOwnProp(b, i)) {
|
|
a[i] = b[i];
|
|
}
|
|
}
|
|
|
|
if (hasOwnProp(b, 'toString')) {
|
|
a.toString = b.toString;
|
|
}
|
|
|
|
if (hasOwnProp(b, 'valueOf')) {
|
|
a.valueOf = b.valueOf;
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
function createUTC(input, format, locale, strict) {
|
|
return createLocalOrUTC(input, format, locale, strict, true).utc();
|
|
}
|
|
|
|
function defaultParsingFlags() {
|
|
// We need to deep clone this object.
|
|
return {
|
|
empty: false,
|
|
unusedTokens: [],
|
|
unusedInput: [],
|
|
overflow: -2,
|
|
charsLeftOver: 0,
|
|
nullInput: false,
|
|
invalidEra: null,
|
|
invalidMonth: null,
|
|
invalidFormat: false,
|
|
userInvalidated: false,
|
|
iso: false,
|
|
parsedDateParts: [],
|
|
era: null,
|
|
meridiem: null,
|
|
rfc2822: false,
|
|
weekdayMismatch: false,
|
|
};
|
|
}
|
|
|
|
function getParsingFlags(m) {
|
|
if (m._pf == null) {
|
|
m._pf = defaultParsingFlags();
|
|
}
|
|
return m._pf;
|
|
}
|
|
|
|
var some;
|
|
if (Array.prototype.some) {
|
|
some = Array.prototype.some;
|
|
} else {
|
|
some = function (fun) {
|
|
var t = Object(this),
|
|
len = t.length >>> 0,
|
|
i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (i in t && fun.call(this, t[i], i, t)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
}
|
|
|
|
function isValid(m) {
|
|
var flags = null,
|
|
parsedParts = false,
|
|
isNowValid = m._d && !isNaN(m._d.getTime());
|
|
if (isNowValid) {
|
|
flags = getParsingFlags(m);
|
|
parsedParts = some.call(flags.parsedDateParts, function (i) {
|
|
return i != null;
|
|
});
|
|
isNowValid =
|
|
flags.overflow < 0 &&
|
|
!flags.empty &&
|
|
!flags.invalidEra &&
|
|
!flags.invalidMonth &&
|
|
!flags.invalidWeekday &&
|
|
!flags.weekdayMismatch &&
|
|
!flags.nullInput &&
|
|
!flags.invalidFormat &&
|
|
!flags.userInvalidated &&
|
|
(!flags.meridiem || (flags.meridiem && parsedParts));
|
|
if (m._strict) {
|
|
isNowValid =
|
|
isNowValid &&
|
|
flags.charsLeftOver === 0 &&
|
|
flags.unusedTokens.length === 0 &&
|
|
flags.bigHour === undefined;
|
|
}
|
|
}
|
|
if (Object.isFrozen == null || !Object.isFrozen(m)) {
|
|
m._isValid = isNowValid;
|
|
} else {
|
|
return isNowValid;
|
|
}
|
|
return m._isValid;
|
|
}
|
|
|
|
function createInvalid(flags) {
|
|
var m = createUTC(NaN);
|
|
if (flags != null) {
|
|
extend(getParsingFlags(m), flags);
|
|
} else {
|
|
getParsingFlags(m).userInvalidated = true;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
// Plugins that add properties should also add the key here (null value),
|
|
// so we can properly clone ourselves.
|
|
var momentProperties = (hooks.momentProperties = []),
|
|
updateInProgress = false;
|
|
|
|
function copyConfig(to, from) {
|
|
var i,
|
|
prop,
|
|
val,
|
|
momentPropertiesLen = momentProperties.length;
|
|
|
|
if (!isUndefined(from._isAMomentObject)) {
|
|
to._isAMomentObject = from._isAMomentObject;
|
|
}
|
|
if (!isUndefined(from._i)) {
|
|
to._i = from._i;
|
|
}
|
|
if (!isUndefined(from._f)) {
|
|
to._f = from._f;
|
|
}
|
|
if (!isUndefined(from._l)) {
|
|
to._l = from._l;
|
|
}
|
|
if (!isUndefined(from._strict)) {
|
|
to._strict = from._strict;
|
|
}
|
|
if (!isUndefined(from._tzm)) {
|
|
to._tzm = from._tzm;
|
|
}
|
|
if (!isUndefined(from._isUTC)) {
|
|
to._isUTC = from._isUTC;
|
|
}
|
|
if (!isUndefined(from._offset)) {
|
|
to._offset = from._offset;
|
|
}
|
|
if (!isUndefined(from._pf)) {
|
|
to._pf = getParsingFlags(from);
|
|
}
|
|
if (!isUndefined(from._locale)) {
|
|
to._locale = from._locale;
|
|
}
|
|
|
|
if (momentPropertiesLen > 0) {
|
|
for (i = 0; i < momentPropertiesLen; i++) {
|
|
prop = momentProperties[i];
|
|
val = from[prop];
|
|
if (!isUndefined(val)) {
|
|
to[prop] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
return to;
|
|
}
|
|
|
|
// Moment prototype object
|
|
function Moment(config) {
|
|
copyConfig(this, config);
|
|
this._d = new Date(config._d != null ? config._d.getTime() : NaN);
|
|
if (!this.isValid()) {
|
|
this._d = new Date(NaN);
|
|
}
|
|
// Prevent infinite loop in case updateOffset creates new moment
|
|
// objects.
|
|
if (updateInProgress === false) {
|
|
updateInProgress = true;
|
|
hooks.updateOffset(this);
|
|
updateInProgress = false;
|
|
}
|
|
}
|
|
|
|
function isMoment(obj) {
|
|
return (
|
|
obj instanceof Moment || (obj != null && obj._isAMomentObject != null)
|
|
);
|
|
}
|
|
|
|
function warn(msg) {
|
|
if (
|
|
hooks.suppressDeprecationWarnings === false &&
|
|
typeof console !== 'undefined' &&
|
|
console.warn
|
|
) {
|
|
console.warn('Deprecation warning: ' + msg);
|
|
}
|
|
}
|
|
|
|
function deprecate(msg, fn) {
|
|
var firstTime = true;
|
|
|
|
return extend(function () {
|
|
if (hooks.deprecationHandler != null) {
|
|
hooks.deprecationHandler(null, msg);
|
|
}
|
|
if (firstTime) {
|
|
var args = [],
|
|
arg,
|
|
i,
|
|
key,
|
|
argLen = arguments.length;
|
|
for (i = 0; i < argLen; i++) {
|
|
arg = '';
|
|
if (typeof arguments[i] === 'object') {
|
|
arg += '\n[' + i + '] ';
|
|
for (key in arguments[0]) {
|
|
if (hasOwnProp(arguments[0], key)) {
|
|
arg += key + ': ' + arguments[0][key] + ', ';
|
|
}
|
|
}
|
|
arg = arg.slice(0, -2); // Remove trailing comma and space
|
|
} else {
|
|
arg = arguments[i];
|
|
}
|
|
args.push(arg);
|
|
}
|
|
warn(
|
|
msg +
|
|
'\nArguments: ' +
|
|
Array.prototype.slice.call(args).join('') +
|
|
'\n' +
|
|
new Error().stack
|
|
);
|
|
firstTime = false;
|
|
}
|
|
return fn.apply(this, arguments);
|
|
}, fn);
|
|
}
|
|
|
|
var deprecations = {};
|
|
|
|
function deprecateSimple(name, msg) {
|
|
if (hooks.deprecationHandler != null) {
|
|
hooks.deprecationHandler(name, msg);
|
|
}
|
|
if (!deprecations[name]) {
|
|
warn(msg);
|
|
deprecations[name] = true;
|
|
}
|
|
}
|
|
|
|
hooks.suppressDeprecationWarnings = false;
|
|
hooks.deprecationHandler = null;
|
|
|
|
function isFunction(input) {
|
|
return (
|
|
(typeof Function !== 'undefined' && input instanceof Function) ||
|
|
Object.prototype.toString.call(input) === '[object Function]'
|
|
);
|
|
}
|
|
|
|
function set(config) {
|
|
var prop, i;
|
|
for (i in config) {
|
|
if (hasOwnProp(config, i)) {
|
|
prop = config[i];
|
|
if (isFunction(prop)) {
|
|
this[i] = prop;
|
|
} else {
|
|
this['_' + i] = prop;
|
|
}
|
|
}
|
|
}
|
|
this._config = config;
|
|
// Lenient ordinal parsing accepts just a number in addition to
|
|
// number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
|
|
// TODO: Remove "ordinalParse" fallback in next major release.
|
|
this._dayOfMonthOrdinalParseLenient = new RegExp(
|
|
(this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) +
|
|
'|' +
|
|
/\d{1,2}/.source
|
|
);
|
|
}
|
|
|
|
function mergeConfigs(parentConfig, childConfig) {
|
|
var res = extend({}, parentConfig),
|
|
prop;
|
|
for (prop in childConfig) {
|
|
if (hasOwnProp(childConfig, prop)) {
|
|
if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
|
|
res[prop] = {};
|
|
extend(res[prop], parentConfig[prop]);
|
|
extend(res[prop], childConfig[prop]);
|
|
} else if (childConfig[prop] != null) {
|
|
res[prop] = childConfig[prop];
|
|
} else {
|
|
delete res[prop];
|
|
}
|
|
}
|
|
}
|
|
for (prop in parentConfig) {
|
|
if (
|
|
hasOwnProp(parentConfig, prop) &&
|
|
!hasOwnProp(childConfig, prop) &&
|
|
isObject(parentConfig[prop])
|
|
) {
|
|
// make sure changes to properties don't modify parent config
|
|
res[prop] = extend({}, res[prop]);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function Locale(config) {
|
|
if (config != null) {
|
|
this.set(config);
|
|
}
|
|
}
|
|
|
|
var keys;
|
|
|
|
if (Object.keys) {
|
|
keys = Object.keys;
|
|
} else {
|
|
keys = function (obj) {
|
|
var i,
|
|
res = [];
|
|
for (i in obj) {
|
|
if (hasOwnProp(obj, i)) {
|
|
res.push(i);
|
|
}
|
|
}
|
|
return res;
|
|
};
|
|
}
|
|
|
|
var defaultCalendar = {
|
|
sameDay: '[Today at] LT',
|
|
nextDay: '[Tomorrow at] LT',
|
|
nextWeek: 'dddd [at] LT',
|
|
lastDay: '[Yesterday at] LT',
|
|
lastWeek: '[Last] dddd [at] LT',
|
|
sameElse: 'L',
|
|
};
|
|
|
|
function calendar(key, mom, now) {
|
|
var output = this._calendar[key] || this._calendar['sameElse'];
|
|
return isFunction(output) ? output.call(mom, now) : output;
|
|
}
|
|
|
|
function zeroFill(number, targetLength, forceSign) {
|
|
var absNumber = '' + Math.abs(number),
|
|
zerosToFill = targetLength - absNumber.length,
|
|
sign = number >= 0;
|
|
return (
|
|
(sign ? (forceSign ? '+' : '') : '-') +
|
|
Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) +
|
|
absNumber
|
|
);
|
|
}
|
|
|
|
var formattingTokens =
|
|
/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,
|
|
localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,
|
|
formatFunctions = {},
|
|
formatTokenFunctions = {};
|
|
|
|
// token: 'M'
|
|
// padded: ['MM', 2]
|
|
// ordinal: 'Mo'
|
|
// callback: function () { this.month() + 1 }
|
|
function addFormatToken(token, padded, ordinal, callback) {
|
|
var func = callback;
|
|
if (typeof callback === 'string') {
|
|
func = function () {
|
|
return this[callback]();
|
|
};
|
|
}
|
|
if (token) {
|
|
formatTokenFunctions[token] = func;
|
|
}
|
|
if (padded) {
|
|
formatTokenFunctions[padded[0]] = function () {
|
|
return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
|
|
};
|
|
}
|
|
if (ordinal) {
|
|
formatTokenFunctions[ordinal] = function () {
|
|
return this.localeData().ordinal(
|
|
func.apply(this, arguments),
|
|
token
|
|
);
|
|
};
|
|
}
|
|
}
|
|
|
|
function removeFormattingTokens(input) {
|
|
if (input.match(/\[[\s\S]/)) {
|
|
return input.replace(/^\[|\]$/g, '');
|
|
}
|
|
return input.replace(/\\/g, '');
|
|
}
|
|
|
|
function makeFormatFunction(format) {
|
|
var array = format.match(formattingTokens),
|
|
i,
|
|
length;
|
|
|
|
for (i = 0, length = array.length; i < length; i++) {
|
|
if (formatTokenFunctions[array[i]]) {
|
|
array[i] = formatTokenFunctions[array[i]];
|
|
} else {
|
|
array[i] = removeFormattingTokens(array[i]);
|
|
}
|
|
}
|
|
|
|
return function (mom) {
|
|
var output = '',
|
|
i;
|
|
for (i = 0; i < length; i++) {
|
|
output += isFunction(array[i])
|
|
? array[i].call(mom, format)
|
|
: array[i];
|
|
}
|
|
return output;
|
|
};
|
|
}
|
|
|
|
// format date using native date object
|
|
function formatMoment(m, format) {
|
|
if (!m.isValid()) {
|
|
return m.localeData().invalidDate();
|
|
}
|
|
|
|
format = expandFormat(format, m.localeData());
|
|
formatFunctions[format] =
|
|
formatFunctions[format] || makeFormatFunction(format);
|
|
|
|
return formatFunctions[format](m);
|
|
}
|
|
|
|
function expandFormat(format, locale) {
|
|
var i = 5;
|
|
|
|
function replaceLongDateFormatTokens(input) {
|
|
return locale.longDateFormat(input) || input;
|
|
}
|
|
|
|
localFormattingTokens.lastIndex = 0;
|
|
while (i >= 0 && localFormattingTokens.test(format)) {
|
|
format = format.replace(
|
|
localFormattingTokens,
|
|
replaceLongDateFormatTokens
|
|
);
|
|
localFormattingTokens.lastIndex = 0;
|
|
i -= 1;
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
var defaultLongDateFormat = {
|
|
LTS: 'h:mm:ss A',
|
|
LT: 'h:mm A',
|
|
L: 'MM/DD/YYYY',
|
|
LL: 'MMMM D, YYYY',
|
|
LLL: 'MMMM D, YYYY h:mm A',
|
|
LLLL: 'dddd, MMMM D, YYYY h:mm A',
|
|
};
|
|
|
|
function longDateFormat(key) {
|
|
var format = this._longDateFormat[key],
|
|
formatUpper = this._longDateFormat[key.toUpperCase()];
|
|
|
|
if (format || !formatUpper) {
|
|
return format;
|
|
}
|
|
|
|
this._longDateFormat[key] = formatUpper
|
|
.match(formattingTokens)
|
|
.map(function (tok) {
|
|
if (
|
|
tok === 'MMMM' ||
|
|
tok === 'MM' ||
|
|
tok === 'DD' ||
|
|
tok === 'dddd'
|
|
) {
|
|
return tok.slice(1);
|
|
}
|
|
return tok;
|
|
})
|
|
.join('');
|
|
|
|
return this._longDateFormat[key];
|
|
}
|
|
|
|
var defaultInvalidDate = 'Invalid date';
|
|
|
|
function invalidDate() {
|
|
return this._invalidDate;
|
|
}
|
|
|
|
var defaultOrdinal = '%d',
|
|
defaultDayOfMonthOrdinalParse = /\d{1,2}/;
|
|
|
|
function ordinal(number) {
|
|
return this._ordinal.replace('%d', number);
|
|
}
|
|
|
|
var defaultRelativeTime = {
|
|
future: 'in %s',
|
|
past: '%s ago',
|
|
s: 'a few seconds',
|
|
ss: '%d seconds',
|
|
m: 'a minute',
|
|
mm: '%d minutes',
|
|
h: 'an hour',
|
|
hh: '%d hours',
|
|
d: 'a day',
|
|
dd: '%d days',
|
|
w: 'a week',
|
|
ww: '%d weeks',
|
|
M: 'a month',
|
|
MM: '%d months',
|
|
y: 'a year',
|
|
yy: '%d years',
|
|
};
|
|
|
|
function relativeTime(number, withoutSuffix, string, isFuture) {
|
|
var output = this._relativeTime[string];
|
|
return isFunction(output)
|
|
? output(number, withoutSuffix, string, isFuture)
|
|
: output.replace(/%d/i, number);
|
|
}
|
|
|
|
function pastFuture(diff, output) {
|
|
var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
|
|
return isFunction(format) ? format(output) : format.replace(/%s/i, output);
|
|
}
|
|
|
|
var aliases = {
|
|
D: 'date',
|
|
dates: 'date',
|
|
date: 'date',
|
|
d: 'day',
|
|
days: 'day',
|
|
day: 'day',
|
|
e: 'weekday',
|
|
weekdays: 'weekday',
|
|
weekday: 'weekday',
|
|
E: 'isoWeekday',
|
|
isoweekdays: 'isoWeekday',
|
|
isoweekday: 'isoWeekday',
|
|
DDD: 'dayOfYear',
|
|
dayofyears: 'dayOfYear',
|
|
dayofyear: 'dayOfYear',
|
|
h: 'hour',
|
|
hours: 'hour',
|
|
hour: 'hour',
|
|
ms: 'millisecond',
|
|
milliseconds: 'millisecond',
|
|
millisecond: 'millisecond',
|
|
m: 'minute',
|
|
minutes: 'minute',
|
|
minute: 'minute',
|
|
M: 'month',
|
|
months: 'month',
|
|
month: 'month',
|
|
Q: 'quarter',
|
|
quarters: 'quarter',
|
|
quarter: 'quarter',
|
|
s: 'second',
|
|
seconds: 'second',
|
|
second: 'second',
|
|
gg: 'weekYear',
|
|
weekyears: 'weekYear',
|
|
weekyear: 'weekYear',
|
|
GG: 'isoWeekYear',
|
|
isoweekyears: 'isoWeekYear',
|
|
isoweekyear: 'isoWeekYear',
|
|
w: 'week',
|
|
weeks: 'week',
|
|
week: 'week',
|
|
W: 'isoWeek',
|
|
isoweeks: 'isoWeek',
|
|
isoweek: 'isoWeek',
|
|
y: 'year',
|
|
years: 'year',
|
|
year: 'year',
|
|
};
|
|
|
|
function normalizeUnits(units) {
|
|
return typeof units === 'string'
|
|
? aliases[units] || aliases[units.toLowerCase()]
|
|
: undefined;
|
|
}
|
|
|
|
function normalizeObjectUnits(inputObject) {
|
|
var normalizedInput = {},
|
|
normalizedProp,
|
|
prop;
|
|
|
|
for (prop in inputObject) {
|
|
if (hasOwnProp(inputObject, prop)) {
|
|
normalizedProp = normalizeUnits(prop);
|
|
if (normalizedProp) {
|
|
normalizedInput[normalizedProp] = inputObject[prop];
|
|
}
|
|
}
|
|
}
|
|
|
|
return normalizedInput;
|
|
}
|
|
|
|
var priorities = {
|
|
date: 9,
|
|
day: 11,
|
|
weekday: 11,
|
|
isoWeekday: 11,
|
|
dayOfYear: 4,
|
|
hour: 13,
|
|
millisecond: 16,
|
|
minute: 14,
|
|
month: 8,
|
|
quarter: 7,
|
|
second: 15,
|
|
weekYear: 1,
|
|
isoWeekYear: 1,
|
|
week: 5,
|
|
isoWeek: 5,
|
|
year: 1,
|
|
};
|
|
|
|
function getPrioritizedUnits(unitsObj) {
|
|
var units = [],
|
|
u;
|
|
for (u in unitsObj) {
|
|
if (hasOwnProp(unitsObj, u)) {
|
|
units.push({ unit: u, priority: priorities[u] });
|
|
}
|
|
}
|
|
units.sort(function (a, b) {
|
|
return a.priority - b.priority;
|
|
});
|
|
return units;
|
|
}
|
|
|
|
var match1 = /\d/, // 0 - 9
|
|
match2 = /\d\d/, // 00 - 99
|
|
match3 = /\d{3}/, // 000 - 999
|
|
match4 = /\d{4}/, // 0000 - 9999
|
|
match6 = /[+-]?\d{6}/, // -999999 - 999999
|
|
match1to2 = /\d\d?/, // 0 - 99
|
|
match3to4 = /\d\d\d\d?/, // 999 - 9999
|
|
match5to6 = /\d\d\d\d\d\d?/, // 99999 - 999999
|
|
match1to3 = /\d{1,3}/, // 0 - 999
|
|
match1to4 = /\d{1,4}/, // 0 - 9999
|
|
match1to6 = /[+-]?\d{1,6}/, // -999999 - 999999
|
|
matchUnsigned = /\d+/, // 0 - inf
|
|
matchSigned = /[+-]?\d+/, // -inf - inf
|
|
matchOffset = /Z|[+-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
|
|
matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi, // +00 -00 +00:00 -00:00 +0000 -0000 or Z
|
|
matchTimestamp = /[+-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
|
|
// any word (or two) characters or numbers including two/three word month in arabic.
|
|
// includes scottish gaelic two word and hyphenated months
|
|
matchWord =
|
|
/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,
|
|
match1to2NoLeadingZero = /^[1-9]\d?/, // 1-99
|
|
match1to2HasZero = /^([1-9]\d|\d)/, // 0-99
|
|
regexes;
|
|
|
|
regexes = {};
|
|
|
|
function addRegexToken(token, regex, strictRegex) {
|
|
regexes[token] = isFunction(regex)
|
|
? regex
|
|
: function (isStrict, localeData) {
|
|
return isStrict && strictRegex ? strictRegex : regex;
|
|
};
|
|
}
|
|
|
|
function getParseRegexForToken(token, config) {
|
|
if (!hasOwnProp(regexes, token)) {
|
|
return new RegExp(unescapeFormat(token));
|
|
}
|
|
|
|
return regexes[token](config._strict, config._locale);
|
|
}
|
|
|
|
// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
|
|
function unescapeFormat(s) {
|
|
return regexEscape(
|
|
s
|
|
.replace('\\', '')
|
|
.replace(
|
|
/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,
|
|
function (matched, p1, p2, p3, p4) {
|
|
return p1 || p2 || p3 || p4;
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
function regexEscape(s) {
|
|
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
}
|
|
|
|
function absFloor(number) {
|
|
if (number < 0) {
|
|
// -0 -> 0
|
|
return Math.ceil(number) || 0;
|
|
} else {
|
|
return Math.floor(number);
|
|
}
|
|
}
|
|
|
|
function toInt(argumentForCoercion) {
|
|
var coercedNumber = +argumentForCoercion,
|
|
value = 0;
|
|
|
|
if (coercedNumber !== 0 && isFinite(coercedNumber)) {
|
|
value = absFloor(coercedNumber);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
var tokens = {};
|
|
|
|
function addParseToken(token, callback) {
|
|
var i,
|
|
func = callback,
|
|
tokenLen;
|
|
if (typeof token === 'string') {
|
|
token = [token];
|
|
}
|
|
if (isNumber(callback)) {
|
|
func = function (input, array) {
|
|
array[callback] = toInt(input);
|
|
};
|
|
}
|
|
tokenLen = token.length;
|
|
for (i = 0; i < tokenLen; i++) {
|
|
tokens[token[i]] = func;
|
|
}
|
|
}
|
|
|
|
function addWeekParseToken(token, callback) {
|
|
addParseToken(token, function (input, array, config, token) {
|
|
config._w = config._w || {};
|
|
callback(input, config._w, config, token);
|
|
});
|
|
}
|
|
|
|
function addTimeToArrayFromToken(token, input, config) {
|
|
if (input != null && hasOwnProp(tokens, token)) {
|
|
tokens[token](input, config._a, config, token);
|
|
}
|
|
}
|
|
|
|
function isLeapYear(year) {
|
|
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
}
|
|
|
|
var YEAR = 0,
|
|
MONTH = 1,
|
|
DATE = 2,
|
|
HOUR = 3,
|
|
MINUTE = 4,
|
|
SECOND = 5,
|
|
MILLISECOND = 6,
|
|
WEEK = 7,
|
|
WEEKDAY = 8;
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('Y', 0, 0, function () {
|
|
var y = this.year();
|
|
return y <= 9999 ? zeroFill(y, 4) : '+' + y;
|
|
});
|
|
|
|
addFormatToken(0, ['YY', 2], 0, function () {
|
|
return this.year() % 100;
|
|
});
|
|
|
|
addFormatToken(0, ['YYYY', 4], 0, 'year');
|
|
addFormatToken(0, ['YYYYY', 5], 0, 'year');
|
|
addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('Y', matchSigned);
|
|
addRegexToken('YY', match1to2, match2);
|
|
addRegexToken('YYYY', match1to4, match4);
|
|
addRegexToken('YYYYY', match1to6, match6);
|
|
addRegexToken('YYYYYY', match1to6, match6);
|
|
|
|
addParseToken(['YYYYY', 'YYYYYY'], YEAR);
|
|
addParseToken('YYYY', function (input, array) {
|
|
array[YEAR] =
|
|
input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
|
|
});
|
|
addParseToken('YY', function (input, array) {
|
|
array[YEAR] = hooks.parseTwoDigitYear(input);
|
|
});
|
|
addParseToken('Y', function (input, array) {
|
|
array[YEAR] = parseInt(input, 10);
|
|
});
|
|
|
|
// HELPERS
|
|
|
|
function daysInYear(year) {
|
|
return isLeapYear(year) ? 366 : 365;
|
|
}
|
|
|
|
// HOOKS
|
|
|
|
hooks.parseTwoDigitYear = function (input) {
|
|
return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
|
|
};
|
|
|
|
// MOMENTS
|
|
|
|
var getSetYear = makeGetSet('FullYear', true);
|
|
|
|
function getIsLeapYear() {
|
|
return isLeapYear(this.year());
|
|
}
|
|
|
|
function makeGetSet(unit, keepTime) {
|
|
return function (value) {
|
|
if (value != null) {
|
|
set$1(this, unit, value);
|
|
hooks.updateOffset(this, keepTime);
|
|
return this;
|
|
} else {
|
|
return get(this, unit);
|
|
}
|
|
};
|
|
}
|
|
|
|
function get(mom, unit) {
|
|
if (!mom.isValid()) {
|
|
return NaN;
|
|
}
|
|
|
|
var d = mom._d,
|
|
isUTC = mom._isUTC;
|
|
|
|
switch (unit) {
|
|
case 'Milliseconds':
|
|
return isUTC ? d.getUTCMilliseconds() : d.getMilliseconds();
|
|
case 'Seconds':
|
|
return isUTC ? d.getUTCSeconds() : d.getSeconds();
|
|
case 'Minutes':
|
|
return isUTC ? d.getUTCMinutes() : d.getMinutes();
|
|
case 'Hours':
|
|
return isUTC ? d.getUTCHours() : d.getHours();
|
|
case 'Date':
|
|
return isUTC ? d.getUTCDate() : d.getDate();
|
|
case 'Day':
|
|
return isUTC ? d.getUTCDay() : d.getDay();
|
|
case 'Month':
|
|
return isUTC ? d.getUTCMonth() : d.getMonth();
|
|
case 'FullYear':
|
|
return isUTC ? d.getUTCFullYear() : d.getFullYear();
|
|
default:
|
|
return NaN; // Just in case
|
|
}
|
|
}
|
|
|
|
function set$1(mom, unit, value) {
|
|
var d, isUTC, year, month, date;
|
|
|
|
if (!mom.isValid() || isNaN(value)) {
|
|
return;
|
|
}
|
|
|
|
d = mom._d;
|
|
isUTC = mom._isUTC;
|
|
|
|
switch (unit) {
|
|
case 'Milliseconds':
|
|
return void (isUTC
|
|
? d.setUTCMilliseconds(value)
|
|
: d.setMilliseconds(value));
|
|
case 'Seconds':
|
|
return void (isUTC ? d.setUTCSeconds(value) : d.setSeconds(value));
|
|
case 'Minutes':
|
|
return void (isUTC ? d.setUTCMinutes(value) : d.setMinutes(value));
|
|
case 'Hours':
|
|
return void (isUTC ? d.setUTCHours(value) : d.setHours(value));
|
|
case 'Date':
|
|
return void (isUTC ? d.setUTCDate(value) : d.setDate(value));
|
|
// case 'Day': // Not real
|
|
// return void (isUTC ? d.setUTCDay(value) : d.setDay(value));
|
|
// case 'Month': // Not used because we need to pass two variables
|
|
// return void (isUTC ? d.setUTCMonth(value) : d.setMonth(value));
|
|
case 'FullYear':
|
|
break; // See below ...
|
|
default:
|
|
return; // Just in case
|
|
}
|
|
|
|
year = value;
|
|
month = mom.month();
|
|
date = mom.date();
|
|
date = date === 29 && month === 1 && !isLeapYear(year) ? 28 : date;
|
|
void (isUTC
|
|
? d.setUTCFullYear(year, month, date)
|
|
: d.setFullYear(year, month, date));
|
|
}
|
|
|
|
// MOMENTS
|
|
|
|
function stringGet(units) {
|
|
units = normalizeUnits(units);
|
|
if (isFunction(this[units])) {
|
|
return this[units]();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
function stringSet(units, value) {
|
|
if (typeof units === 'object') {
|
|
units = normalizeObjectUnits(units);
|
|
var prioritized = getPrioritizedUnits(units),
|
|
i,
|
|
prioritizedLen = prioritized.length;
|
|
for (i = 0; i < prioritizedLen; i++) {
|
|
this[prioritized[i].unit](units[prioritized[i].unit]);
|
|
}
|
|
} else {
|
|
units = normalizeUnits(units);
|
|
if (isFunction(this[units])) {
|
|
return this[units](value);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
function mod(n, x) {
|
|
return ((n % x) + x) % x;
|
|
}
|
|
|
|
var indexOf;
|
|
|
|
if (Array.prototype.indexOf) {
|
|
indexOf = Array.prototype.indexOf;
|
|
} else {
|
|
indexOf = function (o) {
|
|
// I know
|
|
var i;
|
|
for (i = 0; i < this.length; ++i) {
|
|
if (this[i] === o) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
}
|
|
|
|
function daysInMonth(year, month) {
|
|
if (isNaN(year) || isNaN(month)) {
|
|
return NaN;
|
|
}
|
|
var modMonth = mod(month, 12);
|
|
year += (month - modMonth) / 12;
|
|
return modMonth === 1
|
|
? isLeapYear(year)
|
|
? 29
|
|
: 28
|
|
: 31 - ((modMonth % 7) % 2);
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('M', ['MM', 2], 'Mo', function () {
|
|
return this.month() + 1;
|
|
});
|
|
|
|
addFormatToken('MMM', 0, 0, function (format) {
|
|
return this.localeData().monthsShort(this, format);
|
|
});
|
|
|
|
addFormatToken('MMMM', 0, 0, function (format) {
|
|
return this.localeData().months(this, format);
|
|
});
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('M', match1to2, match1to2NoLeadingZero);
|
|
addRegexToken('MM', match1to2, match2);
|
|
addRegexToken('MMM', function (isStrict, locale) {
|
|
return locale.monthsShortRegex(isStrict);
|
|
});
|
|
addRegexToken('MMMM', function (isStrict, locale) {
|
|
return locale.monthsRegex(isStrict);
|
|
});
|
|
|
|
addParseToken(['M', 'MM'], function (input, array) {
|
|
array[MONTH] = toInt(input) - 1;
|
|
});
|
|
|
|
addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
|
|
var month = config._locale.monthsParse(input, token, config._strict);
|
|
// if we didn't find a month name, mark the date as invalid.
|
|
if (month != null) {
|
|
array[MONTH] = month;
|
|
} else {
|
|
getParsingFlags(config).invalidMonth = input;
|
|
}
|
|
});
|
|
|
|
// LOCALES
|
|
|
|
var defaultLocaleMonths =
|
|
'January_February_March_April_May_June_July_August_September_October_November_December'.split(
|
|
'_'
|
|
),
|
|
defaultLocaleMonthsShort =
|
|
'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
|
|
MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,
|
|
defaultMonthsShortRegex = matchWord,
|
|
defaultMonthsRegex = matchWord;
|
|
|
|
function localeMonths(m, format) {
|
|
if (!m) {
|
|
return isArray(this._months)
|
|
? this._months
|
|
: this._months['standalone'];
|
|
}
|
|
return isArray(this._months)
|
|
? this._months[m.month()]
|
|
: this._months[
|
|
(this._months.isFormat || MONTHS_IN_FORMAT).test(format)
|
|
? 'format'
|
|
: 'standalone'
|
|
][m.month()];
|
|
}
|
|
|
|
function localeMonthsShort(m, format) {
|
|
if (!m) {
|
|
return isArray(this._monthsShort)
|
|
? this._monthsShort
|
|
: this._monthsShort['standalone'];
|
|
}
|
|
return isArray(this._monthsShort)
|
|
? this._monthsShort[m.month()]
|
|
: this._monthsShort[
|
|
MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'
|
|
][m.month()];
|
|
}
|
|
|
|
function handleStrictParse(monthName, format, strict) {
|
|
var i,
|
|
ii,
|
|
mom,
|
|
llc = monthName.toLocaleLowerCase();
|
|
if (!this._monthsParse) {
|
|
// this is not used
|
|
this._monthsParse = [];
|
|
this._longMonthsParse = [];
|
|
this._shortMonthsParse = [];
|
|
for (i = 0; i < 12; ++i) {
|
|
mom = createUTC([2000, i]);
|
|
this._shortMonthsParse[i] = this.monthsShort(
|
|
mom,
|
|
''
|
|
).toLocaleLowerCase();
|
|
this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
|
|
}
|
|
}
|
|
|
|
if (strict) {
|
|
if (format === 'MMM') {
|
|
ii = indexOf.call(this._shortMonthsParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
} else {
|
|
ii = indexOf.call(this._longMonthsParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
}
|
|
} else {
|
|
if (format === 'MMM') {
|
|
ii = indexOf.call(this._shortMonthsParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._longMonthsParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
} else {
|
|
ii = indexOf.call(this._longMonthsParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._shortMonthsParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
}
|
|
}
|
|
}
|
|
|
|
function localeMonthsParse(monthName, format, strict) {
|
|
var i, mom, regex;
|
|
|
|
if (this._monthsParseExact) {
|
|
return handleStrictParse.call(this, monthName, format, strict);
|
|
}
|
|
|
|
if (!this._monthsParse) {
|
|
this._monthsParse = [];
|
|
this._longMonthsParse = [];
|
|
this._shortMonthsParse = [];
|
|
}
|
|
|
|
// TODO: add sorting
|
|
// Sorting makes sure if one month (or abbr) is a prefix of another
|
|
// see sorting in computeMonthsParse
|
|
for (i = 0; i < 12; i++) {
|
|
// make the regex if we don't have it already
|
|
mom = createUTC([2000, i]);
|
|
if (strict && !this._longMonthsParse[i]) {
|
|
this._longMonthsParse[i] = new RegExp(
|
|
'^' + this.months(mom, '').replace('.', '') + '$',
|
|
'i'
|
|
);
|
|
this._shortMonthsParse[i] = new RegExp(
|
|
'^' + this.monthsShort(mom, '').replace('.', '') + '$',
|
|
'i'
|
|
);
|
|
}
|
|
if (!strict && !this._monthsParse[i]) {
|
|
regex =
|
|
'^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
|
|
this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
|
|
}
|
|
// test the regex
|
|
if (
|
|
strict &&
|
|
format === 'MMMM' &&
|
|
this._longMonthsParse[i].test(monthName)
|
|
) {
|
|
return i;
|
|
} else if (
|
|
strict &&
|
|
format === 'MMM' &&
|
|
this._shortMonthsParse[i].test(monthName)
|
|
) {
|
|
return i;
|
|
} else if (!strict && this._monthsParse[i].test(monthName)) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// MOMENTS
|
|
|
|
function setMonth(mom, value) {
|
|
if (!mom.isValid()) {
|
|
// No op
|
|
return mom;
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
if (/^\d+$/.test(value)) {
|
|
value = toInt(value);
|
|
} else {
|
|
value = mom.localeData().monthsParse(value);
|
|
// TODO: Another silent failure?
|
|
if (!isNumber(value)) {
|
|
return mom;
|
|
}
|
|
}
|
|
}
|
|
|
|
var month = value,
|
|
date = mom.date();
|
|
|
|
date = date < 29 ? date : Math.min(date, daysInMonth(mom.year(), month));
|
|
void (mom._isUTC
|
|
? mom._d.setUTCMonth(month, date)
|
|
: mom._d.setMonth(month, date));
|
|
return mom;
|
|
}
|
|
|
|
function getSetMonth(value) {
|
|
if (value != null) {
|
|
setMonth(this, value);
|
|
hooks.updateOffset(this, true);
|
|
return this;
|
|
} else {
|
|
return get(this, 'Month');
|
|
}
|
|
}
|
|
|
|
function getDaysInMonth() {
|
|
return daysInMonth(this.year(), this.month());
|
|
}
|
|
|
|
function monthsShortRegex(isStrict) {
|
|
if (this._monthsParseExact) {
|
|
if (!hasOwnProp(this, '_monthsRegex')) {
|
|
computeMonthsParse.call(this);
|
|
}
|
|
if (isStrict) {
|
|
return this._monthsShortStrictRegex;
|
|
} else {
|
|
return this._monthsShortRegex;
|
|
}
|
|
} else {
|
|
if (!hasOwnProp(this, '_monthsShortRegex')) {
|
|
this._monthsShortRegex = defaultMonthsShortRegex;
|
|
}
|
|
return this._monthsShortStrictRegex && isStrict
|
|
? this._monthsShortStrictRegex
|
|
: this._monthsShortRegex;
|
|
}
|
|
}
|
|
|
|
function monthsRegex(isStrict) {
|
|
if (this._monthsParseExact) {
|
|
if (!hasOwnProp(this, '_monthsRegex')) {
|
|
computeMonthsParse.call(this);
|
|
}
|
|
if (isStrict) {
|
|
return this._monthsStrictRegex;
|
|
} else {
|
|
return this._monthsRegex;
|
|
}
|
|
} else {
|
|
if (!hasOwnProp(this, '_monthsRegex')) {
|
|
this._monthsRegex = defaultMonthsRegex;
|
|
}
|
|
return this._monthsStrictRegex && isStrict
|
|
? this._monthsStrictRegex
|
|
: this._monthsRegex;
|
|
}
|
|
}
|
|
|
|
function computeMonthsParse() {
|
|
function cmpLenRev(a, b) {
|
|
return b.length - a.length;
|
|
}
|
|
|
|
var shortPieces = [],
|
|
longPieces = [],
|
|
mixedPieces = [],
|
|
i,
|
|
mom,
|
|
shortP,
|
|
longP;
|
|
for (i = 0; i < 12; i++) {
|
|
// make the regex if we don't have it already
|
|
mom = createUTC([2000, i]);
|
|
shortP = regexEscape(this.monthsShort(mom, ''));
|
|
longP = regexEscape(this.months(mom, ''));
|
|
shortPieces.push(shortP);
|
|
longPieces.push(longP);
|
|
mixedPieces.push(longP);
|
|
mixedPieces.push(shortP);
|
|
}
|
|
// Sorting makes sure if one month (or abbr) is a prefix of another it
|
|
// will match the longer piece.
|
|
shortPieces.sort(cmpLenRev);
|
|
longPieces.sort(cmpLenRev);
|
|
mixedPieces.sort(cmpLenRev);
|
|
|
|
this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
|
|
this._monthsShortRegex = this._monthsRegex;
|
|
this._monthsStrictRegex = new RegExp(
|
|
'^(' + longPieces.join('|') + ')',
|
|
'i'
|
|
);
|
|
this._monthsShortStrictRegex = new RegExp(
|
|
'^(' + shortPieces.join('|') + ')',
|
|
'i'
|
|
);
|
|
}
|
|
|
|
function createDate(y, m, d, h, M, s, ms) {
|
|
// can't just apply() to create a date:
|
|
// https://stackoverflow.com/q/181348
|
|
var date;
|
|
// the date constructor remaps years 0-99 to 1900-1999
|
|
if (y < 100 && y >= 0) {
|
|
// preserve leap years using a full 400 year cycle, then reset
|
|
date = new Date(y + 400, m, d, h, M, s, ms);
|
|
if (isFinite(date.getFullYear())) {
|
|
date.setFullYear(y);
|
|
}
|
|
} else {
|
|
date = new Date(y, m, d, h, M, s, ms);
|
|
}
|
|
|
|
return date;
|
|
}
|
|
|
|
function createUTCDate(y) {
|
|
var date, args;
|
|
// the Date.UTC function remaps years 0-99 to 1900-1999
|
|
if (y < 100 && y >= 0) {
|
|
args = Array.prototype.slice.call(arguments);
|
|
// preserve leap years using a full 400 year cycle, then reset
|
|
args[0] = y + 400;
|
|
date = new Date(Date.UTC.apply(null, args));
|
|
if (isFinite(date.getUTCFullYear())) {
|
|
date.setUTCFullYear(y);
|
|
}
|
|
} else {
|
|
date = new Date(Date.UTC.apply(null, arguments));
|
|
}
|
|
|
|
return date;
|
|
}
|
|
|
|
// start-of-first-week - start-of-year
|
|
function firstWeekOffset(year, dow, doy) {
|
|
var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
|
|
fwd = 7 + dow - doy,
|
|
// first-week day local weekday -- which local weekday is fwd
|
|
fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
|
|
|
|
return -fwdlw + fwd - 1;
|
|
}
|
|
|
|
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
|
|
function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
|
|
var localWeekday = (7 + weekday - dow) % 7,
|
|
weekOffset = firstWeekOffset(year, dow, doy),
|
|
dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
|
|
resYear,
|
|
resDayOfYear;
|
|
|
|
if (dayOfYear <= 0) {
|
|
resYear = year - 1;
|
|
resDayOfYear = daysInYear(resYear) + dayOfYear;
|
|
} else if (dayOfYear > daysInYear(year)) {
|
|
resYear = year + 1;
|
|
resDayOfYear = dayOfYear - daysInYear(year);
|
|
} else {
|
|
resYear = year;
|
|
resDayOfYear = dayOfYear;
|
|
}
|
|
|
|
return {
|
|
year: resYear,
|
|
dayOfYear: resDayOfYear,
|
|
};
|
|
}
|
|
|
|
function weekOfYear(mom, dow, doy) {
|
|
var weekOffset = firstWeekOffset(mom.year(), dow, doy),
|
|
week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
|
|
resWeek,
|
|
resYear;
|
|
|
|
if (week < 1) {
|
|
resYear = mom.year() - 1;
|
|
resWeek = week + weeksInYear(resYear, dow, doy);
|
|
} else if (week > weeksInYear(mom.year(), dow, doy)) {
|
|
resWeek = week - weeksInYear(mom.year(), dow, doy);
|
|
resYear = mom.year() + 1;
|
|
} else {
|
|
resYear = mom.year();
|
|
resWeek = week;
|
|
}
|
|
|
|
return {
|
|
week: resWeek,
|
|
year: resYear,
|
|
};
|
|
}
|
|
|
|
function weeksInYear(year, dow, doy) {
|
|
var weekOffset = firstWeekOffset(year, dow, doy),
|
|
weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
|
|
return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('w', ['ww', 2], 'wo', 'week');
|
|
addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('w', match1to2, match1to2NoLeadingZero);
|
|
addRegexToken('ww', match1to2, match2);
|
|
addRegexToken('W', match1to2, match1to2NoLeadingZero);
|
|
addRegexToken('WW', match1to2, match2);
|
|
|
|
addWeekParseToken(
|
|
['w', 'ww', 'W', 'WW'],
|
|
function (input, week, config, token) {
|
|
week[token.substr(0, 1)] = toInt(input);
|
|
}
|
|
);
|
|
|
|
// HELPERS
|
|
|
|
// LOCALES
|
|
|
|
function localeWeek(mom) {
|
|
return weekOfYear(mom, this._week.dow, this._week.doy).week;
|
|
}
|
|
|
|
var defaultLocaleWeek = {
|
|
dow: 0, // Sunday is the first day of the week.
|
|
doy: 6, // The week that contains Jan 6th is the first week of the year.
|
|
};
|
|
|
|
function localeFirstDayOfWeek() {
|
|
return this._week.dow;
|
|
}
|
|
|
|
function localeFirstDayOfYear() {
|
|
return this._week.doy;
|
|
}
|
|
|
|
// MOMENTS
|
|
|
|
function getSetWeek(input) {
|
|
var week = this.localeData().week(this);
|
|
return input == null ? week : this.add((input - week) * 7, 'd');
|
|
}
|
|
|
|
function getSetISOWeek(input) {
|
|
var week = weekOfYear(this, 1, 4).week;
|
|
return input == null ? week : this.add((input - week) * 7, 'd');
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('d', 0, 'do', 'day');
|
|
|
|
addFormatToken('dd', 0, 0, function (format) {
|
|
return this.localeData().weekdaysMin(this, format);
|
|
});
|
|
|
|
addFormatToken('ddd', 0, 0, function (format) {
|
|
return this.localeData().weekdaysShort(this, format);
|
|
});
|
|
|
|
addFormatToken('dddd', 0, 0, function (format) {
|
|
return this.localeData().weekdays(this, format);
|
|
});
|
|
|
|
addFormatToken('e', 0, 0, 'weekday');
|
|
addFormatToken('E', 0, 0, 'isoWeekday');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('d', match1to2);
|
|
addRegexToken('e', match1to2);
|
|
addRegexToken('E', match1to2);
|
|
addRegexToken('dd', function (isStrict, locale) {
|
|
return locale.weekdaysMinRegex(isStrict);
|
|
});
|
|
addRegexToken('ddd', function (isStrict, locale) {
|
|
return locale.weekdaysShortRegex(isStrict);
|
|
});
|
|
addRegexToken('dddd', function (isStrict, locale) {
|
|
return locale.weekdaysRegex(isStrict);
|
|
});
|
|
|
|
addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
|
|
var weekday = config._locale.weekdaysParse(input, token, config._strict);
|
|
// if we didn't get a weekday name, mark the date as invalid
|
|
if (weekday != null) {
|
|
week.d = weekday;
|
|
} else {
|
|
getParsingFlags(config).invalidWeekday = input;
|
|
}
|
|
});
|
|
|
|
addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
|
|
week[token] = toInt(input);
|
|
});
|
|
|
|
// HELPERS
|
|
|
|
function parseWeekday(input, locale) {
|
|
if (typeof input !== 'string') {
|
|
return input;
|
|
}
|
|
|
|
if (!isNaN(input)) {
|
|
return parseInt(input, 10);
|
|
}
|
|
|
|
input = locale.weekdaysParse(input);
|
|
if (typeof input === 'number') {
|
|
return input;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function parseIsoWeekday(input, locale) {
|
|
if (typeof input === 'string') {
|
|
return locale.weekdaysParse(input) % 7 || 7;
|
|
}
|
|
return isNaN(input) ? null : input;
|
|
}
|
|
|
|
// LOCALES
|
|
function shiftWeekdays(ws, n) {
|
|
return ws.slice(n, 7).concat(ws.slice(0, n));
|
|
}
|
|
|
|
var defaultLocaleWeekdays =
|
|
'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
|
|
defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
|
|
defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
|
|
defaultWeekdaysRegex = matchWord,
|
|
defaultWeekdaysShortRegex = matchWord,
|
|
defaultWeekdaysMinRegex = matchWord;
|
|
|
|
function localeWeekdays(m, format) {
|
|
var weekdays = isArray(this._weekdays)
|
|
? this._weekdays
|
|
: this._weekdays[
|
|
m && m !== true && this._weekdays.isFormat.test(format)
|
|
? 'format'
|
|
: 'standalone'
|
|
];
|
|
return m === true
|
|
? shiftWeekdays(weekdays, this._week.dow)
|
|
: m
|
|
? weekdays[m.day()]
|
|
: weekdays;
|
|
}
|
|
|
|
function localeWeekdaysShort(m) {
|
|
return m === true
|
|
? shiftWeekdays(this._weekdaysShort, this._week.dow)
|
|
: m
|
|
? this._weekdaysShort[m.day()]
|
|
: this._weekdaysShort;
|
|
}
|
|
|
|
function localeWeekdaysMin(m) {
|
|
return m === true
|
|
? shiftWeekdays(this._weekdaysMin, this._week.dow)
|
|
: m
|
|
? this._weekdaysMin[m.day()]
|
|
: this._weekdaysMin;
|
|
}
|
|
|
|
function handleStrictParse$1(weekdayName, format, strict) {
|
|
var i,
|
|
ii,
|
|
mom,
|
|
llc = weekdayName.toLocaleLowerCase();
|
|
if (!this._weekdaysParse) {
|
|
this._weekdaysParse = [];
|
|
this._shortWeekdaysParse = [];
|
|
this._minWeekdaysParse = [];
|
|
|
|
for (i = 0; i < 7; ++i) {
|
|
mom = createUTC([2000, 1]).day(i);
|
|
this._minWeekdaysParse[i] = this.weekdaysMin(
|
|
mom,
|
|
''
|
|
).toLocaleLowerCase();
|
|
this._shortWeekdaysParse[i] = this.weekdaysShort(
|
|
mom,
|
|
''
|
|
).toLocaleLowerCase();
|
|
this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
|
|
}
|
|
}
|
|
|
|
if (strict) {
|
|
if (format === 'dddd') {
|
|
ii = indexOf.call(this._weekdaysParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
} else if (format === 'ddd') {
|
|
ii = indexOf.call(this._shortWeekdaysParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
} else {
|
|
ii = indexOf.call(this._minWeekdaysParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
}
|
|
} else {
|
|
if (format === 'dddd') {
|
|
ii = indexOf.call(this._weekdaysParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._shortWeekdaysParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._minWeekdaysParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
} else if (format === 'ddd') {
|
|
ii = indexOf.call(this._shortWeekdaysParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._weekdaysParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._minWeekdaysParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
} else {
|
|
ii = indexOf.call(this._minWeekdaysParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._weekdaysParse, llc);
|
|
if (ii !== -1) {
|
|
return ii;
|
|
}
|
|
ii = indexOf.call(this._shortWeekdaysParse, llc);
|
|
return ii !== -1 ? ii : null;
|
|
}
|
|
}
|
|
}
|
|
|
|
function localeWeekdaysParse(weekdayName, format, strict) {
|
|
var i, mom, regex;
|
|
|
|
if (this._weekdaysParseExact) {
|
|
return handleStrictParse$1.call(this, weekdayName, format, strict);
|
|
}
|
|
|
|
if (!this._weekdaysParse) {
|
|
this._weekdaysParse = [];
|
|
this._minWeekdaysParse = [];
|
|
this._shortWeekdaysParse = [];
|
|
this._fullWeekdaysParse = [];
|
|
}
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
// make the regex if we don't have it already
|
|
|
|
mom = createUTC([2000, 1]).day(i);
|
|
if (strict && !this._fullWeekdaysParse[i]) {
|
|
this._fullWeekdaysParse[i] = new RegExp(
|
|
'^' + this.weekdays(mom, '').replace('.', '\\.?') + '$',
|
|
'i'
|
|
);
|
|
this._shortWeekdaysParse[i] = new RegExp(
|
|
'^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$',
|
|
'i'
|
|
);
|
|
this._minWeekdaysParse[i] = new RegExp(
|
|
'^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$',
|
|
'i'
|
|
);
|
|
}
|
|
if (!this._weekdaysParse[i]) {
|
|
regex =
|
|
'^' +
|
|
this.weekdays(mom, '') +
|
|
'|^' +
|
|
this.weekdaysShort(mom, '') +
|
|
'|^' +
|
|
this.weekdaysMin(mom, '');
|
|
this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
|
|
}
|
|
// test the regex
|
|
if (
|
|
strict &&
|
|
format === 'dddd' &&
|
|
this._fullWeekdaysParse[i].test(weekdayName)
|
|
) {
|
|
return i;
|
|
} else if (
|
|
strict &&
|
|
format === 'ddd' &&
|
|
this._shortWeekdaysParse[i].test(weekdayName)
|
|
) {
|
|
return i;
|
|
} else if (
|
|
strict &&
|
|
format === 'dd' &&
|
|
this._minWeekdaysParse[i].test(weekdayName)
|
|
) {
|
|
return i;
|
|
} else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// MOMENTS
|
|
|
|
function getSetDayOfWeek(input) {
|
|
if (!this.isValid()) {
|
|
return input != null ? this : NaN;
|
|
}
|
|
|
|
var day = get(this, 'Day');
|
|
if (input != null) {
|
|
input = parseWeekday(input, this.localeData());
|
|
return this.add(input - day, 'd');
|
|
} else {
|
|
return day;
|
|
}
|
|
}
|
|
|
|
function getSetLocaleDayOfWeek(input) {
|
|
if (!this.isValid()) {
|
|
return input != null ? this : NaN;
|
|
}
|
|
var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
|
|
return input == null ? weekday : this.add(input - weekday, 'd');
|
|
}
|
|
|
|
function getSetISODayOfWeek(input) {
|
|
if (!this.isValid()) {
|
|
return input != null ? this : NaN;
|
|
}
|
|
|
|
// behaves the same as moment#day except
|
|
// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
|
|
// as a setter, sunday should belong to the previous week.
|
|
|
|
if (input != null) {
|
|
var weekday = parseIsoWeekday(input, this.localeData());
|
|
return this.day(this.day() % 7 ? weekday : weekday - 7);
|
|
} else {
|
|
return this.day() || 7;
|
|
}
|
|
}
|
|
|
|
function weekdaysRegex(isStrict) {
|
|
if (this._weekdaysParseExact) {
|
|
if (!hasOwnProp(this, '_weekdaysRegex')) {
|
|
computeWeekdaysParse.call(this);
|
|
}
|
|
if (isStrict) {
|
|
return this._weekdaysStrictRegex;
|
|
} else {
|
|
return this._weekdaysRegex;
|
|
}
|
|
} else {
|
|
if (!hasOwnProp(this, '_weekdaysRegex')) {
|
|
this._weekdaysRegex = defaultWeekdaysRegex;
|
|
}
|
|
return this._weekdaysStrictRegex && isStrict
|
|
? this._weekdaysStrictRegex
|
|
: this._weekdaysRegex;
|
|
}
|
|
}
|
|
|
|
function weekdaysShortRegex(isStrict) {
|
|
if (this._weekdaysParseExact) {
|
|
if (!hasOwnProp(this, '_weekdaysRegex')) {
|
|
computeWeekdaysParse.call(this);
|
|
}
|
|
if (isStrict) {
|
|
return this._weekdaysShortStrictRegex;
|
|
} else {
|
|
return this._weekdaysShortRegex;
|
|
}
|
|
} else {
|
|
if (!hasOwnProp(this, '_weekdaysShortRegex')) {
|
|
this._weekdaysShortRegex = defaultWeekdaysShortRegex;
|
|
}
|
|
return this._weekdaysShortStrictRegex && isStrict
|
|
? this._weekdaysShortStrictRegex
|
|
: this._weekdaysShortRegex;
|
|
}
|
|
}
|
|
|
|
function weekdaysMinRegex(isStrict) {
|
|
if (this._weekdaysParseExact) {
|
|
if (!hasOwnProp(this, '_weekdaysRegex')) {
|
|
computeWeekdaysParse.call(this);
|
|
}
|
|
if (isStrict) {
|
|
return this._weekdaysMinStrictRegex;
|
|
} else {
|
|
return this._weekdaysMinRegex;
|
|
}
|
|
} else {
|
|
if (!hasOwnProp(this, '_weekdaysMinRegex')) {
|
|
this._weekdaysMinRegex = defaultWeekdaysMinRegex;
|
|
}
|
|
return this._weekdaysMinStrictRegex && isStrict
|
|
? this._weekdaysMinStrictRegex
|
|
: this._weekdaysMinRegex;
|
|
}
|
|
}
|
|
|
|
function computeWeekdaysParse() {
|
|
function cmpLenRev(a, b) {
|
|
return b.length - a.length;
|
|
}
|
|
|
|
var minPieces = [],
|
|
shortPieces = [],
|
|
longPieces = [],
|
|
mixedPieces = [],
|
|
i,
|
|
mom,
|
|
minp,
|
|
shortp,
|
|
longp;
|
|
for (i = 0; i < 7; i++) {
|
|
// make the regex if we don't have it already
|
|
mom = createUTC([2000, 1]).day(i);
|
|
minp = regexEscape(this.weekdaysMin(mom, ''));
|
|
shortp = regexEscape(this.weekdaysShort(mom, ''));
|
|
longp = regexEscape(this.weekdays(mom, ''));
|
|
minPieces.push(minp);
|
|
shortPieces.push(shortp);
|
|
longPieces.push(longp);
|
|
mixedPieces.push(minp);
|
|
mixedPieces.push(shortp);
|
|
mixedPieces.push(longp);
|
|
}
|
|
// Sorting makes sure if one weekday (or abbr) is a prefix of another it
|
|
// will match the longer piece.
|
|
minPieces.sort(cmpLenRev);
|
|
shortPieces.sort(cmpLenRev);
|
|
longPieces.sort(cmpLenRev);
|
|
mixedPieces.sort(cmpLenRev);
|
|
|
|
this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
|
|
this._weekdaysShortRegex = this._weekdaysRegex;
|
|
this._weekdaysMinRegex = this._weekdaysRegex;
|
|
|
|
this._weekdaysStrictRegex = new RegExp(
|
|
'^(' + longPieces.join('|') + ')',
|
|
'i'
|
|
);
|
|
this._weekdaysShortStrictRegex = new RegExp(
|
|
'^(' + shortPieces.join('|') + ')',
|
|
'i'
|
|
);
|
|
this._weekdaysMinStrictRegex = new RegExp(
|
|
'^(' + minPieces.join('|') + ')',
|
|
'i'
|
|
);
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
function hFormat() {
|
|
return this.hours() % 12 || 12;
|
|
}
|
|
|
|
function kFormat() {
|
|
return this.hours() || 24;
|
|
}
|
|
|
|
addFormatToken('H', ['HH', 2], 0, 'hour');
|
|
addFormatToken('h', ['hh', 2], 0, hFormat);
|
|
addFormatToken('k', ['kk', 2], 0, kFormat);
|
|
|
|
addFormatToken('hmm', 0, 0, function () {
|
|
return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
|
|
});
|
|
|
|
addFormatToken('hmmss', 0, 0, function () {
|
|
return (
|
|
'' +
|
|
hFormat.apply(this) +
|
|
zeroFill(this.minutes(), 2) +
|
|
zeroFill(this.seconds(), 2)
|
|
);
|
|
});
|
|
|
|
addFormatToken('Hmm', 0, 0, function () {
|
|
return '' + this.hours() + zeroFill(this.minutes(), 2);
|
|
});
|
|
|
|
addFormatToken('Hmmss', 0, 0, function () {
|
|
return (
|
|
'' +
|
|
this.hours() +
|
|
zeroFill(this.minutes(), 2) +
|
|
zeroFill(this.seconds(), 2)
|
|
);
|
|
});
|
|
|
|
function meridiem(token, lowercase) {
|
|
addFormatToken(token, 0, 0, function () {
|
|
return this.localeData().meridiem(
|
|
this.hours(),
|
|
this.minutes(),
|
|
lowercase
|
|
);
|
|
});
|
|
}
|
|
|
|
meridiem('a', true);
|
|
meridiem('A', false);
|
|
|
|
// PARSING
|
|
|
|
function matchMeridiem(isStrict, locale) {
|
|
return locale._meridiemParse;
|
|
}
|
|
|
|
addRegexToken('a', matchMeridiem);
|
|
addRegexToken('A', matchMeridiem);
|
|
addRegexToken('H', match1to2, match1to2HasZero);
|
|
addRegexToken('h', match1to2, match1to2NoLeadingZero);
|
|
addRegexToken('k', match1to2, match1to2NoLeadingZero);
|
|
addRegexToken('HH', match1to2, match2);
|
|
addRegexToken('hh', match1to2, match2);
|
|
addRegexToken('kk', match1to2, match2);
|
|
|
|
addRegexToken('hmm', match3to4);
|
|
addRegexToken('hmmss', match5to6);
|
|
addRegexToken('Hmm', match3to4);
|
|
addRegexToken('Hmmss', match5to6);
|
|
|
|
addParseToken(['H', 'HH'], HOUR);
|
|
addParseToken(['k', 'kk'], function (input, array, config) {
|
|
var kInput = toInt(input);
|
|
array[HOUR] = kInput === 24 ? 0 : kInput;
|
|
});
|
|
addParseToken(['a', 'A'], function (input, array, config) {
|
|
config._isPm = config._locale.isPM(input);
|
|
config._meridiem = input;
|
|
});
|
|
addParseToken(['h', 'hh'], function (input, array, config) {
|
|
array[HOUR] = toInt(input);
|
|
getParsingFlags(config).bigHour = true;
|
|
});
|
|
addParseToken('hmm', function (input, array, config) {
|
|
var pos = input.length - 2;
|
|
array[HOUR] = toInt(input.substr(0, pos));
|
|
array[MINUTE] = toInt(input.substr(pos));
|
|
getParsingFlags(config).bigHour = true;
|
|
});
|
|
addParseToken('hmmss', function (input, array, config) {
|
|
var pos1 = input.length - 4,
|
|
pos2 = input.length - 2;
|
|
array[HOUR] = toInt(input.substr(0, pos1));
|
|
array[MINUTE] = toInt(input.substr(pos1, 2));
|
|
array[SECOND] = toInt(input.substr(pos2));
|
|
getParsingFlags(config).bigHour = true;
|
|
});
|
|
addParseToken('Hmm', function (input, array, config) {
|
|
var pos = input.length - 2;
|
|
array[HOUR] = toInt(input.substr(0, pos));
|
|
array[MINUTE] = toInt(input.substr(pos));
|
|
});
|
|
addParseToken('Hmmss', function (input, array, config) {
|
|
var pos1 = input.length - 4,
|
|
pos2 = input.length - 2;
|
|
array[HOUR] = toInt(input.substr(0, pos1));
|
|
array[MINUTE] = toInt(input.substr(pos1, 2));
|
|
array[SECOND] = toInt(input.substr(pos2));
|
|
});
|
|
|
|
// LOCALES
|
|
|
|
function localeIsPM(input) {
|
|
// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
|
|
// Using charAt should be more compatible.
|
|
return (input + '').toLowerCase().charAt(0) === 'p';
|
|
}
|
|
|
|
var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i,
|
|
// Setting the hour should keep the time, because the user explicitly
|
|
// specified which hour they want. So trying to maintain the same hour (in
|
|
// a new timezone) makes sense. Adding/subtracting hours does not follow
|
|
// this rule.
|
|
getSetHour = makeGetSet('Hours', true);
|
|
|
|
function localeMeridiem(hours, minutes, isLower) {
|
|
if (hours > 11) {
|
|
return isLower ? 'pm' : 'PM';
|
|
} else {
|
|
return isLower ? 'am' : 'AM';
|
|
}
|
|
}
|
|
|
|
var baseConfig = {
|
|
calendar: defaultCalendar,
|
|
longDateFormat: defaultLongDateFormat,
|
|
invalidDate: defaultInvalidDate,
|
|
ordinal: defaultOrdinal,
|
|
dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
|
|
relativeTime: defaultRelativeTime,
|
|
|
|
months: defaultLocaleMonths,
|
|
monthsShort: defaultLocaleMonthsShort,
|
|
|
|
week: defaultLocaleWeek,
|
|
|
|
weekdays: defaultLocaleWeekdays,
|
|
weekdaysMin: defaultLocaleWeekdaysMin,
|
|
weekdaysShort: defaultLocaleWeekdaysShort,
|
|
|
|
meridiemParse: defaultLocaleMeridiemParse,
|
|
};
|
|
|
|
// internal storage for locale config files
|
|
var locales = {},
|
|
localeFamilies = {},
|
|
globalLocale;
|
|
|
|
function commonPrefix(arr1, arr2) {
|
|
var i,
|
|
minl = Math.min(arr1.length, arr2.length);
|
|
for (i = 0; i < minl; i += 1) {
|
|
if (arr1[i] !== arr2[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
return minl;
|
|
}
|
|
|
|
function normalizeLocale(key) {
|
|
return key ? key.toLowerCase().replace('_', '-') : key;
|
|
}
|
|
|
|
// pick the locale from the array
|
|
// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
|
|
// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
|
|
function chooseLocale(names) {
|
|
var i = 0,
|
|
j,
|
|
next,
|
|
locale,
|
|
split;
|
|
|
|
while (i < names.length) {
|
|
split = normalizeLocale(names[i]).split('-');
|
|
j = split.length;
|
|
next = normalizeLocale(names[i + 1]);
|
|
next = next ? next.split('-') : null;
|
|
while (j > 0) {
|
|
locale = loadLocale(split.slice(0, j).join('-'));
|
|
if (locale) {
|
|
return locale;
|
|
}
|
|
if (
|
|
next &&
|
|
next.length >= j &&
|
|
commonPrefix(split, next) >= j - 1
|
|
) {
|
|
//the next array item is better than a shallower substring of this one
|
|
break;
|
|
}
|
|
j--;
|
|
}
|
|
i++;
|
|
}
|
|
return globalLocale;
|
|
}
|
|
|
|
function isLocaleNameSane(name) {
|
|
// Prevent names that look like filesystem paths, i.e contain '/' or '\'
|
|
// Ensure name is available and function returns boolean
|
|
return !!(name && name.match('^[^/\\\\]*$'));
|
|
}
|
|
|
|
function loadLocale(name) {
|
|
var oldLocale = null,
|
|
aliasedRequire;
|
|
// TODO: Find a better way to register and load all the locales in Node
|
|
if (
|
|
locales[name] === undefined &&
|
|
'object' !== 'undefined' &&
|
|
module &&
|
|
module.exports &&
|
|
isLocaleNameSane(name)
|
|
) {
|
|
try {
|
|
oldLocale = globalLocale._abbr;
|
|
aliasedRequire = commonjsRequire;
|
|
aliasedRequire('./locale/' + name);
|
|
getSetGlobalLocale(oldLocale);
|
|
} catch (e) {
|
|
// mark as not found to avoid repeating expensive file require call causing high CPU
|
|
// when trying to find en-US, en_US, en-us for every format call
|
|
locales[name] = null; // null means not found
|
|
}
|
|
}
|
|
return locales[name];
|
|
}
|
|
|
|
// This function will load locale and then set the global locale. If
|
|
// no arguments are passed in, it will simply return the current global
|
|
// locale key.
|
|
function getSetGlobalLocale(key, values) {
|
|
var data;
|
|
if (key) {
|
|
if (isUndefined(values)) {
|
|
data = getLocale(key);
|
|
} else {
|
|
data = defineLocale(key, values);
|
|
}
|
|
|
|
if (data) {
|
|
// moment.duration._locale = moment._locale = data;
|
|
globalLocale = data;
|
|
} else {
|
|
if (typeof console !== 'undefined' && console.warn) {
|
|
//warn user if arguments are passed but the locale could not be set
|
|
console.warn(
|
|
'Locale ' + key + ' not found. Did you forget to load it?'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return globalLocale._abbr;
|
|
}
|
|
|
|
function defineLocale(name, config) {
|
|
if (config !== null) {
|
|
var locale,
|
|
parentConfig = baseConfig;
|
|
config.abbr = name;
|
|
if (locales[name] != null) {
|
|
deprecateSimple(
|
|
'defineLocaleOverride',
|
|
'use moment.updateLocale(localeName, config) to change ' +
|
|
'an existing locale. moment.defineLocale(localeName, ' +
|
|
'config) should only be used for creating a new locale ' +
|
|
'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'
|
|
);
|
|
parentConfig = locales[name]._config;
|
|
} else if (config.parentLocale != null) {
|
|
if (locales[config.parentLocale] != null) {
|
|
parentConfig = locales[config.parentLocale]._config;
|
|
} else {
|
|
locale = loadLocale(config.parentLocale);
|
|
if (locale != null) {
|
|
parentConfig = locale._config;
|
|
} else {
|
|
if (!localeFamilies[config.parentLocale]) {
|
|
localeFamilies[config.parentLocale] = [];
|
|
}
|
|
localeFamilies[config.parentLocale].push({
|
|
name: name,
|
|
config: config,
|
|
});
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
locales[name] = new Locale(mergeConfigs(parentConfig, config));
|
|
|
|
if (localeFamilies[name]) {
|
|
localeFamilies[name].forEach(function (x) {
|
|
defineLocale(x.name, x.config);
|
|
});
|
|
}
|
|
|
|
// backwards compat for now: also set the locale
|
|
// make sure we set the locale AFTER all child locales have been
|
|
// created, so we won't end up with the child locale set.
|
|
getSetGlobalLocale(name);
|
|
|
|
return locales[name];
|
|
} else {
|
|
// useful for testing
|
|
delete locales[name];
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function updateLocale(name, config) {
|
|
if (config != null) {
|
|
var locale,
|
|
tmpLocale,
|
|
parentConfig = baseConfig;
|
|
|
|
if (locales[name] != null && locales[name].parentLocale != null) {
|
|
// Update existing child locale in-place to avoid memory-leaks
|
|
locales[name].set(mergeConfigs(locales[name]._config, config));
|
|
} else {
|
|
// MERGE
|
|
tmpLocale = loadLocale(name);
|
|
if (tmpLocale != null) {
|
|
parentConfig = tmpLocale._config;
|
|
}
|
|
config = mergeConfigs(parentConfig, config);
|
|
if (tmpLocale == null) {
|
|
// updateLocale is called for creating a new locale
|
|
// Set abbr so it will have a name (getters return
|
|
// undefined otherwise).
|
|
config.abbr = name;
|
|
}
|
|
locale = new Locale(config);
|
|
locale.parentLocale = locales[name];
|
|
locales[name] = locale;
|
|
}
|
|
|
|
// backwards compat for now: also set the locale
|
|
getSetGlobalLocale(name);
|
|
} else {
|
|
// pass null for config to unupdate, useful for tests
|
|
if (locales[name] != null) {
|
|
if (locales[name].parentLocale != null) {
|
|
locales[name] = locales[name].parentLocale;
|
|
if (name === getSetGlobalLocale()) {
|
|
getSetGlobalLocale(name);
|
|
}
|
|
} else if (locales[name] != null) {
|
|
delete locales[name];
|
|
}
|
|
}
|
|
}
|
|
return locales[name];
|
|
}
|
|
|
|
// returns locale data
|
|
function getLocale(key) {
|
|
var locale;
|
|
|
|
if (key && key._locale && key._locale._abbr) {
|
|
key = key._locale._abbr;
|
|
}
|
|
|
|
if (!key) {
|
|
return globalLocale;
|
|
}
|
|
|
|
if (!isArray(key)) {
|
|
//short-circuit everything else
|
|
locale = loadLocale(key);
|
|
if (locale) {
|
|
return locale;
|
|
}
|
|
key = [key];
|
|
}
|
|
|
|
return chooseLocale(key);
|
|
}
|
|
|
|
function listLocales() {
|
|
return keys(locales);
|
|
}
|
|
|
|
function checkOverflow(m) {
|
|
var overflow,
|
|
a = m._a;
|
|
|
|
if (a && getParsingFlags(m).overflow === -2) {
|
|
overflow =
|
|
a[MONTH] < 0 || a[MONTH] > 11
|
|
? MONTH
|
|
: a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH])
|
|
? DATE
|
|
: a[HOUR] < 0 ||
|
|
a[HOUR] > 24 ||
|
|
(a[HOUR] === 24 &&
|
|
(a[MINUTE] !== 0 ||
|
|
a[SECOND] !== 0 ||
|
|
a[MILLISECOND] !== 0))
|
|
? HOUR
|
|
: a[MINUTE] < 0 || a[MINUTE] > 59
|
|
? MINUTE
|
|
: a[SECOND] < 0 || a[SECOND] > 59
|
|
? SECOND
|
|
: a[MILLISECOND] < 0 || a[MILLISECOND] > 999
|
|
? MILLISECOND
|
|
: -1;
|
|
|
|
if (
|
|
getParsingFlags(m)._overflowDayOfYear &&
|
|
(overflow < YEAR || overflow > DATE)
|
|
) {
|
|
overflow = DATE;
|
|
}
|
|
if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
|
|
overflow = WEEK;
|
|
}
|
|
if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
|
|
overflow = WEEKDAY;
|
|
}
|
|
|
|
getParsingFlags(m).overflow = overflow;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
// iso 8601 regex
|
|
// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
|
|
var extendedIsoRegex =
|
|
/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
|
|
basicIsoRegex =
|
|
/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
|
|
tzRegex = /Z|[+-]\d\d(?::?\d\d)?/,
|
|
isoDates = [
|
|
['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
|
|
['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
|
|
['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
|
|
['GGGG-[W]WW', /\d{4}-W\d\d/, false],
|
|
['YYYY-DDD', /\d{4}-\d{3}/],
|
|
['YYYY-MM', /\d{4}-\d\d/, false],
|
|
['YYYYYYMMDD', /[+-]\d{10}/],
|
|
['YYYYMMDD', /\d{8}/],
|
|
['GGGG[W]WWE', /\d{4}W\d{3}/],
|
|
['GGGG[W]WW', /\d{4}W\d{2}/, false],
|
|
['YYYYDDD', /\d{7}/],
|
|
['YYYYMM', /\d{6}/, false],
|
|
['YYYY', /\d{4}/, false],
|
|
],
|
|
// iso time formats and regexes
|
|
isoTimes = [
|
|
['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
|
|
['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
|
|
['HH:mm:ss', /\d\d:\d\d:\d\d/],
|
|
['HH:mm', /\d\d:\d\d/],
|
|
['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
|
|
['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
|
|
['HHmmss', /\d\d\d\d\d\d/],
|
|
['HHmm', /\d\d\d\d/],
|
|
['HH', /\d\d/],
|
|
],
|
|
aspNetJsonRegex = /^\/?Date\((-?\d+)/i,
|
|
// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
|
|
rfc2822 =
|
|
/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,
|
|
obsOffsets = {
|
|
UT: 0,
|
|
GMT: 0,
|
|
EDT: -4 * 60,
|
|
EST: -5 * 60,
|
|
CDT: -5 * 60,
|
|
CST: -6 * 60,
|
|
MDT: -6 * 60,
|
|
MST: -7 * 60,
|
|
PDT: -7 * 60,
|
|
PST: -8 * 60,
|
|
};
|
|
|
|
// date from iso format
|
|
function configFromISO(config) {
|
|
var i,
|
|
l,
|
|
string = config._i,
|
|
match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
|
|
allowTime,
|
|
dateFormat,
|
|
timeFormat,
|
|
tzFormat,
|
|
isoDatesLen = isoDates.length,
|
|
isoTimesLen = isoTimes.length;
|
|
|
|
if (match) {
|
|
getParsingFlags(config).iso = true;
|
|
for (i = 0, l = isoDatesLen; i < l; i++) {
|
|
if (isoDates[i][1].exec(match[1])) {
|
|
dateFormat = isoDates[i][0];
|
|
allowTime = isoDates[i][2] !== false;
|
|
break;
|
|
}
|
|
}
|
|
if (dateFormat == null) {
|
|
config._isValid = false;
|
|
return;
|
|
}
|
|
if (match[3]) {
|
|
for (i = 0, l = isoTimesLen; i < l; i++) {
|
|
if (isoTimes[i][1].exec(match[3])) {
|
|
// match[2] should be 'T' or space
|
|
timeFormat = (match[2] || ' ') + isoTimes[i][0];
|
|
break;
|
|
}
|
|
}
|
|
if (timeFormat == null) {
|
|
config._isValid = false;
|
|
return;
|
|
}
|
|
}
|
|
if (!allowTime && timeFormat != null) {
|
|
config._isValid = false;
|
|
return;
|
|
}
|
|
if (match[4]) {
|
|
if (tzRegex.exec(match[4])) {
|
|
tzFormat = 'Z';
|
|
} else {
|
|
config._isValid = false;
|
|
return;
|
|
}
|
|
}
|
|
config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
|
|
configFromStringAndFormat(config);
|
|
} else {
|
|
config._isValid = false;
|
|
}
|
|
}
|
|
|
|
function extractFromRFC2822Strings(
|
|
yearStr,
|
|
monthStr,
|
|
dayStr,
|
|
hourStr,
|
|
minuteStr,
|
|
secondStr
|
|
) {
|
|
var result = [
|
|
untruncateYear(yearStr),
|
|
defaultLocaleMonthsShort.indexOf(monthStr),
|
|
parseInt(dayStr, 10),
|
|
parseInt(hourStr, 10),
|
|
parseInt(minuteStr, 10),
|
|
];
|
|
|
|
if (secondStr) {
|
|
result.push(parseInt(secondStr, 10));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function untruncateYear(yearStr) {
|
|
var year = parseInt(yearStr, 10);
|
|
if (year <= 49) {
|
|
return 2000 + year;
|
|
} else if (year <= 999) {
|
|
return 1900 + year;
|
|
}
|
|
return year;
|
|
}
|
|
|
|
function preprocessRFC2822(s) {
|
|
// Remove comments and folding whitespace and replace multiple-spaces with a single space
|
|
return s
|
|
.replace(/\([^()]*\)|[\n\t]/g, ' ')
|
|
.replace(/(\s\s+)/g, ' ')
|
|
.replace(/^\s\s*/, '')
|
|
.replace(/\s\s*$/, '');
|
|
}
|
|
|
|
function checkWeekday(weekdayStr, parsedInput, config) {
|
|
if (weekdayStr) {
|
|
// TODO: Replace the vanilla JS Date object with an independent day-of-week check.
|
|
var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
|
|
weekdayActual = new Date(
|
|
parsedInput[0],
|
|
parsedInput[1],
|
|
parsedInput[2]
|
|
).getDay();
|
|
if (weekdayProvided !== weekdayActual) {
|
|
getParsingFlags(config).weekdayMismatch = true;
|
|
config._isValid = false;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function calculateOffset(obsOffset, militaryOffset, numOffset) {
|
|
if (obsOffset) {
|
|
return obsOffsets[obsOffset];
|
|
} else if (militaryOffset) {
|
|
// the only allowed military tz is Z
|
|
return 0;
|
|
} else {
|
|
var hm = parseInt(numOffset, 10),
|
|
m = hm % 100,
|
|
h = (hm - m) / 100;
|
|
return h * 60 + m;
|
|
}
|
|
}
|
|
|
|
// date and time from ref 2822 format
|
|
function configFromRFC2822(config) {
|
|
var match = rfc2822.exec(preprocessRFC2822(config._i)),
|
|
parsedArray;
|
|
if (match) {
|
|
parsedArray = extractFromRFC2822Strings(
|
|
match[4],
|
|
match[3],
|
|
match[2],
|
|
match[5],
|
|
match[6],
|
|
match[7]
|
|
);
|
|
if (!checkWeekday(match[1], parsedArray, config)) {
|
|
return;
|
|
}
|
|
|
|
config._a = parsedArray;
|
|
config._tzm = calculateOffset(match[8], match[9], match[10]);
|
|
|
|
config._d = createUTCDate.apply(null, config._a);
|
|
config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
|
|
|
|
getParsingFlags(config).rfc2822 = true;
|
|
} else {
|
|
config._isValid = false;
|
|
}
|
|
}
|
|
|
|
// date from 1) ASP.NET, 2) ISO, 3) RFC 2822 formats, or 4) optional fallback if parsing isn't strict
|
|
function configFromString(config) {
|
|
var matched = aspNetJsonRegex.exec(config._i);
|
|
if (matched !== null) {
|
|
config._d = new Date(+matched[1]);
|
|
return;
|
|
}
|
|
|
|
configFromISO(config);
|
|
if (config._isValid === false) {
|
|
delete config._isValid;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
configFromRFC2822(config);
|
|
if (config._isValid === false) {
|
|
delete config._isValid;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
if (config._strict) {
|
|
config._isValid = false;
|
|
} else {
|
|
// Final attempt, use Input Fallback
|
|
hooks.createFromInputFallback(config);
|
|
}
|
|
}
|
|
|
|
hooks.createFromInputFallback = deprecate(
|
|
'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' +
|
|
'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' +
|
|
'discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.',
|
|
function (config) {
|
|
config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
|
|
}
|
|
);
|
|
|
|
// Pick the first defined of two or three arguments.
|
|
function defaults(a, b, c) {
|
|
if (a != null) {
|
|
return a;
|
|
}
|
|
if (b != null) {
|
|
return b;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
function currentDateArray(config) {
|
|
// hooks is actually the exported moment object
|
|
var nowValue = new Date(hooks.now());
|
|
if (config._useUTC) {
|
|
return [
|
|
nowValue.getUTCFullYear(),
|
|
nowValue.getUTCMonth(),
|
|
nowValue.getUTCDate(),
|
|
];
|
|
}
|
|
return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
|
|
}
|
|
|
|
// convert an array to a date.
|
|
// the array should mirror the parameters below
|
|
// note: all values past the year are optional and will default to the lowest possible value.
|
|
// [year, month, day , hour, minute, second, millisecond]
|
|
function configFromArray(config) {
|
|
var i,
|
|
date,
|
|
input = [],
|
|
currentDate,
|
|
expectedWeekday,
|
|
yearToUse;
|
|
|
|
if (config._d) {
|
|
return;
|
|
}
|
|
|
|
currentDate = currentDateArray(config);
|
|
|
|
//compute day of the year from weeks and weekdays
|
|
if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
|
|
dayOfYearFromWeekInfo(config);
|
|
}
|
|
|
|
//if the day of the year is set, figure out what it is
|
|
if (config._dayOfYear != null) {
|
|
yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
|
|
|
|
if (
|
|
config._dayOfYear > daysInYear(yearToUse) ||
|
|
config._dayOfYear === 0
|
|
) {
|
|
getParsingFlags(config)._overflowDayOfYear = true;
|
|
}
|
|
|
|
date = createUTCDate(yearToUse, 0, config._dayOfYear);
|
|
config._a[MONTH] = date.getUTCMonth();
|
|
config._a[DATE] = date.getUTCDate();
|
|
}
|
|
|
|
// Default to current date.
|
|
// * if no year, month, day of month are given, default to today
|
|
// * if day of month is given, default month and year
|
|
// * if month is given, default only year
|
|
// * if year is given, don't default anything
|
|
for (i = 0; i < 3 && config._a[i] == null; ++i) {
|
|
config._a[i] = input[i] = currentDate[i];
|
|
}
|
|
|
|
// Zero out whatever was not defaulted, including time
|
|
for (; i < 7; i++) {
|
|
config._a[i] = input[i] =
|
|
config._a[i] == null ? (i === 2 ? 1 : 0) : config._a[i];
|
|
}
|
|
|
|
// Check for 24:00:00.000
|
|
if (
|
|
config._a[HOUR] === 24 &&
|
|
config._a[MINUTE] === 0 &&
|
|
config._a[SECOND] === 0 &&
|
|
config._a[MILLISECOND] === 0
|
|
) {
|
|
config._nextDay = true;
|
|
config._a[HOUR] = 0;
|
|
}
|
|
|
|
config._d = (config._useUTC ? createUTCDate : createDate).apply(
|
|
null,
|
|
input
|
|
);
|
|
expectedWeekday = config._useUTC
|
|
? config._d.getUTCDay()
|
|
: config._d.getDay();
|
|
|
|
// Apply timezone offset from input. The actual utcOffset can be changed
|
|
// with parseZone.
|
|
if (config._tzm != null) {
|
|
config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
|
|
}
|
|
|
|
if (config._nextDay) {
|
|
config._a[HOUR] = 24;
|
|
}
|
|
|
|
// check for mismatching day of week
|
|
if (
|
|
config._w &&
|
|
typeof config._w.d !== 'undefined' &&
|
|
config._w.d !== expectedWeekday
|
|
) {
|
|
getParsingFlags(config).weekdayMismatch = true;
|
|
}
|
|
}
|
|
|
|
function dayOfYearFromWeekInfo(config) {
|
|
var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow, curWeek;
|
|
|
|
w = config._w;
|
|
if (w.GG != null || w.W != null || w.E != null) {
|
|
dow = 1;
|
|
doy = 4;
|
|
|
|
// TODO: We need to take the current isoWeekYear, but that depends on
|
|
// how we interpret now (local, utc, fixed offset). So create
|
|
// a now version of current config (take local/utc/offset flags, and
|
|
// create now).
|
|
weekYear = defaults(
|
|
w.GG,
|
|
config._a[YEAR],
|
|
weekOfYear(createLocal(), 1, 4).year
|
|
);
|
|
week = defaults(w.W, 1);
|
|
weekday = defaults(w.E, 1);
|
|
if (weekday < 1 || weekday > 7) {
|
|
weekdayOverflow = true;
|
|
}
|
|
} else {
|
|
dow = config._locale._week.dow;
|
|
doy = config._locale._week.doy;
|
|
|
|
curWeek = weekOfYear(createLocal(), dow, doy);
|
|
|
|
weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);
|
|
|
|
// Default to current week.
|
|
week = defaults(w.w, curWeek.week);
|
|
|
|
if (w.d != null) {
|
|
// weekday -- low day numbers are considered next week
|
|
weekday = w.d;
|
|
if (weekday < 0 || weekday > 6) {
|
|
weekdayOverflow = true;
|
|
}
|
|
} else if (w.e != null) {
|
|
// local weekday -- counting starts from beginning of week
|
|
weekday = w.e + dow;
|
|
if (w.e < 0 || w.e > 6) {
|
|
weekdayOverflow = true;
|
|
}
|
|
} else {
|
|
// default to beginning of week
|
|
weekday = dow;
|
|
}
|
|
}
|
|
if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
|
|
getParsingFlags(config)._overflowWeeks = true;
|
|
} else if (weekdayOverflow != null) {
|
|
getParsingFlags(config)._overflowWeekday = true;
|
|
} else {
|
|
temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
|
|
config._a[YEAR] = temp.year;
|
|
config._dayOfYear = temp.dayOfYear;
|
|
}
|
|
}
|
|
|
|
// constant that refers to the ISO standard
|
|
hooks.ISO_8601 = function () {};
|
|
|
|
// constant that refers to the RFC 2822 form
|
|
hooks.RFC_2822 = function () {};
|
|
|
|
// date from string and format string
|
|
function configFromStringAndFormat(config) {
|
|
// TODO: Move this to another part of the creation flow to prevent circular deps
|
|
if (config._f === hooks.ISO_8601) {
|
|
configFromISO(config);
|
|
return;
|
|
}
|
|
if (config._f === hooks.RFC_2822) {
|
|
configFromRFC2822(config);
|
|
return;
|
|
}
|
|
config._a = [];
|
|
getParsingFlags(config).empty = true;
|
|
|
|
// This array is used to make a Date, either with `new Date` or `Date.UTC`
|
|
var string = '' + config._i,
|
|
i,
|
|
parsedInput,
|
|
tokens,
|
|
token,
|
|
skipped,
|
|
stringLength = string.length,
|
|
totalParsedInputLength = 0,
|
|
era,
|
|
tokenLen;
|
|
|
|
tokens =
|
|
expandFormat(config._f, config._locale).match(formattingTokens) || [];
|
|
tokenLen = tokens.length;
|
|
for (i = 0; i < tokenLen; i++) {
|
|
token = tokens[i];
|
|
parsedInput = (string.match(getParseRegexForToken(token, config)) ||
|
|
[])[0];
|
|
if (parsedInput) {
|
|
skipped = string.substr(0, string.indexOf(parsedInput));
|
|
if (skipped.length > 0) {
|
|
getParsingFlags(config).unusedInput.push(skipped);
|
|
}
|
|
string = string.slice(
|
|
string.indexOf(parsedInput) + parsedInput.length
|
|
);
|
|
totalParsedInputLength += parsedInput.length;
|
|
}
|
|
// don't parse if it's not a known token
|
|
if (formatTokenFunctions[token]) {
|
|
if (parsedInput) {
|
|
getParsingFlags(config).empty = false;
|
|
} else {
|
|
getParsingFlags(config).unusedTokens.push(token);
|
|
}
|
|
addTimeToArrayFromToken(token, parsedInput, config);
|
|
} else if (config._strict && !parsedInput) {
|
|
getParsingFlags(config).unusedTokens.push(token);
|
|
}
|
|
}
|
|
|
|
// add remaining unparsed input length to the string
|
|
getParsingFlags(config).charsLeftOver =
|
|
stringLength - totalParsedInputLength;
|
|
if (string.length > 0) {
|
|
getParsingFlags(config).unusedInput.push(string);
|
|
}
|
|
|
|
// clear _12h flag if hour is <= 12
|
|
if (
|
|
config._a[HOUR] <= 12 &&
|
|
getParsingFlags(config).bigHour === true &&
|
|
config._a[HOUR] > 0
|
|
) {
|
|
getParsingFlags(config).bigHour = undefined;
|
|
}
|
|
|
|
getParsingFlags(config).parsedDateParts = config._a.slice(0);
|
|
getParsingFlags(config).meridiem = config._meridiem;
|
|
// handle meridiem
|
|
config._a[HOUR] = meridiemFixWrap(
|
|
config._locale,
|
|
config._a[HOUR],
|
|
config._meridiem
|
|
);
|
|
|
|
// handle era
|
|
era = getParsingFlags(config).era;
|
|
if (era !== null) {
|
|
config._a[YEAR] = config._locale.erasConvertYear(era, config._a[YEAR]);
|
|
}
|
|
|
|
configFromArray(config);
|
|
checkOverflow(config);
|
|
}
|
|
|
|
function meridiemFixWrap(locale, hour, meridiem) {
|
|
var isPm;
|
|
|
|
if (meridiem == null) {
|
|
// nothing to do
|
|
return hour;
|
|
}
|
|
if (locale.meridiemHour != null) {
|
|
return locale.meridiemHour(hour, meridiem);
|
|
} else if (locale.isPM != null) {
|
|
// Fallback
|
|
isPm = locale.isPM(meridiem);
|
|
if (isPm && hour < 12) {
|
|
hour += 12;
|
|
}
|
|
if (!isPm && hour === 12) {
|
|
hour = 0;
|
|
}
|
|
return hour;
|
|
} else {
|
|
// this is not supposed to happen
|
|
return hour;
|
|
}
|
|
}
|
|
|
|
// date from string and array of format strings
|
|
function configFromStringAndArray(config) {
|
|
var tempConfig,
|
|
bestMoment,
|
|
scoreToBeat,
|
|
i,
|
|
currentScore,
|
|
validFormatFound,
|
|
bestFormatIsValid = false,
|
|
configfLen = config._f.length;
|
|
|
|
if (configfLen === 0) {
|
|
getParsingFlags(config).invalidFormat = true;
|
|
config._d = new Date(NaN);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < configfLen; i++) {
|
|
currentScore = 0;
|
|
validFormatFound = false;
|
|
tempConfig = copyConfig({}, config);
|
|
if (config._useUTC != null) {
|
|
tempConfig._useUTC = config._useUTC;
|
|
}
|
|
tempConfig._f = config._f[i];
|
|
configFromStringAndFormat(tempConfig);
|
|
|
|
if (isValid(tempConfig)) {
|
|
validFormatFound = true;
|
|
}
|
|
|
|
// if there is any input that was not parsed add a penalty for that format
|
|
currentScore += getParsingFlags(tempConfig).charsLeftOver;
|
|
|
|
//or tokens
|
|
currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
|
|
|
|
getParsingFlags(tempConfig).score = currentScore;
|
|
|
|
if (!bestFormatIsValid) {
|
|
if (
|
|
scoreToBeat == null ||
|
|
currentScore < scoreToBeat ||
|
|
validFormatFound
|
|
) {
|
|
scoreToBeat = currentScore;
|
|
bestMoment = tempConfig;
|
|
if (validFormatFound) {
|
|
bestFormatIsValid = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (currentScore < scoreToBeat) {
|
|
scoreToBeat = currentScore;
|
|
bestMoment = tempConfig;
|
|
}
|
|
}
|
|
}
|
|
|
|
extend(config, bestMoment || tempConfig);
|
|
}
|
|
|
|
function configFromObject(config) {
|
|
if (config._d) {
|
|
return;
|
|
}
|
|
|
|
var i = normalizeObjectUnits(config._i),
|
|
dayOrDate = i.day === undefined ? i.date : i.day;
|
|
config._a = map(
|
|
[i.year, i.month, dayOrDate, i.hour, i.minute, i.second, i.millisecond],
|
|
function (obj) {
|
|
return obj && parseInt(obj, 10);
|
|
}
|
|
);
|
|
|
|
configFromArray(config);
|
|
}
|
|
|
|
function createFromConfig(config) {
|
|
var res = new Moment(checkOverflow(prepareConfig(config)));
|
|
if (res._nextDay) {
|
|
// Adding is smart enough around DST
|
|
res.add(1, 'd');
|
|
res._nextDay = undefined;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
function prepareConfig(config) {
|
|
var input = config._i,
|
|
format = config._f;
|
|
|
|
config._locale = config._locale || getLocale(config._l);
|
|
|
|
if (input === null || (format === undefined && input === '')) {
|
|
return createInvalid({ nullInput: true });
|
|
}
|
|
|
|
if (typeof input === 'string') {
|
|
config._i = input = config._locale.preparse(input);
|
|
}
|
|
|
|
if (isMoment(input)) {
|
|
return new Moment(checkOverflow(input));
|
|
} else if (isDate(input)) {
|
|
config._d = input;
|
|
} else if (isArray(format)) {
|
|
configFromStringAndArray(config);
|
|
} else if (format) {
|
|
configFromStringAndFormat(config);
|
|
} else {
|
|
configFromInput(config);
|
|
}
|
|
|
|
if (!isValid(config)) {
|
|
config._d = null;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
function configFromInput(config) {
|
|
var input = config._i;
|
|
if (isUndefined(input)) {
|
|
config._d = new Date(hooks.now());
|
|
} else if (isDate(input)) {
|
|
config._d = new Date(input.valueOf());
|
|
} else if (typeof input === 'string') {
|
|
configFromString(config);
|
|
} else if (isArray(input)) {
|
|
config._a = map(input.slice(0), function (obj) {
|
|
return parseInt(obj, 10);
|
|
});
|
|
configFromArray(config);
|
|
} else if (isObject(input)) {
|
|
configFromObject(config);
|
|
} else if (isNumber(input)) {
|
|
// from milliseconds
|
|
config._d = new Date(input);
|
|
} else {
|
|
hooks.createFromInputFallback(config);
|
|
}
|
|
}
|
|
|
|
function createLocalOrUTC(input, format, locale, strict, isUTC) {
|
|
var c = {};
|
|
|
|
if (format === true || format === false) {
|
|
strict = format;
|
|
format = undefined;
|
|
}
|
|
|
|
if (locale === true || locale === false) {
|
|
strict = locale;
|
|
locale = undefined;
|
|
}
|
|
|
|
if (
|
|
(isObject(input) && isObjectEmpty(input)) ||
|
|
(isArray(input) && input.length === 0)
|
|
) {
|
|
input = undefined;
|
|
}
|
|
// object construction must be done this way.
|
|
// https://github.com/moment/moment/issues/1423
|
|
c._isAMomentObject = true;
|
|
c._useUTC = c._isUTC = isUTC;
|
|
c._l = locale;
|
|
c._i = input;
|
|
c._f = format;
|
|
c._strict = strict;
|
|
|
|
return createFromConfig(c);
|
|
}
|
|
|
|
function createLocal(input, format, locale, strict) {
|
|
return createLocalOrUTC(input, format, locale, strict, false);
|
|
}
|
|
|
|
var prototypeMin = deprecate(
|
|
'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
|
|
function () {
|
|
var other = createLocal.apply(null, arguments);
|
|
if (this.isValid() && other.isValid()) {
|
|
return other < this ? this : other;
|
|
} else {
|
|
return createInvalid();
|
|
}
|
|
}
|
|
),
|
|
prototypeMax = deprecate(
|
|
'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
|
|
function () {
|
|
var other = createLocal.apply(null, arguments);
|
|
if (this.isValid() && other.isValid()) {
|
|
return other > this ? this : other;
|
|
} else {
|
|
return createInvalid();
|
|
}
|
|
}
|
|
);
|
|
|
|
// Pick a moment m from moments so that m[fn](other) is true for all
|
|
// other. This relies on the function fn to be transitive.
|
|
//
|
|
// moments should either be an array of moment objects or an array, whose
|
|
// first element is an array of moment objects.
|
|
function pickBy(fn, moments) {
|
|
var res, i;
|
|
if (moments.length === 1 && isArray(moments[0])) {
|
|
moments = moments[0];
|
|
}
|
|
if (!moments.length) {
|
|
return createLocal();
|
|
}
|
|
res = moments[0];
|
|
for (i = 1; i < moments.length; ++i) {
|
|
if (!moments[i].isValid() || moments[i][fn](res)) {
|
|
res = moments[i];
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// TODO: Use [].sort instead?
|
|
function min() {
|
|
var args = [].slice.call(arguments, 0);
|
|
|
|
return pickBy('isBefore', args);
|
|
}
|
|
|
|
function max() {
|
|
var args = [].slice.call(arguments, 0);
|
|
|
|
return pickBy('isAfter', args);
|
|
}
|
|
|
|
var now = function () {
|
|
return Date.now ? Date.now() : +new Date();
|
|
};
|
|
|
|
var ordering = [
|
|
'year',
|
|
'quarter',
|
|
'month',
|
|
'week',
|
|
'day',
|
|
'hour',
|
|
'minute',
|
|
'second',
|
|
'millisecond',
|
|
];
|
|
|
|
function isDurationValid(m) {
|
|
var key,
|
|
unitHasDecimal = false,
|
|
i,
|
|
orderLen = ordering.length;
|
|
for (key in m) {
|
|
if (
|
|
hasOwnProp(m, key) &&
|
|
!(
|
|
indexOf.call(ordering, key) !== -1 &&
|
|
(m[key] == null || !isNaN(m[key]))
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < orderLen; ++i) {
|
|
if (m[ordering[i]]) {
|
|
if (unitHasDecimal) {
|
|
return false; // only allow non-integers for smallest unit
|
|
}
|
|
if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
|
|
unitHasDecimal = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function isValid$1() {
|
|
return this._isValid;
|
|
}
|
|
|
|
function createInvalid$1() {
|
|
return createDuration(NaN);
|
|
}
|
|
|
|
function Duration(duration) {
|
|
var normalizedInput = normalizeObjectUnits(duration),
|
|
years = normalizedInput.year || 0,
|
|
quarters = normalizedInput.quarter || 0,
|
|
months = normalizedInput.month || 0,
|
|
weeks = normalizedInput.week || normalizedInput.isoWeek || 0,
|
|
days = normalizedInput.day || 0,
|
|
hours = normalizedInput.hour || 0,
|
|
minutes = normalizedInput.minute || 0,
|
|
seconds = normalizedInput.second || 0,
|
|
milliseconds = normalizedInput.millisecond || 0;
|
|
|
|
this._isValid = isDurationValid(normalizedInput);
|
|
|
|
// representation for dateAddRemove
|
|
this._milliseconds =
|
|
+milliseconds +
|
|
seconds * 1e3 + // 1000
|
|
minutes * 6e4 + // 1000 * 60
|
|
hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
|
|
// Because of dateAddRemove treats 24 hours as different from a
|
|
// day when working around DST, we need to store them separately
|
|
this._days = +days + weeks * 7;
|
|
// It is impossible to translate months into days without knowing
|
|
// which months you are are talking about, so we have to store
|
|
// it separately.
|
|
this._months = +months + quarters * 3 + years * 12;
|
|
|
|
this._data = {};
|
|
|
|
this._locale = getLocale();
|
|
|
|
this._bubble();
|
|
}
|
|
|
|
function isDuration(obj) {
|
|
return obj instanceof Duration;
|
|
}
|
|
|
|
function absRound(number) {
|
|
if (number < 0) {
|
|
return Math.round(-1 * number) * -1;
|
|
} else {
|
|
return Math.round(number);
|
|
}
|
|
}
|
|
|
|
// compare two arrays, return the number of differences
|
|
function compareArrays(array1, array2, dontConvert) {
|
|
var len = Math.min(array1.length, array2.length),
|
|
lengthDiff = Math.abs(array1.length - array2.length),
|
|
diffs = 0,
|
|
i;
|
|
for (i = 0; i < len; i++) {
|
|
if (
|
|
(dontConvert && array1[i] !== array2[i]) ||
|
|
(!dontConvert && toInt(array1[i]) !== toInt(array2[i]))
|
|
) {
|
|
diffs++;
|
|
}
|
|
}
|
|
return diffs + lengthDiff;
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
function offset(token, separator) {
|
|
addFormatToken(token, 0, 0, function () {
|
|
var offset = this.utcOffset(),
|
|
sign = '+';
|
|
if (offset < 0) {
|
|
offset = -offset;
|
|
sign = '-';
|
|
}
|
|
return (
|
|
sign +
|
|
zeroFill(~~(offset / 60), 2) +
|
|
separator +
|
|
zeroFill(~~offset % 60, 2)
|
|
);
|
|
});
|
|
}
|
|
|
|
offset('Z', ':');
|
|
offset('ZZ', '');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('Z', matchShortOffset);
|
|
addRegexToken('ZZ', matchShortOffset);
|
|
addParseToken(['Z', 'ZZ'], function (input, array, config) {
|
|
config._useUTC = true;
|
|
config._tzm = offsetFromString(matchShortOffset, input);
|
|
});
|
|
|
|
// HELPERS
|
|
|
|
// timezone chunker
|
|
// '+10:00' > ['10', '00']
|
|
// '-1530' > ['-15', '30']
|
|
var chunkOffset = /([\+\-]|\d\d)/gi;
|
|
|
|
function offsetFromString(matcher, string) {
|
|
var matches = (string || '').match(matcher),
|
|
chunk,
|
|
parts,
|
|
minutes;
|
|
|
|
if (matches === null) {
|
|
return null;
|
|
}
|
|
|
|
chunk = matches[matches.length - 1] || [];
|
|
parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
|
|
minutes = +(parts[1] * 60) + toInt(parts[2]);
|
|
|
|
return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes;
|
|
}
|
|
|
|
// Return a moment from input, that is local/utc/zone equivalent to model.
|
|
function cloneWithOffset(input, model) {
|
|
var res, diff;
|
|
if (model._isUTC) {
|
|
res = model.clone();
|
|
diff =
|
|
(isMoment(input) || isDate(input)
|
|
? input.valueOf()
|
|
: createLocal(input).valueOf()) - res.valueOf();
|
|
// Use low-level api, because this fn is low-level api.
|
|
res._d.setTime(res._d.valueOf() + diff);
|
|
hooks.updateOffset(res, false);
|
|
return res;
|
|
} else {
|
|
return createLocal(input).local();
|
|
}
|
|
}
|
|
|
|
function getDateOffset(m) {
|
|
// On Firefox.24 Date#getTimezoneOffset returns a floating point.
|
|
// https://github.com/moment/moment/pull/1871
|
|
return -Math.round(m._d.getTimezoneOffset());
|
|
}
|
|
|
|
// HOOKS
|
|
|
|
// This function will be called whenever a moment is mutated.
|
|
// It is intended to keep the offset in sync with the timezone.
|
|
hooks.updateOffset = function () {};
|
|
|
|
// MOMENTS
|
|
|
|
// keepLocalTime = true means only change the timezone, without
|
|
// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
|
|
// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
|
|
// +0200, so we adjust the time as needed, to be valid.
|
|
//
|
|
// Keeping the time actually adds/subtracts (one hour)
|
|
// from the actual represented time. That is why we call updateOffset
|
|
// a second time. In case it wants us to change the offset again
|
|
// _changeInProgress == true case, then we have to adjust, because
|
|
// there is no such time in the given timezone.
|
|
function getSetOffset(input, keepLocalTime, keepMinutes) {
|
|
var offset = this._offset || 0,
|
|
localAdjust;
|
|
if (!this.isValid()) {
|
|
return input != null ? this : NaN;
|
|
}
|
|
if (input != null) {
|
|
if (typeof input === 'string') {
|
|
input = offsetFromString(matchShortOffset, input);
|
|
if (input === null) {
|
|
return this;
|
|
}
|
|
} else if (Math.abs(input) < 16 && !keepMinutes) {
|
|
input = input * 60;
|
|
}
|
|
if (!this._isUTC && keepLocalTime) {
|
|
localAdjust = getDateOffset(this);
|
|
}
|
|
this._offset = input;
|
|
this._isUTC = true;
|
|
if (localAdjust != null) {
|
|
this.add(localAdjust, 'm');
|
|
}
|
|
if (offset !== input) {
|
|
if (!keepLocalTime || this._changeInProgress) {
|
|
addSubtract(
|
|
this,
|
|
createDuration(input - offset, 'm'),
|
|
1,
|
|
false
|
|
);
|
|
} else if (!this._changeInProgress) {
|
|
this._changeInProgress = true;
|
|
hooks.updateOffset(this, true);
|
|
this._changeInProgress = null;
|
|
}
|
|
}
|
|
return this;
|
|
} else {
|
|
return this._isUTC ? offset : getDateOffset(this);
|
|
}
|
|
}
|
|
|
|
function getSetZone(input, keepLocalTime) {
|
|
if (input != null) {
|
|
if (typeof input !== 'string') {
|
|
input = -input;
|
|
}
|
|
|
|
this.utcOffset(input, keepLocalTime);
|
|
|
|
return this;
|
|
} else {
|
|
return -this.utcOffset();
|
|
}
|
|
}
|
|
|
|
function setOffsetToUTC(keepLocalTime) {
|
|
return this.utcOffset(0, keepLocalTime);
|
|
}
|
|
|
|
function setOffsetToLocal(keepLocalTime) {
|
|
if (this._isUTC) {
|
|
this.utcOffset(0, keepLocalTime);
|
|
this._isUTC = false;
|
|
|
|
if (keepLocalTime) {
|
|
this.subtract(getDateOffset(this), 'm');
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
function setOffsetToParsedOffset() {
|
|
if (this._tzm != null) {
|
|
this.utcOffset(this._tzm, false, true);
|
|
} else if (typeof this._i === 'string') {
|
|
var tZone = offsetFromString(matchOffset, this._i);
|
|
if (tZone != null) {
|
|
this.utcOffset(tZone);
|
|
} else {
|
|
this.utcOffset(0, true);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
function hasAlignedHourOffset(input) {
|
|
if (!this.isValid()) {
|
|
return false;
|
|
}
|
|
input = input ? createLocal(input).utcOffset() : 0;
|
|
|
|
return (this.utcOffset() - input) % 60 === 0;
|
|
}
|
|
|
|
function isDaylightSavingTime() {
|
|
return (
|
|
this.utcOffset() > this.clone().month(0).utcOffset() ||
|
|
this.utcOffset() > this.clone().month(5).utcOffset()
|
|
);
|
|
}
|
|
|
|
function isDaylightSavingTimeShifted() {
|
|
if (!isUndefined(this._isDSTShifted)) {
|
|
return this._isDSTShifted;
|
|
}
|
|
|
|
var c = {},
|
|
other;
|
|
|
|
copyConfig(c, this);
|
|
c = prepareConfig(c);
|
|
|
|
if (c._a) {
|
|
other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
|
|
this._isDSTShifted =
|
|
this.isValid() && compareArrays(c._a, other.toArray()) > 0;
|
|
} else {
|
|
this._isDSTShifted = false;
|
|
}
|
|
|
|
return this._isDSTShifted;
|
|
}
|
|
|
|
function isLocal() {
|
|
return this.isValid() ? !this._isUTC : false;
|
|
}
|
|
|
|
function isUtcOffset() {
|
|
return this.isValid() ? this._isUTC : false;
|
|
}
|
|
|
|
function isUtc() {
|
|
return this.isValid() ? this._isUTC && this._offset === 0 : false;
|
|
}
|
|
|
|
// ASP.NET json date format regex
|
|
var aspNetRegex = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/,
|
|
// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
|
|
// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
|
|
// and further modified to allow for strings containing both week and day
|
|
isoRegex =
|
|
/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;
|
|
|
|
function createDuration(input, key) {
|
|
var duration = input,
|
|
// matching against regexp is expensive, do it on demand
|
|
match = null,
|
|
sign,
|
|
ret,
|
|
diffRes;
|
|
|
|
if (isDuration(input)) {
|
|
duration = {
|
|
ms: input._milliseconds,
|
|
d: input._days,
|
|
M: input._months,
|
|
};
|
|
} else if (isNumber(input) || !isNaN(+input)) {
|
|
duration = {};
|
|
if (key) {
|
|
duration[key] = +input;
|
|
} else {
|
|
duration.milliseconds = +input;
|
|
}
|
|
} else if ((match = aspNetRegex.exec(input))) {
|
|
sign = match[1] === '-' ? -1 : 1;
|
|
duration = {
|
|
y: 0,
|
|
d: toInt(match[DATE]) * sign,
|
|
h: toInt(match[HOUR]) * sign,
|
|
m: toInt(match[MINUTE]) * sign,
|
|
s: toInt(match[SECOND]) * sign,
|
|
ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign, // the millisecond decimal point is included in the match
|
|
};
|
|
} else if ((match = isoRegex.exec(input))) {
|
|
sign = match[1] === '-' ? -1 : 1;
|
|
duration = {
|
|
y: parseIso(match[2], sign),
|
|
M: parseIso(match[3], sign),
|
|
w: parseIso(match[4], sign),
|
|
d: parseIso(match[5], sign),
|
|
h: parseIso(match[6], sign),
|
|
m: parseIso(match[7], sign),
|
|
s: parseIso(match[8], sign),
|
|
};
|
|
} else if (duration == null) {
|
|
// checks for null or undefined
|
|
duration = {};
|
|
} else if (
|
|
typeof duration === 'object' &&
|
|
('from' in duration || 'to' in duration)
|
|
) {
|
|
diffRes = momentsDifference(
|
|
createLocal(duration.from),
|
|
createLocal(duration.to)
|
|
);
|
|
|
|
duration = {};
|
|
duration.ms = diffRes.milliseconds;
|
|
duration.M = diffRes.months;
|
|
}
|
|
|
|
ret = new Duration(duration);
|
|
|
|
if (isDuration(input) && hasOwnProp(input, '_locale')) {
|
|
ret._locale = input._locale;
|
|
}
|
|
|
|
if (isDuration(input) && hasOwnProp(input, '_isValid')) {
|
|
ret._isValid = input._isValid;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
createDuration.fn = Duration.prototype;
|
|
createDuration.invalid = createInvalid$1;
|
|
|
|
function parseIso(inp, sign) {
|
|
// We'd normally use ~~inp for this, but unfortunately it also
|
|
// converts floats to ints.
|
|
// inp may be undefined, so careful calling replace on it.
|
|
var res = inp && parseFloat(inp.replace(',', '.'));
|
|
// apply sign while we're at it
|
|
return (isNaN(res) ? 0 : res) * sign;
|
|
}
|
|
|
|
function positiveMomentsDifference(base, other) {
|
|
var res = {};
|
|
|
|
res.months =
|
|
other.month() - base.month() + (other.year() - base.year()) * 12;
|
|
if (base.clone().add(res.months, 'M').isAfter(other)) {
|
|
--res.months;
|
|
}
|
|
|
|
res.milliseconds = +other - +base.clone().add(res.months, 'M');
|
|
|
|
return res;
|
|
}
|
|
|
|
function momentsDifference(base, other) {
|
|
var res;
|
|
if (!(base.isValid() && other.isValid())) {
|
|
return { milliseconds: 0, months: 0 };
|
|
}
|
|
|
|
other = cloneWithOffset(other, base);
|
|
if (base.isBefore(other)) {
|
|
res = positiveMomentsDifference(base, other);
|
|
} else {
|
|
res = positiveMomentsDifference(other, base);
|
|
res.milliseconds = -res.milliseconds;
|
|
res.months = -res.months;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// TODO: remove 'name' arg after deprecation is removed
|
|
function createAdder(direction, name) {
|
|
return function (val, period) {
|
|
var dur, tmp;
|
|
//invert the arguments, but complain about it
|
|
if (period !== null && !isNaN(+period)) {
|
|
deprecateSimple(
|
|
name,
|
|
'moment().' +
|
|
name +
|
|
'(period, number) is deprecated. Please use moment().' +
|
|
name +
|
|
'(number, period). ' +
|
|
'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'
|
|
);
|
|
tmp = val;
|
|
val = period;
|
|
period = tmp;
|
|
}
|
|
|
|
dur = createDuration(val, period);
|
|
addSubtract(this, dur, direction);
|
|
return this;
|
|
};
|
|
}
|
|
|
|
function addSubtract(mom, duration, isAdding, updateOffset) {
|
|
var milliseconds = duration._milliseconds,
|
|
days = absRound(duration._days),
|
|
months = absRound(duration._months);
|
|
|
|
if (!mom.isValid()) {
|
|
// No op
|
|
return;
|
|
}
|
|
|
|
updateOffset = updateOffset == null ? true : updateOffset;
|
|
|
|
if (months) {
|
|
setMonth(mom, get(mom, 'Month') + months * isAdding);
|
|
}
|
|
if (days) {
|
|
set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
|
|
}
|
|
if (milliseconds) {
|
|
mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
|
|
}
|
|
if (updateOffset) {
|
|
hooks.updateOffset(mom, days || months);
|
|
}
|
|
}
|
|
|
|
var add = createAdder(1, 'add'),
|
|
subtract = createAdder(-1, 'subtract');
|
|
|
|
function isString(input) {
|
|
return typeof input === 'string' || input instanceof String;
|
|
}
|
|
|
|
// type MomentInput = Moment | Date | string | number | (number | string)[] | MomentInputObject | void; // null | undefined
|
|
function isMomentInput(input) {
|
|
return (
|
|
isMoment(input) ||
|
|
isDate(input) ||
|
|
isString(input) ||
|
|
isNumber(input) ||
|
|
isNumberOrStringArray(input) ||
|
|
isMomentInputObject(input) ||
|
|
input === null ||
|
|
input === undefined
|
|
);
|
|
}
|
|
|
|
function isMomentInputObject(input) {
|
|
var objectTest = isObject(input) && !isObjectEmpty(input),
|
|
propertyTest = false,
|
|
properties = [
|
|
'years',
|
|
'year',
|
|
'y',
|
|
'months',
|
|
'month',
|
|
'M',
|
|
'days',
|
|
'day',
|
|
'd',
|
|
'dates',
|
|
'date',
|
|
'D',
|
|
'hours',
|
|
'hour',
|
|
'h',
|
|
'minutes',
|
|
'minute',
|
|
'm',
|
|
'seconds',
|
|
'second',
|
|
's',
|
|
'milliseconds',
|
|
'millisecond',
|
|
'ms',
|
|
],
|
|
i,
|
|
property,
|
|
propertyLen = properties.length;
|
|
|
|
for (i = 0; i < propertyLen; i += 1) {
|
|
property = properties[i];
|
|
propertyTest = propertyTest || hasOwnProp(input, property);
|
|
}
|
|
|
|
return objectTest && propertyTest;
|
|
}
|
|
|
|
function isNumberOrStringArray(input) {
|
|
var arrayTest = isArray(input),
|
|
dataTypeTest = false;
|
|
if (arrayTest) {
|
|
dataTypeTest =
|
|
input.filter(function (item) {
|
|
return !isNumber(item) && isString(input);
|
|
}).length === 0;
|
|
}
|
|
return arrayTest && dataTypeTest;
|
|
}
|
|
|
|
function isCalendarSpec(input) {
|
|
var objectTest = isObject(input) && !isObjectEmpty(input),
|
|
propertyTest = false,
|
|
properties = [
|
|
'sameDay',
|
|
'nextDay',
|
|
'lastDay',
|
|
'nextWeek',
|
|
'lastWeek',
|
|
'sameElse',
|
|
],
|
|
i,
|
|
property;
|
|
|
|
for (i = 0; i < properties.length; i += 1) {
|
|
property = properties[i];
|
|
propertyTest = propertyTest || hasOwnProp(input, property);
|
|
}
|
|
|
|
return objectTest && propertyTest;
|
|
}
|
|
|
|
function getCalendarFormat(myMoment, now) {
|
|
var diff = myMoment.diff(now, 'days', true);
|
|
return diff < -6
|
|
? 'sameElse'
|
|
: diff < -1
|
|
? 'lastWeek'
|
|
: diff < 0
|
|
? 'lastDay'
|
|
: diff < 1
|
|
? 'sameDay'
|
|
: diff < 2
|
|
? 'nextDay'
|
|
: diff < 7
|
|
? 'nextWeek'
|
|
: 'sameElse';
|
|
}
|
|
|
|
function calendar$1(time, formats) {
|
|
// Support for single parameter, formats only overload to the calendar function
|
|
if (arguments.length === 1) {
|
|
if (!arguments[0]) {
|
|
time = undefined;
|
|
formats = undefined;
|
|
} else if (isMomentInput(arguments[0])) {
|
|
time = arguments[0];
|
|
formats = undefined;
|
|
} else if (isCalendarSpec(arguments[0])) {
|
|
formats = arguments[0];
|
|
time = undefined;
|
|
}
|
|
}
|
|
// We want to compare the start of today, vs this.
|
|
// Getting start-of-today depends on whether we're local/utc/offset or not.
|
|
var now = time || createLocal(),
|
|
sod = cloneWithOffset(now, this).startOf('day'),
|
|
format = hooks.calendarFormat(this, sod) || 'sameElse',
|
|
output =
|
|
formats &&
|
|
(isFunction(formats[format])
|
|
? formats[format].call(this, now)
|
|
: formats[format]);
|
|
|
|
return this.format(
|
|
output || this.localeData().calendar(format, this, createLocal(now))
|
|
);
|
|
}
|
|
|
|
function clone() {
|
|
return new Moment(this);
|
|
}
|
|
|
|
function isAfter(input, units) {
|
|
var localInput = isMoment(input) ? input : createLocal(input);
|
|
if (!(this.isValid() && localInput.isValid())) {
|
|
return false;
|
|
}
|
|
units = normalizeUnits(units) || 'millisecond';
|
|
if (units === 'millisecond') {
|
|
return this.valueOf() > localInput.valueOf();
|
|
} else {
|
|
return localInput.valueOf() < this.clone().startOf(units).valueOf();
|
|
}
|
|
}
|
|
|
|
function isBefore(input, units) {
|
|
var localInput = isMoment(input) ? input : createLocal(input);
|
|
if (!(this.isValid() && localInput.isValid())) {
|
|
return false;
|
|
}
|
|
units = normalizeUnits(units) || 'millisecond';
|
|
if (units === 'millisecond') {
|
|
return this.valueOf() < localInput.valueOf();
|
|
} else {
|
|
return this.clone().endOf(units).valueOf() < localInput.valueOf();
|
|
}
|
|
}
|
|
|
|
function isBetween(from, to, units, inclusivity) {
|
|
var localFrom = isMoment(from) ? from : createLocal(from),
|
|
localTo = isMoment(to) ? to : createLocal(to);
|
|
if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) {
|
|
return false;
|
|
}
|
|
inclusivity = inclusivity || '()';
|
|
return (
|
|
(inclusivity[0] === '('
|
|
? this.isAfter(localFrom, units)
|
|
: !this.isBefore(localFrom, units)) &&
|
|
(inclusivity[1] === ')'
|
|
? this.isBefore(localTo, units)
|
|
: !this.isAfter(localTo, units))
|
|
);
|
|
}
|
|
|
|
function isSame(input, units) {
|
|
var localInput = isMoment(input) ? input : createLocal(input),
|
|
inputMs;
|
|
if (!(this.isValid() && localInput.isValid())) {
|
|
return false;
|
|
}
|
|
units = normalizeUnits(units) || 'millisecond';
|
|
if (units === 'millisecond') {
|
|
return this.valueOf() === localInput.valueOf();
|
|
} else {
|
|
inputMs = localInput.valueOf();
|
|
return (
|
|
this.clone().startOf(units).valueOf() <= inputMs &&
|
|
inputMs <= this.clone().endOf(units).valueOf()
|
|
);
|
|
}
|
|
}
|
|
|
|
function isSameOrAfter(input, units) {
|
|
return this.isSame(input, units) || this.isAfter(input, units);
|
|
}
|
|
|
|
function isSameOrBefore(input, units) {
|
|
return this.isSame(input, units) || this.isBefore(input, units);
|
|
}
|
|
|
|
function diff(input, units, asFloat) {
|
|
var that, zoneDelta, output;
|
|
|
|
if (!this.isValid()) {
|
|
return NaN;
|
|
}
|
|
|
|
that = cloneWithOffset(input, this);
|
|
|
|
if (!that.isValid()) {
|
|
return NaN;
|
|
}
|
|
|
|
zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
|
|
|
|
units = normalizeUnits(units);
|
|
|
|
switch (units) {
|
|
case 'year':
|
|
output = monthDiff(this, that) / 12;
|
|
break;
|
|
case 'month':
|
|
output = monthDiff(this, that);
|
|
break;
|
|
case 'quarter':
|
|
output = monthDiff(this, that) / 3;
|
|
break;
|
|
case 'second':
|
|
output = (this - that) / 1e3;
|
|
break; // 1000
|
|
case 'minute':
|
|
output = (this - that) / 6e4;
|
|
break; // 1000 * 60
|
|
case 'hour':
|
|
output = (this - that) / 36e5;
|
|
break; // 1000 * 60 * 60
|
|
case 'day':
|
|
output = (this - that - zoneDelta) / 864e5;
|
|
break; // 1000 * 60 * 60 * 24, negate dst
|
|
case 'week':
|
|
output = (this - that - zoneDelta) / 6048e5;
|
|
break; // 1000 * 60 * 60 * 24 * 7, negate dst
|
|
default:
|
|
output = this - that;
|
|
}
|
|
|
|
return asFloat ? output : absFloor(output);
|
|
}
|
|
|
|
function monthDiff(a, b) {
|
|
if (a.date() < b.date()) {
|
|
// end-of-month calculations work correct when the start month has more
|
|
// days than the end month.
|
|
return -monthDiff(b, a);
|
|
}
|
|
// difference in months
|
|
var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()),
|
|
// b is in (anchor - 1 month, anchor + 1 month)
|
|
anchor = a.clone().add(wholeMonthDiff, 'months'),
|
|
anchor2,
|
|
adjust;
|
|
|
|
if (b - anchor < 0) {
|
|
anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
|
|
// linear across the month
|
|
adjust = (b - anchor) / (anchor - anchor2);
|
|
} else {
|
|
anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
|
|
// linear across the month
|
|
adjust = (b - anchor) / (anchor2 - anchor);
|
|
}
|
|
|
|
//check for negative zero, return zero if negative zero
|
|
return -(wholeMonthDiff + adjust) || 0;
|
|
}
|
|
|
|
hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
|
|
hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
|
|
|
|
function toString() {
|
|
return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
|
|
}
|
|
|
|
function toISOString(keepOffset) {
|
|
if (!this.isValid()) {
|
|
return null;
|
|
}
|
|
var utc = keepOffset !== true,
|
|
m = utc ? this.clone().utc() : this;
|
|
if (m.year() < 0 || m.year() > 9999) {
|
|
return formatMoment(
|
|
m,
|
|
utc
|
|
? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'
|
|
: 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'
|
|
);
|
|
}
|
|
if (isFunction(Date.prototype.toISOString)) {
|
|
// native implementation is ~50x faster, use it when we can
|
|
if (utc) {
|
|
return this.toDate().toISOString();
|
|
} else {
|
|
return new Date(this.valueOf() + this.utcOffset() * 60 * 1000)
|
|
.toISOString()
|
|
.replace('Z', formatMoment(m, 'Z'));
|
|
}
|
|
}
|
|
return formatMoment(
|
|
m,
|
|
utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return a human readable representation of a moment that can
|
|
* also be evaluated to get a new moment which is the same
|
|
*
|
|
* @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
|
|
*/
|
|
function inspect() {
|
|
if (!this.isValid()) {
|
|
return 'moment.invalid(/* ' + this._i + ' */)';
|
|
}
|
|
var func = 'moment',
|
|
zone = '',
|
|
prefix,
|
|
year,
|
|
datetime,
|
|
suffix;
|
|
if (!this.isLocal()) {
|
|
func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
|
|
zone = 'Z';
|
|
}
|
|
prefix = '[' + func + '("]';
|
|
year = 0 <= this.year() && this.year() <= 9999 ? 'YYYY' : 'YYYYYY';
|
|
datetime = '-MM-DD[T]HH:mm:ss.SSS';
|
|
suffix = zone + '[")]';
|
|
|
|
return this.format(prefix + year + datetime + suffix);
|
|
}
|
|
|
|
function format(inputString) {
|
|
if (!inputString) {
|
|
inputString = this.isUtc()
|
|
? hooks.defaultFormatUtc
|
|
: hooks.defaultFormat;
|
|
}
|
|
var output = formatMoment(this, inputString);
|
|
return this.localeData().postformat(output);
|
|
}
|
|
|
|
function from(time, withoutSuffix) {
|
|
if (
|
|
this.isValid() &&
|
|
((isMoment(time) && time.isValid()) || createLocal(time).isValid())
|
|
) {
|
|
return createDuration({ to: this, from: time })
|
|
.locale(this.locale())
|
|
.humanize(!withoutSuffix);
|
|
} else {
|
|
return this.localeData().invalidDate();
|
|
}
|
|
}
|
|
|
|
function fromNow(withoutSuffix) {
|
|
return this.from(createLocal(), withoutSuffix);
|
|
}
|
|
|
|
function to(time, withoutSuffix) {
|
|
if (
|
|
this.isValid() &&
|
|
((isMoment(time) && time.isValid()) || createLocal(time).isValid())
|
|
) {
|
|
return createDuration({ from: this, to: time })
|
|
.locale(this.locale())
|
|
.humanize(!withoutSuffix);
|
|
} else {
|
|
return this.localeData().invalidDate();
|
|
}
|
|
}
|
|
|
|
function toNow(withoutSuffix) {
|
|
return this.to(createLocal(), withoutSuffix);
|
|
}
|
|
|
|
// If passed a locale key, it will set the locale for this
|
|
// instance. Otherwise, it will return the locale configuration
|
|
// variables for this instance.
|
|
function locale(key) {
|
|
var newLocaleData;
|
|
|
|
if (key === undefined) {
|
|
return this._locale._abbr;
|
|
} else {
|
|
newLocaleData = getLocale(key);
|
|
if (newLocaleData != null) {
|
|
this._locale = newLocaleData;
|
|
}
|
|
return this;
|
|
}
|
|
}
|
|
|
|
var lang = deprecate(
|
|
'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
|
|
function (key) {
|
|
if (key === undefined) {
|
|
return this.localeData();
|
|
} else {
|
|
return this.locale(key);
|
|
}
|
|
}
|
|
);
|
|
|
|
function localeData() {
|
|
return this._locale;
|
|
}
|
|
|
|
var MS_PER_SECOND = 1000,
|
|
MS_PER_MINUTE = 60 * MS_PER_SECOND,
|
|
MS_PER_HOUR = 60 * MS_PER_MINUTE,
|
|
MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR;
|
|
|
|
// actual modulo - handles negative numbers (for dates before 1970):
|
|
function mod$1(dividend, divisor) {
|
|
return ((dividend % divisor) + divisor) % divisor;
|
|
}
|
|
|
|
function localStartOfDate(y, m, d) {
|
|
// the date constructor remaps years 0-99 to 1900-1999
|
|
if (y < 100 && y >= 0) {
|
|
// preserve leap years using a full 400 year cycle, then reset
|
|
return new Date(y + 400, m, d) - MS_PER_400_YEARS;
|
|
} else {
|
|
return new Date(y, m, d).valueOf();
|
|
}
|
|
}
|
|
|
|
function utcStartOfDate(y, m, d) {
|
|
// Date.UTC remaps years 0-99 to 1900-1999
|
|
if (y < 100 && y >= 0) {
|
|
// preserve leap years using a full 400 year cycle, then reset
|
|
return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS;
|
|
} else {
|
|
return Date.UTC(y, m, d);
|
|
}
|
|
}
|
|
|
|
function startOf(units) {
|
|
var time, startOfDate;
|
|
units = normalizeUnits(units);
|
|
if (units === undefined || units === 'millisecond' || !this.isValid()) {
|
|
return this;
|
|
}
|
|
|
|
startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
|
|
|
|
switch (units) {
|
|
case 'year':
|
|
time = startOfDate(this.year(), 0, 1);
|
|
break;
|
|
case 'quarter':
|
|
time = startOfDate(
|
|
this.year(),
|
|
this.month() - (this.month() % 3),
|
|
1
|
|
);
|
|
break;
|
|
case 'month':
|
|
time = startOfDate(this.year(), this.month(), 1);
|
|
break;
|
|
case 'week':
|
|
time = startOfDate(
|
|
this.year(),
|
|
this.month(),
|
|
this.date() - this.weekday()
|
|
);
|
|
break;
|
|
case 'isoWeek':
|
|
time = startOfDate(
|
|
this.year(),
|
|
this.month(),
|
|
this.date() - (this.isoWeekday() - 1)
|
|
);
|
|
break;
|
|
case 'day':
|
|
case 'date':
|
|
time = startOfDate(this.year(), this.month(), this.date());
|
|
break;
|
|
case 'hour':
|
|
time = this._d.valueOf();
|
|
time -= mod$1(
|
|
time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE),
|
|
MS_PER_HOUR
|
|
);
|
|
break;
|
|
case 'minute':
|
|
time = this._d.valueOf();
|
|
time -= mod$1(time, MS_PER_MINUTE);
|
|
break;
|
|
case 'second':
|
|
time = this._d.valueOf();
|
|
time -= mod$1(time, MS_PER_SECOND);
|
|
break;
|
|
}
|
|
|
|
this._d.setTime(time);
|
|
hooks.updateOffset(this, true);
|
|
return this;
|
|
}
|
|
|
|
function endOf(units) {
|
|
var time, startOfDate;
|
|
units = normalizeUnits(units);
|
|
if (units === undefined || units === 'millisecond' || !this.isValid()) {
|
|
return this;
|
|
}
|
|
|
|
startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
|
|
|
|
switch (units) {
|
|
case 'year':
|
|
time = startOfDate(this.year() + 1, 0, 1) - 1;
|
|
break;
|
|
case 'quarter':
|
|
time =
|
|
startOfDate(
|
|
this.year(),
|
|
this.month() - (this.month() % 3) + 3,
|
|
1
|
|
) - 1;
|
|
break;
|
|
case 'month':
|
|
time = startOfDate(this.year(), this.month() + 1, 1) - 1;
|
|
break;
|
|
case 'week':
|
|
time =
|
|
startOfDate(
|
|
this.year(),
|
|
this.month(),
|
|
this.date() - this.weekday() + 7
|
|
) - 1;
|
|
break;
|
|
case 'isoWeek':
|
|
time =
|
|
startOfDate(
|
|
this.year(),
|
|
this.month(),
|
|
this.date() - (this.isoWeekday() - 1) + 7
|
|
) - 1;
|
|
break;
|
|
case 'day':
|
|
case 'date':
|
|
time = startOfDate(this.year(), this.month(), this.date() + 1) - 1;
|
|
break;
|
|
case 'hour':
|
|
time = this._d.valueOf();
|
|
time +=
|
|
MS_PER_HOUR -
|
|
mod$1(
|
|
time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE),
|
|
MS_PER_HOUR
|
|
) -
|
|
1;
|
|
break;
|
|
case 'minute':
|
|
time = this._d.valueOf();
|
|
time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1;
|
|
break;
|
|
case 'second':
|
|
time = this._d.valueOf();
|
|
time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1;
|
|
break;
|
|
}
|
|
|
|
this._d.setTime(time);
|
|
hooks.updateOffset(this, true);
|
|
return this;
|
|
}
|
|
|
|
function valueOf() {
|
|
return this._d.valueOf() - (this._offset || 0) * 60000;
|
|
}
|
|
|
|
function unix() {
|
|
return Math.floor(this.valueOf() / 1000);
|
|
}
|
|
|
|
function toDate() {
|
|
return new Date(this.valueOf());
|
|
}
|
|
|
|
function toArray() {
|
|
var m = this;
|
|
return [
|
|
m.year(),
|
|
m.month(),
|
|
m.date(),
|
|
m.hour(),
|
|
m.minute(),
|
|
m.second(),
|
|
m.millisecond(),
|
|
];
|
|
}
|
|
|
|
function toObject() {
|
|
var m = this;
|
|
return {
|
|
years: m.year(),
|
|
months: m.month(),
|
|
date: m.date(),
|
|
hours: m.hours(),
|
|
minutes: m.minutes(),
|
|
seconds: m.seconds(),
|
|
milliseconds: m.milliseconds(),
|
|
};
|
|
}
|
|
|
|
function toJSON() {
|
|
// new Date(NaN).toJSON() === null
|
|
return this.isValid() ? this.toISOString() : null;
|
|
}
|
|
|
|
function isValid$2() {
|
|
return isValid(this);
|
|
}
|
|
|
|
function parsingFlags() {
|
|
return extend({}, getParsingFlags(this));
|
|
}
|
|
|
|
function invalidAt() {
|
|
return getParsingFlags(this).overflow;
|
|
}
|
|
|
|
function creationData() {
|
|
return {
|
|
input: this._i,
|
|
format: this._f,
|
|
locale: this._locale,
|
|
isUTC: this._isUTC,
|
|
strict: this._strict,
|
|
};
|
|
}
|
|
|
|
addFormatToken('N', 0, 0, 'eraAbbr');
|
|
addFormatToken('NN', 0, 0, 'eraAbbr');
|
|
addFormatToken('NNN', 0, 0, 'eraAbbr');
|
|
addFormatToken('NNNN', 0, 0, 'eraName');
|
|
addFormatToken('NNNNN', 0, 0, 'eraNarrow');
|
|
|
|
addFormatToken('y', ['y', 1], 'yo', 'eraYear');
|
|
addFormatToken('y', ['yy', 2], 0, 'eraYear');
|
|
addFormatToken('y', ['yyy', 3], 0, 'eraYear');
|
|
addFormatToken('y', ['yyyy', 4], 0, 'eraYear');
|
|
|
|
addRegexToken('N', matchEraAbbr);
|
|
addRegexToken('NN', matchEraAbbr);
|
|
addRegexToken('NNN', matchEraAbbr);
|
|
addRegexToken('NNNN', matchEraName);
|
|
addRegexToken('NNNNN', matchEraNarrow);
|
|
|
|
addParseToken(
|
|
['N', 'NN', 'NNN', 'NNNN', 'NNNNN'],
|
|
function (input, array, config, token) {
|
|
var era = config._locale.erasParse(input, token, config._strict);
|
|
if (era) {
|
|
getParsingFlags(config).era = era;
|
|
} else {
|
|
getParsingFlags(config).invalidEra = input;
|
|
}
|
|
}
|
|
);
|
|
|
|
addRegexToken('y', matchUnsigned);
|
|
addRegexToken('yy', matchUnsigned);
|
|
addRegexToken('yyy', matchUnsigned);
|
|
addRegexToken('yyyy', matchUnsigned);
|
|
addRegexToken('yo', matchEraYearOrdinal);
|
|
|
|
addParseToken(['y', 'yy', 'yyy', 'yyyy'], YEAR);
|
|
addParseToken(['yo'], function (input, array, config, token) {
|
|
var match;
|
|
if (config._locale._eraYearOrdinalRegex) {
|
|
match = input.match(config._locale._eraYearOrdinalRegex);
|
|
}
|
|
|
|
if (config._locale.eraYearOrdinalParse) {
|
|
array[YEAR] = config._locale.eraYearOrdinalParse(input, match);
|
|
} else {
|
|
array[YEAR] = parseInt(input, 10);
|
|
}
|
|
});
|
|
|
|
function localeEras(m, format) {
|
|
var i,
|
|
l,
|
|
date,
|
|
eras = this._eras || getLocale('en')._eras;
|
|
for (i = 0, l = eras.length; i < l; ++i) {
|
|
switch (typeof eras[i].since) {
|
|
case 'string':
|
|
// truncate time
|
|
date = hooks(eras[i].since).startOf('day');
|
|
eras[i].since = date.valueOf();
|
|
break;
|
|
}
|
|
|
|
switch (typeof eras[i].until) {
|
|
case 'undefined':
|
|
eras[i].until = +Infinity;
|
|
break;
|
|
case 'string':
|
|
// truncate time
|
|
date = hooks(eras[i].until).startOf('day').valueOf();
|
|
eras[i].until = date.valueOf();
|
|
break;
|
|
}
|
|
}
|
|
return eras;
|
|
}
|
|
|
|
function localeErasParse(eraName, format, strict) {
|
|
var i,
|
|
l,
|
|
eras = this.eras(),
|
|
name,
|
|
abbr,
|
|
narrow;
|
|
eraName = eraName.toUpperCase();
|
|
|
|
for (i = 0, l = eras.length; i < l; ++i) {
|
|
name = eras[i].name.toUpperCase();
|
|
abbr = eras[i].abbr.toUpperCase();
|
|
narrow = eras[i].narrow.toUpperCase();
|
|
|
|
if (strict) {
|
|
switch (format) {
|
|
case 'N':
|
|
case 'NN':
|
|
case 'NNN':
|
|
if (abbr === eraName) {
|
|
return eras[i];
|
|
}
|
|
break;
|
|
|
|
case 'NNNN':
|
|
if (name === eraName) {
|
|
return eras[i];
|
|
}
|
|
break;
|
|
|
|
case 'NNNNN':
|
|
if (narrow === eraName) {
|
|
return eras[i];
|
|
}
|
|
break;
|
|
}
|
|
} else if ([name, abbr, narrow].indexOf(eraName) >= 0) {
|
|
return eras[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
function localeErasConvertYear(era, year) {
|
|
var dir = era.since <= era.until ? +1 : -1;
|
|
if (year === undefined) {
|
|
return hooks(era.since).year();
|
|
} else {
|
|
return hooks(era.since).year() + (year - era.offset) * dir;
|
|
}
|
|
}
|
|
|
|
function getEraName() {
|
|
var i,
|
|
l,
|
|
val,
|
|
eras = this.localeData().eras();
|
|
for (i = 0, l = eras.length; i < l; ++i) {
|
|
// truncate time
|
|
val = this.clone().startOf('day').valueOf();
|
|
|
|
if (eras[i].since <= val && val <= eras[i].until) {
|
|
return eras[i].name;
|
|
}
|
|
if (eras[i].until <= val && val <= eras[i].since) {
|
|
return eras[i].name;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function getEraNarrow() {
|
|
var i,
|
|
l,
|
|
val,
|
|
eras = this.localeData().eras();
|
|
for (i = 0, l = eras.length; i < l; ++i) {
|
|
// truncate time
|
|
val = this.clone().startOf('day').valueOf();
|
|
|
|
if (eras[i].since <= val && val <= eras[i].until) {
|
|
return eras[i].narrow;
|
|
}
|
|
if (eras[i].until <= val && val <= eras[i].since) {
|
|
return eras[i].narrow;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function getEraAbbr() {
|
|
var i,
|
|
l,
|
|
val,
|
|
eras = this.localeData().eras();
|
|
for (i = 0, l = eras.length; i < l; ++i) {
|
|
// truncate time
|
|
val = this.clone().startOf('day').valueOf();
|
|
|
|
if (eras[i].since <= val && val <= eras[i].until) {
|
|
return eras[i].abbr;
|
|
}
|
|
if (eras[i].until <= val && val <= eras[i].since) {
|
|
return eras[i].abbr;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function getEraYear() {
|
|
var i,
|
|
l,
|
|
dir,
|
|
val,
|
|
eras = this.localeData().eras();
|
|
for (i = 0, l = eras.length; i < l; ++i) {
|
|
dir = eras[i].since <= eras[i].until ? +1 : -1;
|
|
|
|
// truncate time
|
|
val = this.clone().startOf('day').valueOf();
|
|
|
|
if (
|
|
(eras[i].since <= val && val <= eras[i].until) ||
|
|
(eras[i].until <= val && val <= eras[i].since)
|
|
) {
|
|
return (
|
|
(this.year() - hooks(eras[i].since).year()) * dir +
|
|
eras[i].offset
|
|
);
|
|
}
|
|
}
|
|
|
|
return this.year();
|
|
}
|
|
|
|
function erasNameRegex(isStrict) {
|
|
if (!hasOwnProp(this, '_erasNameRegex')) {
|
|
computeErasParse.call(this);
|
|
}
|
|
return isStrict ? this._erasNameRegex : this._erasRegex;
|
|
}
|
|
|
|
function erasAbbrRegex(isStrict) {
|
|
if (!hasOwnProp(this, '_erasAbbrRegex')) {
|
|
computeErasParse.call(this);
|
|
}
|
|
return isStrict ? this._erasAbbrRegex : this._erasRegex;
|
|
}
|
|
|
|
function erasNarrowRegex(isStrict) {
|
|
if (!hasOwnProp(this, '_erasNarrowRegex')) {
|
|
computeErasParse.call(this);
|
|
}
|
|
return isStrict ? this._erasNarrowRegex : this._erasRegex;
|
|
}
|
|
|
|
function matchEraAbbr(isStrict, locale) {
|
|
return locale.erasAbbrRegex(isStrict);
|
|
}
|
|
|
|
function matchEraName(isStrict, locale) {
|
|
return locale.erasNameRegex(isStrict);
|
|
}
|
|
|
|
function matchEraNarrow(isStrict, locale) {
|
|
return locale.erasNarrowRegex(isStrict);
|
|
}
|
|
|
|
function matchEraYearOrdinal(isStrict, locale) {
|
|
return locale._eraYearOrdinalRegex || matchUnsigned;
|
|
}
|
|
|
|
function computeErasParse() {
|
|
var abbrPieces = [],
|
|
namePieces = [],
|
|
narrowPieces = [],
|
|
mixedPieces = [],
|
|
i,
|
|
l,
|
|
erasName,
|
|
erasAbbr,
|
|
erasNarrow,
|
|
eras = this.eras();
|
|
|
|
for (i = 0, l = eras.length; i < l; ++i) {
|
|
erasName = regexEscape(eras[i].name);
|
|
erasAbbr = regexEscape(eras[i].abbr);
|
|
erasNarrow = regexEscape(eras[i].narrow);
|
|
|
|
namePieces.push(erasName);
|
|
abbrPieces.push(erasAbbr);
|
|
narrowPieces.push(erasNarrow);
|
|
mixedPieces.push(erasName);
|
|
mixedPieces.push(erasAbbr);
|
|
mixedPieces.push(erasNarrow);
|
|
}
|
|
|
|
this._erasRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
|
|
this._erasNameRegex = new RegExp('^(' + namePieces.join('|') + ')', 'i');
|
|
this._erasAbbrRegex = new RegExp('^(' + abbrPieces.join('|') + ')', 'i');
|
|
this._erasNarrowRegex = new RegExp(
|
|
'^(' + narrowPieces.join('|') + ')',
|
|
'i'
|
|
);
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken(0, ['gg', 2], 0, function () {
|
|
return this.weekYear() % 100;
|
|
});
|
|
|
|
addFormatToken(0, ['GG', 2], 0, function () {
|
|
return this.isoWeekYear() % 100;
|
|
});
|
|
|
|
function addWeekYearFormatToken(token, getter) {
|
|
addFormatToken(0, [token, token.length], 0, getter);
|
|
}
|
|
|
|
addWeekYearFormatToken('gggg', 'weekYear');
|
|
addWeekYearFormatToken('ggggg', 'weekYear');
|
|
addWeekYearFormatToken('GGGG', 'isoWeekYear');
|
|
addWeekYearFormatToken('GGGGG', 'isoWeekYear');
|
|
|
|
// ALIASES
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('G', matchSigned);
|
|
addRegexToken('g', matchSigned);
|
|
addRegexToken('GG', match1to2, match2);
|
|
addRegexToken('gg', match1to2, match2);
|
|
addRegexToken('GGGG', match1to4, match4);
|
|
addRegexToken('gggg', match1to4, match4);
|
|
addRegexToken('GGGGG', match1to6, match6);
|
|
addRegexToken('ggggg', match1to6, match6);
|
|
|
|
addWeekParseToken(
|
|
['gggg', 'ggggg', 'GGGG', 'GGGGG'],
|
|
function (input, week, config, token) {
|
|
week[token.substr(0, 2)] = toInt(input);
|
|
}
|
|
);
|
|
|
|
addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
|
|
week[token] = hooks.parseTwoDigitYear(input);
|
|
});
|
|
|
|
// MOMENTS
|
|
|
|
function getSetWeekYear(input) {
|
|
return getSetWeekYearHelper.call(
|
|
this,
|
|
input,
|
|
this.week(),
|
|
this.weekday() + this.localeData()._week.dow,
|
|
this.localeData()._week.dow,
|
|
this.localeData()._week.doy
|
|
);
|
|
}
|
|
|
|
function getSetISOWeekYear(input) {
|
|
return getSetWeekYearHelper.call(
|
|
this,
|
|
input,
|
|
this.isoWeek(),
|
|
this.isoWeekday(),
|
|
1,
|
|
4
|
|
);
|
|
}
|
|
|
|
function getISOWeeksInYear() {
|
|
return weeksInYear(this.year(), 1, 4);
|
|
}
|
|
|
|
function getISOWeeksInISOWeekYear() {
|
|
return weeksInYear(this.isoWeekYear(), 1, 4);
|
|
}
|
|
|
|
function getWeeksInYear() {
|
|
var weekInfo = this.localeData()._week;
|
|
return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
|
|
}
|
|
|
|
function getWeeksInWeekYear() {
|
|
var weekInfo = this.localeData()._week;
|
|
return weeksInYear(this.weekYear(), weekInfo.dow, weekInfo.doy);
|
|
}
|
|
|
|
function getSetWeekYearHelper(input, week, weekday, dow, doy) {
|
|
var weeksTarget;
|
|
if (input == null) {
|
|
return weekOfYear(this, dow, doy).year;
|
|
} else {
|
|
weeksTarget = weeksInYear(input, dow, doy);
|
|
if (week > weeksTarget) {
|
|
week = weeksTarget;
|
|
}
|
|
return setWeekAll.call(this, input, week, weekday, dow, doy);
|
|
}
|
|
}
|
|
|
|
function setWeekAll(weekYear, week, weekday, dow, doy) {
|
|
var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
|
|
date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
|
|
|
|
this.year(date.getUTCFullYear());
|
|
this.month(date.getUTCMonth());
|
|
this.date(date.getUTCDate());
|
|
return this;
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('Q', 0, 'Qo', 'quarter');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('Q', match1);
|
|
addParseToken('Q', function (input, array) {
|
|
array[MONTH] = (toInt(input) - 1) * 3;
|
|
});
|
|
|
|
// MOMENTS
|
|
|
|
function getSetQuarter(input) {
|
|
return input == null
|
|
? Math.ceil((this.month() + 1) / 3)
|
|
: this.month((input - 1) * 3 + (this.month() % 3));
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('D', ['DD', 2], 'Do', 'date');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('D', match1to2, match1to2NoLeadingZero);
|
|
addRegexToken('DD', match1to2, match2);
|
|
addRegexToken('Do', function (isStrict, locale) {
|
|
// TODO: Remove "ordinalParse" fallback in next major release.
|
|
return isStrict
|
|
? locale._dayOfMonthOrdinalParse || locale._ordinalParse
|
|
: locale._dayOfMonthOrdinalParseLenient;
|
|
});
|
|
|
|
addParseToken(['D', 'DD'], DATE);
|
|
addParseToken('Do', function (input, array) {
|
|
array[DATE] = toInt(input.match(match1to2)[0]);
|
|
});
|
|
|
|
// MOMENTS
|
|
|
|
var getSetDayOfMonth = makeGetSet('Date', true);
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('DDD', match1to3);
|
|
addRegexToken('DDDD', match3);
|
|
addParseToken(['DDD', 'DDDD'], function (input, array, config) {
|
|
config._dayOfYear = toInt(input);
|
|
});
|
|
|
|
// HELPERS
|
|
|
|
// MOMENTS
|
|
|
|
function getSetDayOfYear(input) {
|
|
var dayOfYear =
|
|
Math.round(
|
|
(this.clone().startOf('day') - this.clone().startOf('year')) / 864e5
|
|
) + 1;
|
|
return input == null ? dayOfYear : this.add(input - dayOfYear, 'd');
|
|
}
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('m', ['mm', 2], 0, 'minute');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('m', match1to2, match1to2HasZero);
|
|
addRegexToken('mm', match1to2, match2);
|
|
addParseToken(['m', 'mm'], MINUTE);
|
|
|
|
// MOMENTS
|
|
|
|
var getSetMinute = makeGetSet('Minutes', false);
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('s', ['ss', 2], 0, 'second');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('s', match1to2, match1to2HasZero);
|
|
addRegexToken('ss', match1to2, match2);
|
|
addParseToken(['s', 'ss'], SECOND);
|
|
|
|
// MOMENTS
|
|
|
|
var getSetSecond = makeGetSet('Seconds', false);
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('S', 0, 0, function () {
|
|
return ~~(this.millisecond() / 100);
|
|
});
|
|
|
|
addFormatToken(0, ['SS', 2], 0, function () {
|
|
return ~~(this.millisecond() / 10);
|
|
});
|
|
|
|
addFormatToken(0, ['SSS', 3], 0, 'millisecond');
|
|
addFormatToken(0, ['SSSS', 4], 0, function () {
|
|
return this.millisecond() * 10;
|
|
});
|
|
addFormatToken(0, ['SSSSS', 5], 0, function () {
|
|
return this.millisecond() * 100;
|
|
});
|
|
addFormatToken(0, ['SSSSSS', 6], 0, function () {
|
|
return this.millisecond() * 1000;
|
|
});
|
|
addFormatToken(0, ['SSSSSSS', 7], 0, function () {
|
|
return this.millisecond() * 10000;
|
|
});
|
|
addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
|
|
return this.millisecond() * 100000;
|
|
});
|
|
addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
|
|
return this.millisecond() * 1000000;
|
|
});
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('S', match1to3, match1);
|
|
addRegexToken('SS', match1to3, match2);
|
|
addRegexToken('SSS', match1to3, match3);
|
|
|
|
var token, getSetMillisecond;
|
|
for (token = 'SSSS'; token.length <= 9; token += 'S') {
|
|
addRegexToken(token, matchUnsigned);
|
|
}
|
|
|
|
function parseMs(input, array) {
|
|
array[MILLISECOND] = toInt(('0.' + input) * 1000);
|
|
}
|
|
|
|
for (token = 'S'; token.length <= 9; token += 'S') {
|
|
addParseToken(token, parseMs);
|
|
}
|
|
|
|
getSetMillisecond = makeGetSet('Milliseconds', false);
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('z', 0, 0, 'zoneAbbr');
|
|
addFormatToken('zz', 0, 0, 'zoneName');
|
|
|
|
// MOMENTS
|
|
|
|
function getZoneAbbr() {
|
|
return this._isUTC ? 'UTC' : '';
|
|
}
|
|
|
|
function getZoneName() {
|
|
return this._isUTC ? 'Coordinated Universal Time' : '';
|
|
}
|
|
|
|
var proto = Moment.prototype;
|
|
|
|
proto.add = add;
|
|
proto.calendar = calendar$1;
|
|
proto.clone = clone;
|
|
proto.diff = diff;
|
|
proto.endOf = endOf;
|
|
proto.format = format;
|
|
proto.from = from;
|
|
proto.fromNow = fromNow;
|
|
proto.to = to;
|
|
proto.toNow = toNow;
|
|
proto.get = stringGet;
|
|
proto.invalidAt = invalidAt;
|
|
proto.isAfter = isAfter;
|
|
proto.isBefore = isBefore;
|
|
proto.isBetween = isBetween;
|
|
proto.isSame = isSame;
|
|
proto.isSameOrAfter = isSameOrAfter;
|
|
proto.isSameOrBefore = isSameOrBefore;
|
|
proto.isValid = isValid$2;
|
|
proto.lang = lang;
|
|
proto.locale = locale;
|
|
proto.localeData = localeData;
|
|
proto.max = prototypeMax;
|
|
proto.min = prototypeMin;
|
|
proto.parsingFlags = parsingFlags;
|
|
proto.set = stringSet;
|
|
proto.startOf = startOf;
|
|
proto.subtract = subtract;
|
|
proto.toArray = toArray;
|
|
proto.toObject = toObject;
|
|
proto.toDate = toDate;
|
|
proto.toISOString = toISOString;
|
|
proto.inspect = inspect;
|
|
if (typeof Symbol !== 'undefined' && Symbol.for != null) {
|
|
proto[Symbol.for('nodejs.util.inspect.custom')] = function () {
|
|
return 'Moment<' + this.format() + '>';
|
|
};
|
|
}
|
|
proto.toJSON = toJSON;
|
|
proto.toString = toString;
|
|
proto.unix = unix;
|
|
proto.valueOf = valueOf;
|
|
proto.creationData = creationData;
|
|
proto.eraName = getEraName;
|
|
proto.eraNarrow = getEraNarrow;
|
|
proto.eraAbbr = getEraAbbr;
|
|
proto.eraYear = getEraYear;
|
|
proto.year = getSetYear;
|
|
proto.isLeapYear = getIsLeapYear;
|
|
proto.weekYear = getSetWeekYear;
|
|
proto.isoWeekYear = getSetISOWeekYear;
|
|
proto.quarter = proto.quarters = getSetQuarter;
|
|
proto.month = getSetMonth;
|
|
proto.daysInMonth = getDaysInMonth;
|
|
proto.week = proto.weeks = getSetWeek;
|
|
proto.isoWeek = proto.isoWeeks = getSetISOWeek;
|
|
proto.weeksInYear = getWeeksInYear;
|
|
proto.weeksInWeekYear = getWeeksInWeekYear;
|
|
proto.isoWeeksInYear = getISOWeeksInYear;
|
|
proto.isoWeeksInISOWeekYear = getISOWeeksInISOWeekYear;
|
|
proto.date = getSetDayOfMonth;
|
|
proto.day = proto.days = getSetDayOfWeek;
|
|
proto.weekday = getSetLocaleDayOfWeek;
|
|
proto.isoWeekday = getSetISODayOfWeek;
|
|
proto.dayOfYear = getSetDayOfYear;
|
|
proto.hour = proto.hours = getSetHour;
|
|
proto.minute = proto.minutes = getSetMinute;
|
|
proto.second = proto.seconds = getSetSecond;
|
|
proto.millisecond = proto.milliseconds = getSetMillisecond;
|
|
proto.utcOffset = getSetOffset;
|
|
proto.utc = setOffsetToUTC;
|
|
proto.local = setOffsetToLocal;
|
|
proto.parseZone = setOffsetToParsedOffset;
|
|
proto.hasAlignedHourOffset = hasAlignedHourOffset;
|
|
proto.isDST = isDaylightSavingTime;
|
|
proto.isLocal = isLocal;
|
|
proto.isUtcOffset = isUtcOffset;
|
|
proto.isUtc = isUtc;
|
|
proto.isUTC = isUtc;
|
|
proto.zoneAbbr = getZoneAbbr;
|
|
proto.zoneName = getZoneName;
|
|
proto.dates = deprecate(
|
|
'dates accessor is deprecated. Use date instead.',
|
|
getSetDayOfMonth
|
|
);
|
|
proto.months = deprecate(
|
|
'months accessor is deprecated. Use month instead',
|
|
getSetMonth
|
|
);
|
|
proto.years = deprecate(
|
|
'years accessor is deprecated. Use year instead',
|
|
getSetYear
|
|
);
|
|
proto.zone = deprecate(
|
|
'moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/',
|
|
getSetZone
|
|
);
|
|
proto.isDSTShifted = deprecate(
|
|
'isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information',
|
|
isDaylightSavingTimeShifted
|
|
);
|
|
|
|
function createUnix(input) {
|
|
return createLocal(input * 1000);
|
|
}
|
|
|
|
function createInZone() {
|
|
return createLocal.apply(null, arguments).parseZone();
|
|
}
|
|
|
|
function preParsePostFormat(string) {
|
|
return string;
|
|
}
|
|
|
|
var proto$1 = Locale.prototype;
|
|
|
|
proto$1.calendar = calendar;
|
|
proto$1.longDateFormat = longDateFormat;
|
|
proto$1.invalidDate = invalidDate;
|
|
proto$1.ordinal = ordinal;
|
|
proto$1.preparse = preParsePostFormat;
|
|
proto$1.postformat = preParsePostFormat;
|
|
proto$1.relativeTime = relativeTime;
|
|
proto$1.pastFuture = pastFuture;
|
|
proto$1.set = set;
|
|
proto$1.eras = localeEras;
|
|
proto$1.erasParse = localeErasParse;
|
|
proto$1.erasConvertYear = localeErasConvertYear;
|
|
proto$1.erasAbbrRegex = erasAbbrRegex;
|
|
proto$1.erasNameRegex = erasNameRegex;
|
|
proto$1.erasNarrowRegex = erasNarrowRegex;
|
|
|
|
proto$1.months = localeMonths;
|
|
proto$1.monthsShort = localeMonthsShort;
|
|
proto$1.monthsParse = localeMonthsParse;
|
|
proto$1.monthsRegex = monthsRegex;
|
|
proto$1.monthsShortRegex = monthsShortRegex;
|
|
proto$1.week = localeWeek;
|
|
proto$1.firstDayOfYear = localeFirstDayOfYear;
|
|
proto$1.firstDayOfWeek = localeFirstDayOfWeek;
|
|
|
|
proto$1.weekdays = localeWeekdays;
|
|
proto$1.weekdaysMin = localeWeekdaysMin;
|
|
proto$1.weekdaysShort = localeWeekdaysShort;
|
|
proto$1.weekdaysParse = localeWeekdaysParse;
|
|
|
|
proto$1.weekdaysRegex = weekdaysRegex;
|
|
proto$1.weekdaysShortRegex = weekdaysShortRegex;
|
|
proto$1.weekdaysMinRegex = weekdaysMinRegex;
|
|
|
|
proto$1.isPM = localeIsPM;
|
|
proto$1.meridiem = localeMeridiem;
|
|
|
|
function get$1(format, index, field, setter) {
|
|
var locale = getLocale(),
|
|
utc = createUTC().set(setter, index);
|
|
return locale[field](utc, format);
|
|
}
|
|
|
|
function listMonthsImpl(format, index, field) {
|
|
if (isNumber(format)) {
|
|
index = format;
|
|
format = undefined;
|
|
}
|
|
|
|
format = format || '';
|
|
|
|
if (index != null) {
|
|
return get$1(format, index, field, 'month');
|
|
}
|
|
|
|
var i,
|
|
out = [];
|
|
for (i = 0; i < 12; i++) {
|
|
out[i] = get$1(format, i, field, 'month');
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// ()
|
|
// (5)
|
|
// (fmt, 5)
|
|
// (fmt)
|
|
// (true)
|
|
// (true, 5)
|
|
// (true, fmt, 5)
|
|
// (true, fmt)
|
|
function listWeekdaysImpl(localeSorted, format, index, field) {
|
|
if (typeof localeSorted === 'boolean') {
|
|
if (isNumber(format)) {
|
|
index = format;
|
|
format = undefined;
|
|
}
|
|
|
|
format = format || '';
|
|
} else {
|
|
format = localeSorted;
|
|
index = format;
|
|
localeSorted = false;
|
|
|
|
if (isNumber(format)) {
|
|
index = format;
|
|
format = undefined;
|
|
}
|
|
|
|
format = format || '';
|
|
}
|
|
|
|
var locale = getLocale(),
|
|
shift = localeSorted ? locale._week.dow : 0,
|
|
i,
|
|
out = [];
|
|
|
|
if (index != null) {
|
|
return get$1(format, (index + shift) % 7, field, 'day');
|
|
}
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
out[i] = get$1(format, (i + shift) % 7, field, 'day');
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function listMonths(format, index) {
|
|
return listMonthsImpl(format, index, 'months');
|
|
}
|
|
|
|
function listMonthsShort(format, index) {
|
|
return listMonthsImpl(format, index, 'monthsShort');
|
|
}
|
|
|
|
function listWeekdays(localeSorted, format, index) {
|
|
return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
|
|
}
|
|
|
|
function listWeekdaysShort(localeSorted, format, index) {
|
|
return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
|
|
}
|
|
|
|
function listWeekdaysMin(localeSorted, format, index) {
|
|
return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
|
|
}
|
|
|
|
getSetGlobalLocale('en', {
|
|
eras: [
|
|
{
|
|
since: '0001-01-01',
|
|
until: +Infinity,
|
|
offset: 1,
|
|
name: 'Anno Domini',
|
|
narrow: 'AD',
|
|
abbr: 'AD',
|
|
},
|
|
{
|
|
since: '0000-12-31',
|
|
until: -Infinity,
|
|
offset: 1,
|
|
name: 'Before Christ',
|
|
narrow: 'BC',
|
|
abbr: 'BC',
|
|
},
|
|
],
|
|
dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
|
|
ordinal: function (number) {
|
|
var b = number % 10,
|
|
output =
|
|
toInt((number % 100) / 10) === 1
|
|
? 'th'
|
|
: b === 1
|
|
? 'st'
|
|
: b === 2
|
|
? 'nd'
|
|
: b === 3
|
|
? 'rd'
|
|
: 'th';
|
|
return number + output;
|
|
},
|
|
});
|
|
|
|
// Side effect imports
|
|
|
|
hooks.lang = deprecate(
|
|
'moment.lang is deprecated. Use moment.locale instead.',
|
|
getSetGlobalLocale
|
|
);
|
|
hooks.langData = deprecate(
|
|
'moment.langData is deprecated. Use moment.localeData instead.',
|
|
getLocale
|
|
);
|
|
|
|
var mathAbs = Math.abs;
|
|
|
|
function abs() {
|
|
var data = this._data;
|
|
|
|
this._milliseconds = mathAbs(this._milliseconds);
|
|
this._days = mathAbs(this._days);
|
|
this._months = mathAbs(this._months);
|
|
|
|
data.milliseconds = mathAbs(data.milliseconds);
|
|
data.seconds = mathAbs(data.seconds);
|
|
data.minutes = mathAbs(data.minutes);
|
|
data.hours = mathAbs(data.hours);
|
|
data.months = mathAbs(data.months);
|
|
data.years = mathAbs(data.years);
|
|
|
|
return this;
|
|
}
|
|
|
|
function addSubtract$1(duration, input, value, direction) {
|
|
var other = createDuration(input, value);
|
|
|
|
duration._milliseconds += direction * other._milliseconds;
|
|
duration._days += direction * other._days;
|
|
duration._months += direction * other._months;
|
|
|
|
return duration._bubble();
|
|
}
|
|
|
|
// supports only 2.0-style add(1, 's') or add(duration)
|
|
function add$1(input, value) {
|
|
return addSubtract$1(this, input, value, 1);
|
|
}
|
|
|
|
// supports only 2.0-style subtract(1, 's') or subtract(duration)
|
|
function subtract$1(input, value) {
|
|
return addSubtract$1(this, input, value, -1);
|
|
}
|
|
|
|
function absCeil(number) {
|
|
if (number < 0) {
|
|
return Math.floor(number);
|
|
} else {
|
|
return Math.ceil(number);
|
|
}
|
|
}
|
|
|
|
function bubble() {
|
|
var milliseconds = this._milliseconds,
|
|
days = this._days,
|
|
months = this._months,
|
|
data = this._data,
|
|
seconds,
|
|
minutes,
|
|
hours,
|
|
years,
|
|
monthsFromDays;
|
|
|
|
// if we have a mix of positive and negative values, bubble down first
|
|
// check: https://github.com/moment/moment/issues/2166
|
|
if (
|
|
!(
|
|
(milliseconds >= 0 && days >= 0 && months >= 0) ||
|
|
(milliseconds <= 0 && days <= 0 && months <= 0)
|
|
)
|
|
) {
|
|
milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
|
|
days = 0;
|
|
months = 0;
|
|
}
|
|
|
|
// The following code bubbles up values, see the tests for
|
|
// examples of what that means.
|
|
data.milliseconds = milliseconds % 1000;
|
|
|
|
seconds = absFloor(milliseconds / 1000);
|
|
data.seconds = seconds % 60;
|
|
|
|
minutes = absFloor(seconds / 60);
|
|
data.minutes = minutes % 60;
|
|
|
|
hours = absFloor(minutes / 60);
|
|
data.hours = hours % 24;
|
|
|
|
days += absFloor(hours / 24);
|
|
|
|
// convert days to months
|
|
monthsFromDays = absFloor(daysToMonths(days));
|
|
months += monthsFromDays;
|
|
days -= absCeil(monthsToDays(monthsFromDays));
|
|
|
|
// 12 months -> 1 year
|
|
years = absFloor(months / 12);
|
|
months %= 12;
|
|
|
|
data.days = days;
|
|
data.months = months;
|
|
data.years = years;
|
|
|
|
return this;
|
|
}
|
|
|
|
function daysToMonths(days) {
|
|
// 400 years have 146097 days (taking into account leap year rules)
|
|
// 400 years have 12 months === 4800
|
|
return (days * 4800) / 146097;
|
|
}
|
|
|
|
function monthsToDays(months) {
|
|
// the reverse of daysToMonths
|
|
return (months * 146097) / 4800;
|
|
}
|
|
|
|
function as(units) {
|
|
if (!this.isValid()) {
|
|
return NaN;
|
|
}
|
|
var days,
|
|
months,
|
|
milliseconds = this._milliseconds;
|
|
|
|
units = normalizeUnits(units);
|
|
|
|
if (units === 'month' || units === 'quarter' || units === 'year') {
|
|
days = this._days + milliseconds / 864e5;
|
|
months = this._months + daysToMonths(days);
|
|
switch (units) {
|
|
case 'month':
|
|
return months;
|
|
case 'quarter':
|
|
return months / 3;
|
|
case 'year':
|
|
return months / 12;
|
|
}
|
|
} else {
|
|
// handle milliseconds separately because of floating point math errors (issue #1867)
|
|
days = this._days + Math.round(monthsToDays(this._months));
|
|
switch (units) {
|
|
case 'week':
|
|
return days / 7 + milliseconds / 6048e5;
|
|
case 'day':
|
|
return days + milliseconds / 864e5;
|
|
case 'hour':
|
|
return days * 24 + milliseconds / 36e5;
|
|
case 'minute':
|
|
return days * 1440 + milliseconds / 6e4;
|
|
case 'second':
|
|
return days * 86400 + milliseconds / 1000;
|
|
// Math.floor prevents floating point math errors here
|
|
case 'millisecond':
|
|
return Math.floor(days * 864e5) + milliseconds;
|
|
default:
|
|
throw new Error('Unknown unit ' + units);
|
|
}
|
|
}
|
|
}
|
|
|
|
function makeAs(alias) {
|
|
return function () {
|
|
return this.as(alias);
|
|
};
|
|
}
|
|
|
|
var asMilliseconds = makeAs('ms'),
|
|
asSeconds = makeAs('s'),
|
|
asMinutes = makeAs('m'),
|
|
asHours = makeAs('h'),
|
|
asDays = makeAs('d'),
|
|
asWeeks = makeAs('w'),
|
|
asMonths = makeAs('M'),
|
|
asQuarters = makeAs('Q'),
|
|
asYears = makeAs('y'),
|
|
valueOf$1 = asMilliseconds;
|
|
|
|
function clone$1() {
|
|
return createDuration(this);
|
|
}
|
|
|
|
function get$2(units) {
|
|
units = normalizeUnits(units);
|
|
return this.isValid() ? this[units + 's']() : NaN;
|
|
}
|
|
|
|
function makeGetter(name) {
|
|
return function () {
|
|
return this.isValid() ? this._data[name] : NaN;
|
|
};
|
|
}
|
|
|
|
var milliseconds = makeGetter('milliseconds'),
|
|
seconds = makeGetter('seconds'),
|
|
minutes = makeGetter('minutes'),
|
|
hours = makeGetter('hours'),
|
|
days = makeGetter('days'),
|
|
months = makeGetter('months'),
|
|
years = makeGetter('years');
|
|
|
|
function weeks() {
|
|
return absFloor(this.days() / 7);
|
|
}
|
|
|
|
var round = Math.round,
|
|
thresholds = {
|
|
ss: 44, // a few seconds to seconds
|
|
s: 45, // seconds to minute
|
|
m: 45, // minutes to hour
|
|
h: 22, // hours to day
|
|
d: 26, // days to month/week
|
|
w: null, // weeks to month
|
|
M: 11, // months to year
|
|
};
|
|
|
|
// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
|
|
function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
|
|
return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
|
|
}
|
|
|
|
function relativeTime$1(posNegDuration, withoutSuffix, thresholds, locale) {
|
|
var duration = createDuration(posNegDuration).abs(),
|
|
seconds = round(duration.as('s')),
|
|
minutes = round(duration.as('m')),
|
|
hours = round(duration.as('h')),
|
|
days = round(duration.as('d')),
|
|
months = round(duration.as('M')),
|
|
weeks = round(duration.as('w')),
|
|
years = round(duration.as('y')),
|
|
a =
|
|
(seconds <= thresholds.ss && ['s', seconds]) ||
|
|
(seconds < thresholds.s && ['ss', seconds]) ||
|
|
(minutes <= 1 && ['m']) ||
|
|
(minutes < thresholds.m && ['mm', minutes]) ||
|
|
(hours <= 1 && ['h']) ||
|
|
(hours < thresholds.h && ['hh', hours]) ||
|
|
(days <= 1 && ['d']) ||
|
|
(days < thresholds.d && ['dd', days]);
|
|
|
|
if (thresholds.w != null) {
|
|
a =
|
|
a ||
|
|
(weeks <= 1 && ['w']) ||
|
|
(weeks < thresholds.w && ['ww', weeks]);
|
|
}
|
|
a = a ||
|
|
(months <= 1 && ['M']) ||
|
|
(months < thresholds.M && ['MM', months]) ||
|
|
(years <= 1 && ['y']) || ['yy', years];
|
|
|
|
a[2] = withoutSuffix;
|
|
a[3] = +posNegDuration > 0;
|
|
a[4] = locale;
|
|
return substituteTimeAgo.apply(null, a);
|
|
}
|
|
|
|
// This function allows you to set the rounding function for relative time strings
|
|
function getSetRelativeTimeRounding(roundingFunction) {
|
|
if (roundingFunction === undefined) {
|
|
return round;
|
|
}
|
|
if (typeof roundingFunction === 'function') {
|
|
round = roundingFunction;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// This function allows you to set a threshold for relative time strings
|
|
function getSetRelativeTimeThreshold(threshold, limit) {
|
|
if (thresholds[threshold] === undefined) {
|
|
return false;
|
|
}
|
|
if (limit === undefined) {
|
|
return thresholds[threshold];
|
|
}
|
|
thresholds[threshold] = limit;
|
|
if (threshold === 's') {
|
|
thresholds.ss = limit - 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function humanize(argWithSuffix, argThresholds) {
|
|
if (!this.isValid()) {
|
|
return this.localeData().invalidDate();
|
|
}
|
|
|
|
var withSuffix = false,
|
|
th = thresholds,
|
|
locale,
|
|
output;
|
|
|
|
if (typeof argWithSuffix === 'object') {
|
|
argThresholds = argWithSuffix;
|
|
argWithSuffix = false;
|
|
}
|
|
if (typeof argWithSuffix === 'boolean') {
|
|
withSuffix = argWithSuffix;
|
|
}
|
|
if (typeof argThresholds === 'object') {
|
|
th = Object.assign({}, thresholds, argThresholds);
|
|
if (argThresholds.s != null && argThresholds.ss == null) {
|
|
th.ss = argThresholds.s - 1;
|
|
}
|
|
}
|
|
|
|
locale = this.localeData();
|
|
output = relativeTime$1(this, !withSuffix, th, locale);
|
|
|
|
if (withSuffix) {
|
|
output = locale.pastFuture(+this, output);
|
|
}
|
|
|
|
return locale.postformat(output);
|
|
}
|
|
|
|
var abs$1 = Math.abs;
|
|
|
|
function sign(x) {
|
|
return (x > 0) - (x < 0) || +x;
|
|
}
|
|
|
|
function toISOString$1() {
|
|
// for ISO strings we do not use the normal bubbling rules:
|
|
// * milliseconds bubble up until they become hours
|
|
// * days do not bubble at all
|
|
// * months bubble up until they become years
|
|
// This is because there is no context-free conversion between hours and days
|
|
// (think of clock changes)
|
|
// and also not between days and months (28-31 days per month)
|
|
if (!this.isValid()) {
|
|
return this.localeData().invalidDate();
|
|
}
|
|
|
|
var seconds = abs$1(this._milliseconds) / 1000,
|
|
days = abs$1(this._days),
|
|
months = abs$1(this._months),
|
|
minutes,
|
|
hours,
|
|
years,
|
|
s,
|
|
total = this.asSeconds(),
|
|
totalSign,
|
|
ymSign,
|
|
daysSign,
|
|
hmsSign;
|
|
|
|
if (!total) {
|
|
// this is the same as C#'s (Noda) and python (isodate)...
|
|
// but not other JS (goog.date)
|
|
return 'P0D';
|
|
}
|
|
|
|
// 3600 seconds -> 60 minutes -> 1 hour
|
|
minutes = absFloor(seconds / 60);
|
|
hours = absFloor(minutes / 60);
|
|
seconds %= 60;
|
|
minutes %= 60;
|
|
|
|
// 12 months -> 1 year
|
|
years = absFloor(months / 12);
|
|
months %= 12;
|
|
|
|
// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
|
|
s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : '';
|
|
|
|
totalSign = total < 0 ? '-' : '';
|
|
ymSign = sign(this._months) !== sign(total) ? '-' : '';
|
|
daysSign = sign(this._days) !== sign(total) ? '-' : '';
|
|
hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : '';
|
|
|
|
return (
|
|
totalSign +
|
|
'P' +
|
|
(years ? ymSign + years + 'Y' : '') +
|
|
(months ? ymSign + months + 'M' : '') +
|
|
(days ? daysSign + days + 'D' : '') +
|
|
(hours || minutes || seconds ? 'T' : '') +
|
|
(hours ? hmsSign + hours + 'H' : '') +
|
|
(minutes ? hmsSign + minutes + 'M' : '') +
|
|
(seconds ? hmsSign + s + 'S' : '')
|
|
);
|
|
}
|
|
|
|
var proto$2 = Duration.prototype;
|
|
|
|
proto$2.isValid = isValid$1;
|
|
proto$2.abs = abs;
|
|
proto$2.add = add$1;
|
|
proto$2.subtract = subtract$1;
|
|
proto$2.as = as;
|
|
proto$2.asMilliseconds = asMilliseconds;
|
|
proto$2.asSeconds = asSeconds;
|
|
proto$2.asMinutes = asMinutes;
|
|
proto$2.asHours = asHours;
|
|
proto$2.asDays = asDays;
|
|
proto$2.asWeeks = asWeeks;
|
|
proto$2.asMonths = asMonths;
|
|
proto$2.asQuarters = asQuarters;
|
|
proto$2.asYears = asYears;
|
|
proto$2.valueOf = valueOf$1;
|
|
proto$2._bubble = bubble;
|
|
proto$2.clone = clone$1;
|
|
proto$2.get = get$2;
|
|
proto$2.milliseconds = milliseconds;
|
|
proto$2.seconds = seconds;
|
|
proto$2.minutes = minutes;
|
|
proto$2.hours = hours;
|
|
proto$2.days = days;
|
|
proto$2.weeks = weeks;
|
|
proto$2.months = months;
|
|
proto$2.years = years;
|
|
proto$2.humanize = humanize;
|
|
proto$2.toISOString = toISOString$1;
|
|
proto$2.toString = toISOString$1;
|
|
proto$2.toJSON = toISOString$1;
|
|
proto$2.locale = locale;
|
|
proto$2.localeData = localeData;
|
|
|
|
proto$2.toIsoString = deprecate(
|
|
'toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)',
|
|
toISOString$1
|
|
);
|
|
proto$2.lang = lang;
|
|
|
|
// FORMATTING
|
|
|
|
addFormatToken('X', 0, 0, 'unix');
|
|
addFormatToken('x', 0, 0, 'valueOf');
|
|
|
|
// PARSING
|
|
|
|
addRegexToken('x', matchSigned);
|
|
addRegexToken('X', matchTimestamp);
|
|
addParseToken('X', function (input, array, config) {
|
|
config._d = new Date(parseFloat(input) * 1000);
|
|
});
|
|
addParseToken('x', function (input, array, config) {
|
|
config._d = new Date(toInt(input));
|
|
});
|
|
|
|
//! moment.js
|
|
|
|
hooks.version = '2.30.1';
|
|
|
|
setHookCallback(createLocal);
|
|
|
|
hooks.fn = proto;
|
|
hooks.min = min;
|
|
hooks.max = max;
|
|
hooks.now = now;
|
|
hooks.utc = createUTC;
|
|
hooks.unix = createUnix;
|
|
hooks.months = listMonths;
|
|
hooks.isDate = isDate;
|
|
hooks.locale = getSetGlobalLocale;
|
|
hooks.invalid = createInvalid;
|
|
hooks.duration = createDuration;
|
|
hooks.isMoment = isMoment;
|
|
hooks.weekdays = listWeekdays;
|
|
hooks.parseZone = createInZone;
|
|
hooks.localeData = getLocale;
|
|
hooks.isDuration = isDuration;
|
|
hooks.monthsShort = listMonthsShort;
|
|
hooks.weekdaysMin = listWeekdaysMin;
|
|
hooks.defineLocale = defineLocale;
|
|
hooks.updateLocale = updateLocale;
|
|
hooks.locales = listLocales;
|
|
hooks.weekdaysShort = listWeekdaysShort;
|
|
hooks.normalizeUnits = normalizeUnits;
|
|
hooks.relativeTimeRounding = getSetRelativeTimeRounding;
|
|
hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
|
|
hooks.calendarFormat = getCalendarFormat;
|
|
hooks.prototype = proto;
|
|
|
|
// currently HTML5 input type only supports 24-hour formats
|
|
hooks.HTML5_FMT = {
|
|
DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // <input type="datetime-local" />
|
|
DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // <input type="datetime-local" step="1" />
|
|
DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // <input type="datetime-local" step="0.001" />
|
|
DATE: 'YYYY-MM-DD', // <input type="date" />
|
|
TIME: 'HH:mm', // <input type="time" />
|
|
TIME_SECONDS: 'HH:mm:ss', // <input type="time" step="1" />
|
|
TIME_MS: 'HH:mm:ss.SSS', // <input type="time" step="0.001" />
|
|
WEEK: 'GGGG-[W]WW', // <input type="week" />
|
|
MONTH: 'YYYY-MM', // <input type="month" />
|
|
};
|
|
|
|
return hooks;
|
|
|
|
})));
|
|
} (moment$1, moment$1.exports));
|
|
|
|
var momentExports = moment$1.exports;
|
|
var moment = /*@__PURE__*/getDefaultExportFromCjs(momentExports);
|
|
|
|
function imageTagProcessor(app, noteFile, settings, defaultdir) {
|
|
const unique = Math.random().toString(16).slice(2);
|
|
function processImageTag(match, anchor, link, caption, imgsize) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
logError("processImageTag: " + match);
|
|
if (!isUrl(link)) {
|
|
return match;
|
|
}
|
|
try {
|
|
var lock = new AsyncLock();
|
|
let fpath;
|
|
let fileData;
|
|
const opsys = process.platform;
|
|
const mediaDir = yield getMDir(app.app, noteFile, settings, defaultdir, unique);
|
|
yield app.ensureFolderExists(mediaDir);
|
|
const protocol = link.slice(0, 5);
|
|
if (protocol == "data:") {
|
|
logError("ReadBase64: \r\n" + fpath, false);
|
|
fileData = yield base64ToBuff(link);
|
|
}
|
|
else if (protocol == "file:") {
|
|
logError("Readlocal: \r\n" + fpath, false);
|
|
if (SUPPORTED_OS.win.includes(opsys)) {
|
|
fpath = link.replace("file:///", "");
|
|
}
|
|
else if (SUPPORTED_OS.unix.includes(opsys)) {
|
|
fpath = link.replace("file://", "");
|
|
}
|
|
else {
|
|
fpath = link.replace("file://", "");
|
|
}
|
|
fileData = yield readFromDisk(fpath);
|
|
if (fileData === null) {
|
|
fileData = yield readFromDisk(decodeURI(fpath));
|
|
}
|
|
}
|
|
else {
|
|
//Try to download several times
|
|
let trycount = 0;
|
|
while (trycount < settings.tryCount) {
|
|
fileData = yield downloadImage(link);
|
|
logError("\r\n\nDownloading (try): " + trycount + "\r\n\n");
|
|
if (fileData !== null) {
|
|
break;
|
|
}
|
|
trycount++;
|
|
}
|
|
}
|
|
if (fileData === null) {
|
|
logError("Cannot get an attachment content!", false);
|
|
return null;
|
|
}
|
|
if (Math.round(fileData.byteLength / 1024) < settings.filesizeLimit) {
|
|
logError("Lower limit of the file size!", false);
|
|
return null;
|
|
}
|
|
try {
|
|
const { fileName, needWrite } = yield lock.acquire(match, function () {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const parsedUrl = new url.URL(link);
|
|
let fileExt = yield getFileExt(fileData, parsedUrl.pathname);
|
|
if (fileExt == "png" && settings.PngToJpeg) {
|
|
const blob = new Blob([new Uint8Array(fileData)]);
|
|
fileData = yield blobToJpegArrayBuffer(blob, settings.JpegQuality * 0.01);
|
|
logError("arbuf: ");
|
|
logError(fileData);
|
|
}
|
|
const { fileName, needWrite } = yield chooseFileName(app.app.vault.adapter, mediaDir, link, fileData, settings);
|
|
return { fileName, needWrite };
|
|
});
|
|
});
|
|
if (needWrite && fileName) {
|
|
yield app.app.vault.createBinary(fileName, fileData);
|
|
}
|
|
if (fileName) {
|
|
let shortName = "";
|
|
const rdir = yield getRDir(noteFile, settings, fileName, link);
|
|
let pathWiki = rdir[0];
|
|
let pathMd = rdir[1];
|
|
if (settings.addNameOfFile && protocol == "file:") {
|
|
if (!app.app.vault.getConfig("useMarkdownLinks")) {
|
|
shortName = "\r\n[[" +
|
|
fileName +
|
|
"\|" +
|
|
rdir[2]["lnkurid"] + "]]\r\n";
|
|
}
|
|
else {
|
|
shortName = "\r\n[" +
|
|
rdir[2]["lnkurid"] +
|
|
"](" +
|
|
rdir[2]["pathuri"] +
|
|
")\r\n";
|
|
}
|
|
}
|
|
if (!app.app.vault.getConfig("useMarkdownLinks")) {
|
|
// image caption
|
|
(!settings.useCaptions || !caption.length) ? caption = "" : caption = "\|" + caption;
|
|
// image size has higher priority
|
|
(!settings.useCaptions || !imgsize.length) ? caption = "" : caption = "\|" + imgsize;
|
|
return [match, `![[${pathWiki}${caption}]]`, `${shortName}`];
|
|
}
|
|
else {
|
|
(!settings.useCaptions || !caption.length) ? caption = "" : caption = " " + caption;
|
|
return [match, ``, `${shortName}`];
|
|
}
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
catch (error) {
|
|
if (error.message === "File already exists.") {
|
|
}
|
|
else {
|
|
throw error;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
catch (error) {
|
|
logError("Image processing failed: " + error, false);
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
return processImageTag;
|
|
}
|
|
function getRDir(noteFile, settings, fileName, link = undefined) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
let pathWiki = "";
|
|
let pathMd = "";
|
|
const notePath = normalizePath(noteFile.parent.path);
|
|
const parsedPath = path__default["default"].parse(normalizePath(fileName));
|
|
const parsedPathE = {
|
|
parentd: path__default["default"].basename(parsedPath["dir"]),
|
|
basen: (parsedPath["name"] + parsedPath["ext"]),
|
|
lnkurid: path__default["default"].basename(decodeURI(link)),
|
|
pathuri: encodeURI(normalizePath(fileName))
|
|
};
|
|
switch (settings.pathInTags) {
|
|
case "baseFileName":
|
|
pathWiki = pathMd = parsedPathE["basen"];
|
|
break;
|
|
case "onlyRelative":
|
|
pathWiki = pathJoin([path__default["default"].relative(path__default["default"].sep + notePath, path__default["default"].sep + parsedPath["dir"]), parsedPathE["basen"]]);
|
|
pathMd = encodeURI(pathWiki);
|
|
break;
|
|
case "fullDirPath":
|
|
pathWiki = fileName.replace(/\\/g, "/");
|
|
pathMd = parsedPathE["pathuri"];
|
|
break;
|
|
default:
|
|
pathWiki = fileName;
|
|
pathMd = parsedPathE["pathuri"];
|
|
}
|
|
return [pathWiki, pathMd, parsedPathE];
|
|
});
|
|
}
|
|
function getMDir(app, noteFile, settings, defaultdir = false, unique = "") {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
noteFile.parent.path;
|
|
const current_date = moment().format(settings.DateFormat);
|
|
const obsmediadir = app.vault.getConfig("attachmentFolderPath");
|
|
const mediadir = settings.mediaRootDir;
|
|
var attdir = settings.saveAttE;
|
|
if (defaultdir) {
|
|
attdir = "";
|
|
}
|
|
let root = "/";
|
|
switch (attdir) {
|
|
case 'inFolderBelow':
|
|
root = mediadir
|
|
.replace("${notename}", noteFile.basename)
|
|
.replace("${unique}", unique)
|
|
.replace("${date}", current_date);
|
|
break;
|
|
case 'nextToNoteS':
|
|
root = (pathJoin([noteFile.parent.path, mediadir]))
|
|
.replace("${notename}", noteFile.basename)
|
|
.replace("${unique}", unique)
|
|
.replace("${date}", current_date);
|
|
break;
|
|
default:
|
|
if (obsmediadir === '/') {
|
|
root = obsmediadir;
|
|
}
|
|
else if (obsmediadir === './') {
|
|
root = pathJoin([noteFile.parent.path]);
|
|
}
|
|
else if (obsmediadir.match(/\.\/.+/g) !== null) {
|
|
root = pathJoin([noteFile.parent.path, obsmediadir.replace('\.\/', '')]);
|
|
}
|
|
else {
|
|
root = normalizePath(obsmediadir);
|
|
}
|
|
}
|
|
return trimAny(root, ["/", "\\"]);
|
|
});
|
|
}
|
|
function chooseFileName(adapter, dir, link, contentData, settings) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const parsedUrl = new url.URL(link);
|
|
const ignoredExt = settings.ignoredExt.split("|");
|
|
let fileExt = yield getFileExt(contentData, parsedUrl.pathname);
|
|
logError("file: " + link + " content: " + contentData + " file ext: " + fileExt, false);
|
|
if (fileExt == "unknown" && !settings.downUnknown) {
|
|
return { fileName: "", needWrite: false };
|
|
}
|
|
if (ignoredExt.includes(fileExt)) {
|
|
return { fileName: "", needWrite: false };
|
|
}
|
|
const baseName = md5Sig(contentData);
|
|
let needWrite = true;
|
|
let fileName = "";
|
|
const suggestedName = pathJoin([dir, cleanFileName(`${baseName}` + `.${fileExt}`)]);
|
|
if (yield adapter.exists(suggestedName, false)) {
|
|
const fileData = yield adapter.readBinary(suggestedName);
|
|
const existing_file_md5 = md5Sig(fileData);
|
|
if (existing_file_md5 === baseName) {
|
|
fileName = suggestedName;
|
|
needWrite = false;
|
|
}
|
|
else {
|
|
fileName = pathJoin([dir, cleanFileName(Math.random().toString(9).slice(2) + `.${fileExt}`)]);
|
|
}
|
|
}
|
|
else {
|
|
fileName = suggestedName;
|
|
}
|
|
logError("File name: " + fileName, false);
|
|
if (!fileName) {
|
|
throw new Error("Failed to generate file name for media file.");
|
|
}
|
|
//linkHashes.ensureHashGenerated(link, contentData);
|
|
return { fileName, needWrite };
|
|
});
|
|
}
|
|
|
|
// Queue that keep only unique values and counts attempts
|
|
class UniqueQueue {
|
|
constructor() {
|
|
this.queue = new Array();
|
|
}
|
|
push(value, attempts) {
|
|
if (attempts < 1) {
|
|
console.error("Triyng to enqueue item with attempts < 1");
|
|
return;
|
|
}
|
|
this.remove(value);
|
|
this.queue.push({ value, attempts });
|
|
}
|
|
remove(value) {
|
|
this.queue = this.queue.filter((item) => item.value !== value);
|
|
}
|
|
iterationQueue() {
|
|
const extractIteration = (prev, curr) => {
|
|
prev.iteration.push(curr.value);
|
|
if (curr.attempts > 1) {
|
|
prev.queue.push(curr.value, curr.attempts - 1);
|
|
}
|
|
return prev;
|
|
};
|
|
const { queue, iteration } = this.queue.reduce(extractIteration, {
|
|
queue: new UniqueQueue(),
|
|
iteration: [],
|
|
});
|
|
this.queue = queue.queue;
|
|
return iteration;
|
|
}
|
|
}
|
|
|
|
class ModalW1 extends obsidian.Modal {
|
|
constructor(app) {
|
|
super(app);
|
|
this.messg = "";
|
|
this.callbackFunc = null;
|
|
}
|
|
onOpen() {
|
|
let { contentEl, titleEl } = this;
|
|
titleEl.setText(APP_TITLE);
|
|
contentEl.createDiv({
|
|
text: this.messg
|
|
});
|
|
contentEl.createEl("button", {
|
|
cls: ["mod-cta"],
|
|
text: "Cancel"
|
|
}).addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
|
|
this.close();
|
|
}));
|
|
contentEl.createEl("button", {
|
|
cls: ["mod-cta"],
|
|
text: "Confirm"
|
|
}).addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
|
|
this.close();
|
|
if (this.callbackFunc) {
|
|
this.callbackFunc();
|
|
}
|
|
}));
|
|
}
|
|
onClose() {
|
|
let { contentEl } = this;
|
|
contentEl.empty();
|
|
}
|
|
}
|
|
|
|
require('fs').promises;
|
|
//import { count, log } from "console"
|
|
class LocalImagesPlugin extends obsidian.Plugin {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.modifiedQueue = new UniqueQueue();
|
|
this.intervalId = 0;
|
|
this.newfCreated = [];
|
|
this.noteModified = [];
|
|
this.newfMoveReq = true;
|
|
this.newfCreatedByDownloader = [];
|
|
// using arrow syntax for callbacks to correctly pass this context
|
|
this.processActivePage = (defaultdir = false) => () => __awaiter(this, void 0, void 0, function* () {
|
|
logError("processActivePage");
|
|
try {
|
|
const activeFile = this.getCurrentNote();
|
|
yield this.processPage(activeFile, defaultdir);
|
|
}
|
|
catch (e) {
|
|
showBalloon(`Please select a note or click inside selected note in canvas.`, this.settings.showNotifications);
|
|
return;
|
|
}
|
|
});
|
|
this.processAllPages = () => __awaiter(this, void 0, void 0, function* () {
|
|
const files = this.app.vault.getMarkdownFiles();
|
|
const pagesCount = files.length;
|
|
const notice = this.settings.showNotifications
|
|
? new obsidian.Notice(APP_TITLE + `\nStart processing. Total ${pagesCount} pages. `, TIMEOUT_LIKE_INFINITY)
|
|
: null;
|
|
for (const [index, file] of files.entries()) {
|
|
if (this.ExemplaryOfMD(file.path)) {
|
|
if (notice) {
|
|
//setMessage() is undeclared but factically existing, so ignore the TS error //@ts-expect-error
|
|
notice.setMessage(APP_TITLE + `\nProcessing \n"${file.path}" \nPage ${index} of ${pagesCount}`);
|
|
}
|
|
yield this.processPage(file);
|
|
}
|
|
}
|
|
if (notice) {
|
|
// dum @ts-expect-error
|
|
notice.setMessage(APP_TITLE + `\n${pagesCount} pages were processed.`);
|
|
setTimeout(() => {
|
|
notice.hide();
|
|
}, NOTICE_TIMEOUT);
|
|
}
|
|
});
|
|
this.removeOrphans = (type = undefined, filesToRemove = undefined, noteFile = undefined) => () => __awaiter(this, void 0, void 0, function* () {
|
|
var _a, _b, _c, _d, _e;
|
|
const obsmediadir = app.vault.getConfig("attachmentFolderPath");
|
|
const allFiles = this.app.vault.getFiles();
|
|
let oldRootdir = this.settings.mediaRootDir;
|
|
if (type == "plugin") {
|
|
let orphanedAttachments = [];
|
|
let allAttachmentsLinks = [];
|
|
if (this.settings.saveAttE != "nextToNoteS" ||
|
|
!path__default["default"].basename(oldRootdir).endsWith("${notename}") ||
|
|
oldRootdir.includes("${date}")) {
|
|
showBalloon("This command requires the settings 'Next to note in the folder specified below' and pattern '${notename}' at the end to be enabled, also the path cannot contain ${date} pattern.\nPlease, change settings first!\r\n", this.settings.showNotifications);
|
|
return;
|
|
}
|
|
if (!noteFile) {
|
|
noteFile = this.getCurrentNote();
|
|
if (!noteFile) {
|
|
showBalloon("Please, select a note or click inside a note in canvas!", this.settings.showNotifications);
|
|
return;
|
|
}
|
|
}
|
|
if (this.ExemplaryOfMD(noteFile.path)) {
|
|
oldRootdir = oldRootdir.replace("${notename}", (_a = path__default["default"].parse(noteFile.path)) === null || _a === void 0 ? void 0 : _a.name);
|
|
oldRootdir = trimAny(pathJoin([(_b = path__default["default"].parse(noteFile.path)) === null || _b === void 0 ? void 0 : _b.dir, oldRootdir]), ["\/"]);
|
|
if (!(yield this.app.vault.exists(oldRootdir))) {
|
|
showBalloon("The attachment folder " + oldRootdir + " does not exist!", this.settings.showNotifications);
|
|
return;
|
|
}
|
|
const allAttachments = yield ((_c = this.app.vault.getAbstractFileByPath(oldRootdir)) === null || _c === void 0 ? void 0 : _c.children);
|
|
const metaCache = this.app.metadataCache.getFileCache(noteFile);
|
|
const embeds = metaCache === null || metaCache === void 0 ? void 0 : metaCache.embeds;
|
|
const links = metaCache === null || metaCache === void 0 ? void 0 : metaCache.links;
|
|
if (embeds) {
|
|
for (const embed of embeds) {
|
|
allAttachmentsLinks.push(path__default["default"].basename(embed.link));
|
|
}
|
|
}
|
|
if (links) {
|
|
for (const link of links) {
|
|
allAttachmentsLinks.push(path__default["default"].basename(link.link));
|
|
}
|
|
}
|
|
if (allAttachments) {
|
|
for (const attach of allAttachments) {
|
|
if (!allAttachmentsLinks.includes(attach.name) && attach.children == undefined) {
|
|
logError("orph: " + attach.basename);
|
|
orphanedAttachments.push(attach);
|
|
}
|
|
}
|
|
}
|
|
if (orphanedAttachments.length > 0) {
|
|
const mod = new ModalW1(this.app);
|
|
mod.messg = "Confirm remove " + orphanedAttachments.length + " orphan(s) from '" + oldRootdir + "'\r\n\r\n ";
|
|
mod.plugin = this;
|
|
mod.callbackFunc = this.removeOrphans("execremove", orphanedAttachments);
|
|
mod.open();
|
|
}
|
|
else {
|
|
showBalloon("No orphaned files found!", this.settings.showNotifications);
|
|
}
|
|
}
|
|
}
|
|
if (type == "obsidian") {
|
|
if (obsmediadir.slice(0, 2) == "./" || obsmediadir == "/") {
|
|
showBalloon("This command cannot run on vault's root or on subfolder next to note!\nPlease, change settings first!\r\n", this.settings.showNotifications);
|
|
return;
|
|
}
|
|
const allAttachments = (_d = this.app.vault.getAbstractFileByPath(obsmediadir)) === null || _d === void 0 ? void 0 : _d.children;
|
|
let orphanedAttachments = [];
|
|
let allAttachmentsLinks = [];
|
|
if (allFiles) {
|
|
for (const file of allFiles) {
|
|
//Fix for canvas files
|
|
if (file !== null && this.ExemplaryOfCANVAS(file.path)) {
|
|
logError(file);
|
|
logError(this.app.metadataCache.getCache(file.path));
|
|
let canvasData;
|
|
try {
|
|
canvasData = JSON.parse(yield app.vault.cachedRead(file));
|
|
}
|
|
catch (e) {
|
|
logError("Parse canvas data error");
|
|
continue;
|
|
}
|
|
if (canvasData.nodes && canvasData.nodes.length > 0) {
|
|
for (const node of canvasData.nodes) {
|
|
logError(node);
|
|
if (node.type === "file") {
|
|
logError("file json");
|
|
allAttachmentsLinks.push(path__default["default"].basename(node.file));
|
|
}
|
|
else if (node.type == "text") {
|
|
logError("text json");
|
|
//https://github.com/Fevol/obsidian-typings
|
|
//Undocumented API may be altered in the future
|
|
const AllNodeLinks = (_e = (yield this.app.internalPlugins.plugins.canvas.instance.index.parseText(node.text))) === null || _e === void 0 ? void 0 : _e.links;
|
|
logError(AllNodeLinks);
|
|
if (AllNodeLinks === undefined) {
|
|
continue;
|
|
}
|
|
for (const Nodelink of AllNodeLinks) {
|
|
allAttachmentsLinks.push(path__default["default"].basename(Nodelink.link));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (file !== null && this.ExemplaryOfMD(file.path)) {
|
|
const metaCache = this.app.metadataCache.getCache(file.path);
|
|
const embeds = metaCache === null || metaCache === void 0 ? void 0 : metaCache.embeds;
|
|
const links = metaCache === null || metaCache === void 0 ? void 0 : metaCache.links;
|
|
logError(embeds);
|
|
logError(links);
|
|
if (embeds) {
|
|
for (const embed of embeds) {
|
|
allAttachmentsLinks.push(path__default["default"].basename(embed.link));
|
|
}
|
|
}
|
|
if (links) {
|
|
for (const link of links) {
|
|
allAttachmentsLinks.push(path__default["default"].basename(link.link));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (const attach of allAttachments) {
|
|
if (!allAttachmentsLinks.includes(attach.name) && attach.children == undefined) {
|
|
logError(allAttachmentsLinks);
|
|
logError(attach.name);
|
|
logError("orph: " + attach.name);
|
|
orphanedAttachments.push(attach);
|
|
}
|
|
}
|
|
}
|
|
logError("Orphaned: ");
|
|
logError(orphanedAttachments, true);
|
|
if (orphanedAttachments.length > 0) {
|
|
const mod = new ModalW1(this.app);
|
|
mod.messg = "Confirm remove " + orphanedAttachments.length + " orphan(s) from '" + obsmediadir + " '\r\n \
|
|
NOTE: Be careful when running this command on Obsidian attachments folder, since some html-linked files may also be moved.\r\n ";
|
|
mod.plugin = this;
|
|
mod.callbackFunc = this.removeOrphans("execremove", orphanedAttachments);
|
|
mod.open();
|
|
}
|
|
else {
|
|
showBalloon("No orphaned files found!", this.settings.showNotifications);
|
|
}
|
|
}
|
|
if (type == "execremove") {
|
|
const useSysTrash = (this.app.vault.getConfig("trashOption") === "system");
|
|
const remcompl = this.settings.removeOrphansCompl;
|
|
let msg = "";
|
|
if (filesToRemove) {
|
|
filesToRemove.forEach((el) => {
|
|
if (remcompl) {
|
|
msg = "were deleted completely.";
|
|
this.app.vault.delete(el, true);
|
|
}
|
|
else {
|
|
if (useSysTrash) {
|
|
msg = "were moved to the system garbage can.";
|
|
}
|
|
else {
|
|
msg = "were moved to the Obsidian garbage can.";
|
|
}
|
|
this.app.vault.trash(el, useSysTrash);
|
|
}
|
|
});
|
|
}
|
|
showBalloon(filesToRemove.length + " file(s) " + msg, this.settings.showNotifications);
|
|
}
|
|
});
|
|
this.openProcessAllModal = () => {
|
|
const mod = new ModalW1(this.app);
|
|
mod.messg = "Confirm processing all pages.\r\n ";
|
|
mod.plugin = this;
|
|
mod.callbackFunc = this.processAllPages;
|
|
mod.open();
|
|
};
|
|
this.processMdFilesOnTimer = () => __awaiter(this, void 0, void 0, function* () {
|
|
var _f;
|
|
const th = this;
|
|
function onRet() {
|
|
th.newfCreated = [];
|
|
th.newfCreatedByDownloader = [];
|
|
th.noteModified = [];
|
|
th.newfMoveReq = false;
|
|
window.clearInterval(th.newfProcInt);
|
|
th.newfProcInt = 0;
|
|
}
|
|
logError("func processMdFilesOnTimer:\r\n");
|
|
logError(this.noteModified, true);
|
|
try {
|
|
window.clearInterval(this.newfProcInt);
|
|
this.newfProcInt = 0;
|
|
this.newfMoveReq = false;
|
|
let itemcount = 0;
|
|
const useMdLinks = this.app.vault.getConfig("useMarkdownLinks");
|
|
for (let note of this.noteModified) {
|
|
const metaCache = this.app.metadataCache.getFileCache(note);
|
|
let filedata = yield this.app.vault.cachedRead(note);
|
|
let pr = false;
|
|
for (const reg_p of MD_SEARCH_PATTERN) {
|
|
if (reg_p.test(filedata)) {
|
|
pr = true;
|
|
break;
|
|
}
|
|
}
|
|
const mdir = yield getMDir(this.app, note, this.settings);
|
|
const obsmdir = yield getMDir(this.app, note, this.settings, true);
|
|
let embeds = metaCache === null || metaCache === void 0 ? void 0 : metaCache.embeds;
|
|
if (obsmdir != "" && !(yield this.app.vault.adapter.exists(obsmdir))) {
|
|
if (!this.settings.DoNotCreateObsFolder) {
|
|
this.ensureFolderExists(obsmdir);
|
|
showBalloon("You obsidian media folder set to '" + obsmdir + "', and has been created by the plugin. Please, try again. ", this.settings.showNotifications);
|
|
onRet();
|
|
}
|
|
return;
|
|
}
|
|
if (embeds || pr) {
|
|
yield this.ensureFolderExists(mdir);
|
|
for (let el of embeds) {
|
|
logError(el);
|
|
let oldpath = pathJoin([obsmdir, path__default["default"].basename(el.link)]);
|
|
let oldtag = el["original"];
|
|
logError(useMdLinks);
|
|
logError(this.newfCreated);
|
|
if ((this.newfCreated.indexOf(el.link) != -1 || (obsmdir != "" && (this.newfCreated.includes(oldpath) || this.newfCreated.includes(el.link)))) &&
|
|
!this.newfCreatedByDownloader.includes(oldtag)) {
|
|
if (!(yield this.app.vault.adapter.exists(oldpath))) {
|
|
logError("Cannot find " + el.link + " skipping...");
|
|
continue;
|
|
}
|
|
let newpath = pathJoin([mdir, cFileName(path__default["default"].basename(el.link))]);
|
|
let newlink = yield getRDir(note, this.settings, newpath);
|
|
logError(el.link);
|
|
//let newBinData: Buffer | null = null
|
|
let newBinData = null;
|
|
let newMD5 = null;
|
|
const oldBinData = yield readFromDiskB(pathJoin([this.app.vault.adapter.basePath, oldpath]), 5000);
|
|
const oldMD5 = md5Sig(oldBinData);
|
|
const fileExt = yield getFileExt(oldBinData, oldpath);
|
|
logError("oldbindata: " + oldBinData);
|
|
logError("oldext: " + fileExt);
|
|
if (this.settings.PngToJpegLocal && fileExt == "png") {
|
|
logError("converting to Jpeg");
|
|
const blob = new Blob([new Uint8Array(yield this.app.vault.adapter.readBinary(oldpath))]);
|
|
newBinData = yield blobToJpegArrayBuffer(blob, this.settings.JpegQuality * 0.01);
|
|
newMD5 = md5Sig(newBinData);
|
|
logError(newBinData);
|
|
if (newBinData != null) {
|
|
if (this.settings.useMD5ForNewAtt) {
|
|
newpath = pathJoin([mdir, newMD5 + ".jpeg"]);
|
|
}
|
|
else {
|
|
newpath = pathJoin([mdir, cFileName(((_f = path__default["default"].parse(el.link)) === null || _f === void 0 ? void 0 : _f.name) + ".jpeg")]);
|
|
}
|
|
newlink = yield getRDir(note, this.settings, newpath);
|
|
}
|
|
}
|
|
else if (this.settings.useMD5ForNewAtt) {
|
|
newpath = pathJoin([mdir, oldMD5 + path__default["default"].extname(el.link)]);
|
|
newlink = yield getRDir(note, this.settings, newpath);
|
|
}
|
|
else if (!this.settings.useMD5ForNewAtt) {
|
|
newpath = pathJoin([mdir, cFileName(path__default["default"].basename(el.link))]);
|
|
newlink = yield getRDir(note, this.settings, newpath);
|
|
}
|
|
if (yield this.app.vault.adapter.exists(newpath)) {
|
|
let newFMD5;
|
|
if (newBinData != null) {
|
|
newFMD5 = md5Sig(yield this.app.vault.adapter.readBinary(newpath));
|
|
}
|
|
else {
|
|
newFMD5 = md5Sig(yield readFromDiskB(pathJoin([this.app.vault.adapter.basePath, newpath]), 5000));
|
|
}
|
|
if (newMD5 === newFMD5 || (oldMD5 === newFMD5 && oldpath != newpath)) {
|
|
logError(path__default["default"].dirname(oldpath));
|
|
logError("Deleting duplicate file: " + oldpath);
|
|
yield this.app.vault.adapter.remove(oldpath);
|
|
}
|
|
else if (oldpath != newpath) {
|
|
logError("Renaming existing: " + oldpath);
|
|
let inc = 1;
|
|
while (yield this.app.vault.adapter.exists(newpath)) {
|
|
newpath = pathJoin([mdir, `(${inc}) ` + cFileName(path__default["default"].basename(el.link))]);
|
|
inc++;
|
|
}
|
|
newlink = yield getRDir(note, this.settings, newpath);
|
|
yield this.app.vault.adapter.rename(oldpath, newpath);
|
|
}
|
|
}
|
|
else {
|
|
logError(`renaming ${oldpath} to ${newpath}`);
|
|
try {
|
|
if (newBinData != null) {
|
|
yield this.app.vault.adapter.writeBinary(newpath, newBinData).then();
|
|
{
|
|
yield this.app.vault.adapter.remove(oldpath);
|
|
}
|
|
}
|
|
else {
|
|
yield this.app.vault.adapter.rename(oldpath, newpath);
|
|
}
|
|
}
|
|
catch (error) {
|
|
logError(error);
|
|
}
|
|
}
|
|
let addName = "";
|
|
if (this.settings.addNameOfFile) {
|
|
if (useMdLinks) {
|
|
addName = `[Open: ${path__default["default"].basename(el.link)}](${newlink[1]})\r\n`;
|
|
}
|
|
else {
|
|
addName = `[[${newlink[0]}|Open: ${path__default["default"].basename(el.link)}]]\r\n`;
|
|
}
|
|
}
|
|
let newtag = addName + oldtag.replace(el.link, newlink[0]);
|
|
if (useMdLinks) {
|
|
newtag = addName + oldtag.replace(encObsURI(el.link), newlink[1]);
|
|
}
|
|
filedata = filedata.replaceAll(oldtag, newtag);
|
|
itemcount++;
|
|
}
|
|
}
|
|
}
|
|
if (itemcount > 0) {
|
|
yield this.app.vault.modify(note, filedata);
|
|
showBalloon(itemcount + " attachments for note " + note.path + " were processed.", this.settings.showNotifications);
|
|
itemcount = 0;
|
|
}
|
|
}
|
|
}
|
|
catch (e) {
|
|
logError(e);
|
|
onRet();
|
|
}
|
|
onRet();
|
|
});
|
|
this.setTitleAsName = () => __awaiter(this, void 0, void 0, function* () {
|
|
try {
|
|
const noteFile = this.getCurrentNote();
|
|
const fileData = yield this.app.vault.cachedRead(noteFile);
|
|
const title = fileData.match(/^#{1,6} .+?($|\n)/gm);
|
|
var ind = 0;
|
|
if (title !== null) {
|
|
const newName = cFileName(trimAny(title[0].toString(), ["#", " "])).slice(0, 200);
|
|
var fullPath = pathJoin([noteFile.parent.path, newName + ".md"]);
|
|
var fExist = yield this.app.vault.exists(fullPath);
|
|
if (trimAny(noteFile.path, ["\\", "/"]) != trimAny(fullPath, ["\\", "/"])) {
|
|
while (fExist) {
|
|
ind++;
|
|
var fullPath = pathJoin([noteFile.parent.path, newName + " (" + ind + ")" + ".md"]);
|
|
var fExist = yield this.app.vault.exists(fullPath);
|
|
}
|
|
yield this.app.vault.rename(noteFile, fullPath);
|
|
showBalloon(`The note was renamed to ` + fullPath, this.settings.showNotifications);
|
|
}
|
|
}
|
|
}
|
|
catch (e) {
|
|
showBalloon(`Cannot rename.`, this.settings.showNotifications);
|
|
return;
|
|
}
|
|
});
|
|
this.convertSelToURI = () => __awaiter(this, void 0, void 0, function* () {
|
|
this.app.workspace.activeEditor.editor.replaceSelection(encObsURI(yield this.app.workspace.activeEditor.getSelection()));
|
|
});
|
|
this.convertSelToMD = () => __awaiter(this, void 0, void 0, function* () {
|
|
this.app.workspace.activeEditor.editor.replaceSelection(obsidian.htmlToMarkdown(yield this.app.workspace.activeEditor.getSelection()));
|
|
});
|
|
this.processModifiedQueue = () => __awaiter(this, void 0, void 0, function* () {
|
|
const iteration = this.modifiedQueue.iterationQueue();
|
|
for (const page of iteration) {
|
|
this.processPage(page, false);
|
|
}
|
|
});
|
|
}
|
|
onload() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
yield this.loadSettings();
|
|
this.addCommand({
|
|
id: "download-images",
|
|
name: "Localize attachments for the current note (plugin folder)",
|
|
callback: this.processActivePage(false),
|
|
});
|
|
this.addCommand({
|
|
id: "download-images-def",
|
|
name: "Localize attachments for the current note (Obsidian folder)",
|
|
callback: this.processActivePage(true),
|
|
});
|
|
if (!this.settings.disAddCom) {
|
|
this.addRibbonIcon("dice", APP_TITLE + "\r\nLocalize attachments (plugin folder)", () => {
|
|
this.processActivePage(false)();
|
|
});
|
|
this.addCommand({
|
|
id: "set-title-as-name",
|
|
name: "Set the first found # header as a note name.",
|
|
callback: this.setTitleAsName,
|
|
});
|
|
this.addCommand({
|
|
id: "download-images-all",
|
|
name: "Localize attachments for all your notes (plugin folder)",
|
|
callback: this.openProcessAllModal,
|
|
});
|
|
this.addCommand({
|
|
id: "convert-selection-to-URI",
|
|
name: "Convert selection to URI",
|
|
callback: this.convertSelToURI,
|
|
});
|
|
this.addCommand({
|
|
id: "convert-selection-to-md",
|
|
name: "Convert selection from html to markdown",
|
|
callback: this.convertSelToMD,
|
|
});
|
|
this.addCommand({
|
|
id: "remove-orphans-from-obsidian-folder",
|
|
name: "Remove all orphaned attachments (Obsidian folder)",
|
|
callback: () => { this.removeOrphans("obsidian")(); },
|
|
});
|
|
this.addCommand({
|
|
id: "remove-orphans-from-plugin-folder",
|
|
name: "Remove all orphaned attachments (Plugin folder)",
|
|
callback: () => { this.removeOrphans("plugin")(); },
|
|
});
|
|
}
|
|
// Some file has been created
|
|
this.app.vault.on('create', (file) => __awaiter(this, void 0, void 0, function* () {
|
|
logError("New file created: " + file.path);
|
|
if (this.ExemplaryOfMD(file.path)) {
|
|
this.onMdCreateFunc(file);
|
|
}
|
|
else {
|
|
this.onFCreateFunc(file);
|
|
}
|
|
}));
|
|
// Some file has been deleted
|
|
this.app.vault.on('delete', (file) => __awaiter(this, void 0, void 0, function* () {
|
|
if (!file ||
|
|
!(file instanceof obsidian.TFile) ||
|
|
!(this.ExemplaryOfMD(file.path)) ||
|
|
!this.settings.removeMediaFolder ||
|
|
this.settings.saveAttE != "nextToNoteS") {
|
|
return;
|
|
}
|
|
let rootdir = this.settings.mediaRootDir;
|
|
const useSysTrash = (this.app.vault.getConfig("trashOption") === "system");
|
|
if (this.settings.saveAttE !== "obsFolder" &&
|
|
path__default["default"].basename(rootdir).includes("${notename}") &&
|
|
!rootdir.includes("${date}")) {
|
|
rootdir = rootdir.replace("${notename}", file.basename);
|
|
if (this.settings.saveAttE == "nextToNoteS") {
|
|
rootdir = pathJoin([path__default["default"].dirname((file === null || file === void 0 ? void 0 : file.path) || ""), rootdir]);
|
|
}
|
|
try {
|
|
if (this.app.vault.getAbstractFileByPath(rootdir) instanceof obsidian.TFolder) {
|
|
this.app.vault.trash(app.vault.getAbstractFileByPath(rootdir), useSysTrash);
|
|
showBalloon("Attachment folder " + rootdir + " was moved to trash can.", this.settings.showNotifications);
|
|
}
|
|
}
|
|
catch (e) {
|
|
logError(e);
|
|
return;
|
|
}
|
|
}
|
|
}));
|
|
this.app.vault.on('rename', (file, oldPath) => __awaiter(this, void 0, void 0, function* () {
|
|
var _a, _b, _c;
|
|
if (!file ||
|
|
!(file instanceof obsidian.TFile) ||
|
|
!(this.ExemplaryOfMD(file.path)) ||
|
|
!this.settings.removeMediaFolder ||
|
|
this.settings.saveAttE != "nextToNoteS" ||
|
|
this.settings.pathInTags != "onlyRelative") {
|
|
return;
|
|
}
|
|
let oldRootdir = this.settings.mediaRootDir;
|
|
if (path__default["default"].basename(oldRootdir).includes("${notename}") &&
|
|
!oldRootdir.includes("${date}")) {
|
|
oldRootdir = oldRootdir.replace("${notename}", (_a = path__default["default"].parse(oldPath)) === null || _a === void 0 ? void 0 : _a.name);
|
|
let newRootDir = oldRootdir.replace((_b = path__default["default"].parse(oldPath)) === null || _b === void 0 ? void 0 : _b.name, (_c = path__default["default"].parse(file.path)) === null || _c === void 0 ? void 0 : _c.name);
|
|
let newRootDir_ = newRootDir;
|
|
let oldRootdir_ = oldRootdir;
|
|
oldRootdir_ = pathJoin([(path__default["default"].dirname(oldPath) || ""), oldRootdir]);
|
|
newRootDir_ = pathJoin([(path__default["default"].dirname(file.path) || ""), newRootDir]);
|
|
try {
|
|
if (this.app.vault.getAbstractFileByPath(oldRootdir_) instanceof obsidian.TFolder) {
|
|
yield this.ensureFolderExists(path__default["default"].dirname(newRootDir_));
|
|
//await this.app.fileManager.renameFile(app.vault.getAbstractFileByPath(oldRootdir),newRootDir)
|
|
yield this.app.vault.adapter.rename(oldRootdir_, newRootDir_);
|
|
showBalloon("Attachment folder was renamed to " + newRootDir_, this.settings.showNotifications);
|
|
}
|
|
}
|
|
catch (e) {
|
|
showBalloon("Cannot move attachment folder: \r\n" + e, this.settings.showNotifications);
|
|
logError(e);
|
|
return;
|
|
}
|
|
let content = yield this.app.vault.cachedRead(file);
|
|
content = content
|
|
.replaceAll("](" + encodeURI(oldRootdir), "](" + encodeURI(newRootDir))
|
|
.replaceAll("[" + oldRootdir, "[" + newRootDir);
|
|
this.app.vault.modify(file, content);
|
|
}
|
|
}));
|
|
// Some file has been modified
|
|
this.app.vault.on('modify', (file) => __awaiter(this, void 0, void 0, function* () {
|
|
if (!this.newfMoveReq)
|
|
return;
|
|
logError("File modified: " + file.path, false);
|
|
if (!file ||
|
|
!(file instanceof obsidian.TFile) ||
|
|
!(this.ExemplaryOfMD(file.path))) {
|
|
return;
|
|
}
|
|
else {
|
|
if (this.settings.processAll) {
|
|
if (!this.noteModified.includes(file)) {
|
|
this.noteModified.push(file);
|
|
}
|
|
this.setupNewMdFilesProcInterval();
|
|
}
|
|
}
|
|
}));
|
|
this.app.workspace.on("editor-paste", (evt, editor, info) => {
|
|
this.onPasteFunc(evt, editor, info);
|
|
});
|
|
this.setupQueueInterval();
|
|
this.addSettingTab(new SettingTab(this.app, this));
|
|
});
|
|
}
|
|
setupQueueInterval() {
|
|
if (this.intervalId) {
|
|
const intervalId = this.intervalId;
|
|
this.intervalId = 0;
|
|
window.clearInterval(intervalId);
|
|
}
|
|
if (this.settings.realTimeUpdate &&
|
|
this.settings.realTimeUpdateInterval > 0) {
|
|
this.intervalId = window.setInterval(this.processModifiedQueue, this.settings.realTimeUpdateInterval * 1000);
|
|
this.registerInterval(this.intervalId);
|
|
}
|
|
}
|
|
getCurrentNote() {
|
|
try {
|
|
const noteFile = app.workspace.activeEditor.file;
|
|
return noteFile;
|
|
}
|
|
catch (e) {
|
|
showBalloon("Cannot get current note! ", this.settings.showNotifications);
|
|
}
|
|
return null;
|
|
}
|
|
processPage(file, defaultdir = false) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (file == null) {
|
|
return null;
|
|
}
|
|
const content = yield this.app.vault.cachedRead(file);
|
|
if (content.length == 0) {
|
|
return null;
|
|
}
|
|
const fixedContent = yield replaceAsync(content, MD_SEARCH_PATTERN, imageTagProcessor(this, file, this.settings, defaultdir));
|
|
if (content != fixedContent[0] && fixedContent[1] === false) {
|
|
this.modifiedQueue.remove(file);
|
|
yield this.app.vault.modify(file, fixedContent[0]);
|
|
fixedContent[2].forEach((element) => {
|
|
this.newfCreatedByDownloader.push(element);
|
|
});
|
|
showBalloon(`Attachments for "${file.path}" were processed.`, this.settings.showNotifications);
|
|
}
|
|
else if (content != fixedContent[0] && fixedContent[1] === true) {
|
|
this.modifiedQueue.remove(file);
|
|
yield this.app.vault.modify(file, fixedContent[0]);
|
|
fixedContent[2].forEach((element) => {
|
|
this.newfCreatedByDownloader.push(element);
|
|
});
|
|
showBalloon(`WARNING!\r\nAttachments for "${file.path}" were processed, but some attachments were not downloaded/replaced...`, this.settings.showNotifications);
|
|
}
|
|
else {
|
|
if (this.settings.showNotifications) {
|
|
showBalloon(`Page "${file.path}" has been processed, but nothing was changed.`, this.settings.showNotifications);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
onPasteFunc(evt = undefined, editor = undefined, info = undefined) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (evt === undefined) {
|
|
return;
|
|
}
|
|
if (!this.settings.realTimeUpdate) {
|
|
return;
|
|
}
|
|
try {
|
|
const activeFile = this.getCurrentNote();
|
|
const fItems = evt.clipboardData.files;
|
|
const tItems = evt.clipboardData.items;
|
|
if (fItems.length != 0) {
|
|
return;
|
|
}
|
|
for (const key in tItems) {
|
|
// Check if it was a text/html
|
|
if (tItems[key].kind == "string") {
|
|
if (this.settings.realTimeUpdate) {
|
|
const cont = obsidian.htmlToMarkdown(evt.clipboardData.getData("text/html")) +
|
|
obsidian.htmlToMarkdown(evt.clipboardData.getData("text"));
|
|
for (const reg_p of MD_SEARCH_PATTERN) {
|
|
if (reg_p.test(cont)) {
|
|
logError("content: " + cont);
|
|
showBalloon("Media links were found, processing...", this.settings.showNotifications);
|
|
this.enqueueActivePage(activeFile);
|
|
this.setupQueueInterval();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
catch (e) {
|
|
showBalloon(`Please select a note or click inside selected note in canvas.`, this.settings.showNotifications);
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
onMdCreateFunc(file) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (!file ||
|
|
!(file instanceof obsidian.TFile) ||
|
|
!(this.settings.processCreated) ||
|
|
!this.ExemplaryOfMD(file.path))
|
|
return;
|
|
const timeGapMs = Math.abs(Date.now() - file.stat.ctime);
|
|
if (timeGapMs > 1000)
|
|
return;
|
|
logError("func onMdCreateFunc: " + file.path);
|
|
logError(file, true);
|
|
var cont = yield this.app.vault.cachedRead(file);
|
|
logError(cont);
|
|
this.enqueueActivePage(file);
|
|
this.setupQueueInterval();
|
|
this.setupNewMdFilesProcInterval();
|
|
});
|
|
}
|
|
onFCreateFunc(file) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (!file ||
|
|
!(file instanceof obsidian.TFile) ||
|
|
this.ExemplaryOfMD(file.path) ||
|
|
this.ExemplaryOfCANVAS(file.path) ||
|
|
!(this.settings.processAll))
|
|
return;
|
|
if (!file.stat.ctime)
|
|
return;
|
|
const timeGapMs = Math.abs(Date.now() - file.stat.mtime);
|
|
if (timeGapMs > 1000)
|
|
return;
|
|
this.newfCreated.push(file.path);
|
|
this.newfMoveReq = true;
|
|
this.setupNewMdFilesProcInterval();
|
|
logError("file created ");
|
|
});
|
|
}
|
|
ExemplaryOfMD(pat) {
|
|
var _a, _b;
|
|
const includeRegex = new RegExp(this.settings.includepattern, "i");
|
|
return (((_b = (_a = pat.match(includeRegex)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.md) != undefined);
|
|
}
|
|
ExemplaryOfCANVAS(pat) {
|
|
var _a, _b;
|
|
const includeRegex = new RegExp(this.settings.includepattern, "i");
|
|
return (((_b = (_a = pat.match(includeRegex)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.canvas) != undefined);
|
|
}
|
|
setupNewMdFilesProcInterval() {
|
|
logError("func setupNewFilesProcInterval: \r\n");
|
|
window.clearInterval(this.newfProcInt);
|
|
this.newfProcInt = 0;
|
|
this.newfProcInt = window.setInterval(this.processMdFilesOnTimer, this.settings.realTimeUpdateInterval * 1000);
|
|
this.registerInterval(this.newfProcInt);
|
|
}
|
|
enqueueActivePage(activeFile) {
|
|
this.modifiedQueue.push(activeFile, 1 //this.settings.realTim3AttemptsToProcess
|
|
);
|
|
}
|
|
// ------------ Load / Save settings -----------------
|
|
onunload() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
this.app.workspace.off("editor-drop", null);
|
|
this.app.workspace.off("editor-paste", null);
|
|
this.app.workspace.off('file-menu', null);
|
|
//this.app.vault.off("create", null)
|
|
logError(" unloaded.");
|
|
});
|
|
}
|
|
loadSettings() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
|
|
this.setupQueueInterval();
|
|
});
|
|
}
|
|
saveSettings() {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
try {
|
|
yield this.saveData(this.settings);
|
|
}
|
|
catch (error) {
|
|
displayError(error);
|
|
}
|
|
});
|
|
}
|
|
ensureFolderExists(folderPath) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
try {
|
|
yield this.app.vault.createFolder(folderPath);
|
|
return;
|
|
}
|
|
catch (e) {
|
|
logError(e);
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = LocalImagesPlugin;
|
|
|
|
|
|
/* nosourcemap */ |