/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // node_modules/smiles-drawer/src/ArrayHelper.js var require_ArrayHelper = __commonJS({ "node_modules/smiles-drawer/src/ArrayHelper.js"(exports, module2) { var ArrayHelper = class { /** * Clone an array or an object. If an object is passed, a shallow clone will be created. * * @static * @param {*} arr The array or object to be cloned. * @returns {*} A clone of the array or object. */ static clone(arr) { let out = Array.isArray(arr) ? Array() : {}; for (let key in arr) { let value = arr[key]; if (typeof value.clone === "function") { out[key] = value.clone(); } else { out[key] = typeof value === "object" ? ArrayHelper.clone(value) : value; } } return out; } /** * Returns a boolean indicating whether or not the two arrays contain the same elements. * Only supports 1d, non-nested arrays. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Boolean} A boolean indicating whether or not the two arrays contain the same elements. */ static equals(arrA, arrB) { if (arrA.length !== arrB.length) { return false; } let tmpA = arrA.slice().sort(); let tmpB = arrB.slice().sort(); for (var i = 0; i < tmpA.length; i++) { if (tmpA[i] !== tmpB[i]) { return false; } } return true; } /** * Returns a string representation of an array. If the array contains objects with an id property, the id property is printed for each of the elements. * * @static * @param {Object[]} arr An array. * @param {*} arr[].id If the array contains an object with the property 'id', the properties value is printed. Else, the array elements value is printend. * @returns {String} A string representation of the array. */ static print(arr) { if (arr.length == 0) { return ""; } let s = "("; for (let i = 0; i < arr.length; i++) { s += arr[i].id ? arr[i].id + ", " : arr[i] + ", "; } s = s.substring(0, s.length - 2); return s + ")"; } /** * Run a function for each element in the array. The element is supplied as an argument for the callback function * * @static * @param {Array} arr An array. * @param {Function} callback The callback function that is called for each element. */ static each(arr, callback) { for (let i = 0; i < arr.length; i++) { callback(arr[i]); } } /** * Return the array element from an array containing objects, where a property of the object is set to a given value. * * @static * @param {Array} arr An array. * @param {(String|Number)} property A property contained within an object in the array. * @param {(String|Number)} value The value of the property. * @returns {*} The array element matching the value. */ static get(arr, property, value) { for (let i = 0; i < arr.length; i++) { if (arr[i][property] == value) { return arr[i]; } } } /** * Checks whether or not an array contains a given value. the options object passed as a second argument can contain three properties. value: The value to be searched for. property: The property that is to be searched for a given value. func: A function that is used as a callback to return either true or false in order to do a custom comparison. * * @static * @param {Array} arr An array. * @param {Object} options See method description. * @param {*} options.value The value for which to check. * @param {String} [options.property=undefined] The property on which to check. * @param {Function} [options.func=undefined] A custom property function. * @returns {Boolean} A boolean whether or not the array contains a value. */ static contains(arr, options) { if (!options.property && !options.func) { for (let i = 0; i < arr.length; i++) { if (arr[i] == options.value) { return true; } } } else if (options.func) { for (let i = 0; i < arr.length; i++) { if (options.func(arr[i])) { return true; } } } else { for (let i = 0; i < arr.length; i++) { if (arr[i][options.property] == options.value) { return true; } } } return false; } /** * Returns an array containing the intersection between two arrays. That is, values that are common to both arrays. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Array} The intersecting vlaues. */ static intersection(arrA, arrB) { let intersection = new Array(); for (let i = 0; i < arrA.length; i++) { for (let j = 0; j < arrB.length; j++) { if (arrA[i] === arrB[j]) { intersection.push(arrA[i]); } } } return intersection; } /** * Returns an array of unique elements contained in an array. * * @static * @param {Array} arr An array. * @returns {Array} An array of unique elements contained within the array supplied as an argument. */ static unique(arr) { let contains = {}; return arr.filter(function(i) { return contains[i] !== void 0 ? false : contains[i] = true; }); } /** * Count the number of occurences of a value in an array. * * @static * @param {Array} arr An array. * @param {*} value A value to be counted. * @returns {Number} The number of occurences of a value in the array. */ static count(arr, value) { let count = 0; for (let i = 0; i < arr.length; i++) { if (arr[i] === value) { count++; } } return count; } /** * Toggles the value of an array. If a value is not contained in an array, the array returned will contain all the values of the original array including the value. If a value is contained in an array, the array returned will contain all the values of the original array excluding the value. * * @static * @param {Array} arr An array. * @param {*} value A value to be toggled. * @returns {Array} The toggled array. */ static toggle(arr, value) { let newArr = Array(); let removed = false; for (let i = 0; i < arr.length; i++) { if (arr[i] !== value) { newArr.push(arr[i]); } else { removed = true; } } if (!removed) { newArr.push(value); } return newArr; } /** * Remove a value from an array. * * @static * @param {Array} arr An array. * @param {*} value A value to be removed. * @returns {Array} A new array with the element with a given value removed. */ static remove(arr, value) { let tmp = Array(); for (let i = 0; i < arr.length; i++) { if (arr[i] !== value) { tmp.push(arr[i]); } } return tmp; } /** * Remove a value from an array with unique values. * * @static * @param {Array} arr An array. * @param {*} value A value to be removed. * @returns {Array} An array with the element with a given value removed. */ static removeUnique(arr, value) { let index = arr.indexOf(value); if (index > -1) { arr.splice(index, 1); } return arr; } /** * Remove all elements contained in one array from another array. * * @static * @param {Array} arrA The array to be filtered. * @param {Array} arrB The array containing elements that will be removed from the other array. * @returns {Array} The filtered array. */ static removeAll(arrA, arrB) { return arrA.filter(function(item) { return arrB.indexOf(item) === -1; }); } /** * Merges two arrays and returns the result. The first array will be appended to the second array. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Array} The merged array. */ static merge(arrA, arrB) { let arr = new Array(arrA.length + arrB.length); for (let i = 0; i < arrA.length; i++) { arr[i] = arrA[i]; } for (let i = 0; i < arrB.length; i++) { arr[arrA.length + i] = arrB[i]; } return arr; } /** * Checks whether or not an array contains all the elements of another array, without regard to the order. * * @static * @param {Array} arrA An array. * @param {Array} arrB An array. * @returns {Boolean} A boolean indicating whether or not both array contain the same elements. */ static containsAll(arrA, arrB) { let containing = 0; for (let i = 0; i < arrA.length; i++) { for (let j = 0; j < arrB.length; j++) { if (arrA[i] === arrB[j]) { containing++; } } } return containing === arrB.length; } /** * Sort an array of atomic number information. Where the number is indicated as x, x.y, x.y.z, ... * * @param {Object[]} arr An array of vertex ids with their associated atomic numbers. * @param {Number} arr[].vertexId A vertex id. * @param {String} arr[].atomicNumber The atomic number associated with the vertex id. * @returns {Object[]} The array sorted by atomic number. Example of an array entry: { atomicNumber: 2, vertexId: 5 }. */ static sortByAtomicNumberDesc(arr) { let map = arr.map(function(e, i) { return { index: i, value: e.atomicNumber.split(".").map(Number) }; }); map.sort(function(a, b) { let min = Math.min(b.value.length, a.value.length); let i = 0; while (i < min && b.value[i] === a.value[i]) { i++; } return i === min ? b.value.length - a.value.length : b.value[i] - a.value[i]; }); return map.map(function(e) { return arr[e.index]; }); } /** * Copies a an n-dimensional array. * * @param {Array} arr The array to be copied. * @returns {Array} The copy. */ static deepCopy(arr) { let newArr = Array(); for (let i = 0; i < arr.length; i++) { let item = arr[i]; if (item instanceof Array) { newArr[i] = ArrayHelper.deepCopy(item); } else { newArr[i] = item; } } return newArr; } }; module2.exports = ArrayHelper; } }); // node_modules/smiles-drawer/src/MathHelper.js var require_MathHelper = __commonJS({ "node_modules/smiles-drawer/src/MathHelper.js"(exports, module2) { var MathHelper = class { /** * Rounds a value to a given number of decimals. * * @static * @param {Number} value A number. * @param {Number} decimals The number of decimals. * @returns {Number} A number rounded to a given number of decimals. */ static round(value, decimals) { decimals = decimals ? decimals : 1; return Number(Math.round(value + "e" + decimals) + "e-" + decimals); } /** * Returns the means of the angles contained in an array. In radians. * * @static * @param {Number[]} arr An array containing angles (in radians). * @returns {Number} The mean angle in radians. */ static meanAngle(arr) { let sin = 0; let cos = 0; for (var i = 0; i < arr.length; i++) { sin += Math.sin(arr[i]); cos += Math.cos(arr[i]); } return Math.atan2(sin / arr.length, cos / arr.length); } /** * Returns the inner angle of a n-sided regular polygon. * * @static * @param {Number} n Number of sides of a regular polygon. * @returns {Number} The inner angle of a given regular polygon. */ static innerAngle(n) { return MathHelper.toRad((n - 2) * 180 / n); } /** * Returns the circumradius of a n-sided regular polygon with a given side-length. * * @static * @param {Number} s The side length of the regular polygon. * @param {Number} n The number of sides. * @returns {Number} The circumradius of the regular polygon. */ static polyCircumradius(s, n) { return s / (2 * Math.sin(Math.PI / n)); } /** * Returns the apothem of a regular n-sided polygon based on its radius. * * @static * @param {Number} r The radius. * @param {Number} n The number of edges of the regular polygon. * @returns {Number} The apothem of a n-sided polygon based on its radius. */ static apothem(r, n) { return r * Math.cos(Math.PI / n); } static apothemFromSideLength(s, n) { let r = MathHelper.polyCircumradius(s, n); return MathHelper.apothem(r, n); } /** * The central angle of a n-sided regular polygon. In radians. * * @static * @param {Number} n The number of sides of the regular polygon. * @returns {Number} The central angle of the n-sided polygon in radians. */ static centralAngle(n) { return MathHelper.toRad(360 / n); } /** * Convertes radians to degrees. * * @static * @param {Number} rad An angle in radians. * @returns {Number} The angle in degrees. */ static toDeg(rad) { return rad * MathHelper.degFactor; } /** * Converts degrees to radians. * * @static * @param {Number} deg An angle in degrees. * @returns {Number} The angle in radians. */ static toRad(deg) { return deg * MathHelper.radFactor; } /** * Returns the parity of the permutation (1 or -1) * @param {(Array|Uint8Array)} arr An array containing the permutation. * @returns {Number} The parity of the permutation (1 or -1), where 1 means even and -1 means odd. */ static parityOfPermutation(arr) { let visited = new Uint8Array(arr.length); let evenLengthCycleCount = 0; let traverseCycle = function(i2, cycleLength = 0) { if (visited[i2] === 1) { return cycleLength; } cycleLength++; visited[i2] = 1; return traverseCycle(arr[i2], cycleLength); }; for (var i = 0; i < arr.length; i++) { if (visited[i] === 1) { continue; } let cycleLength = traverseCycle(i); evenLengthCycleCount += 1 - cycleLength % 2; } return evenLengthCycleCount % 2 ? -1 : 1; } /** The factor to convert degrees to radians. */ static get radFactor() { return Math.PI / 180; } /** The factor to convert radians to degrees. */ static get degFactor() { return 180 / Math.PI; } /** Two times PI. */ static get twoPI() { return 2 * Math.PI; } }; module2.exports = MathHelper; } }); // node_modules/smiles-drawer/src/Vector2.js var require_Vector2 = __commonJS({ "node_modules/smiles-drawer/src/Vector2.js"(exports, module2) { var Vector2 = class { /** * The constructor of the class Vector2. * * @param {(Number|Vector2)} x The initial x coordinate value or, if the single argument, a Vector2 object. * @param {Number} y The initial y coordinate value. */ constructor(x, y) { if (arguments.length == 0) { this.x = 0; this.y = 0; } else if (arguments.length == 1) { this.x = x.x; this.y = x.y; } else { this.x = x; this.y = y; } } /** * Clones this vector and returns the clone. * * @returns {Vector2} The clone of this vector. */ clone() { return new Vector2(this.x, this.y); } /** * Returns a string representation of this vector. * * @returns {String} A string representation of this vector. */ toString() { return "(" + this.x + "," + this.y + ")"; } /** * Add the x and y coordinate values of a vector to the x and y coordinate values of this vector. * * @param {Vector2} vec Another vector. * @returns {Vector2} Returns itself. */ add(vec) { this.x += vec.x; this.y += vec.y; return this; } /** * Subtract the x and y coordinate values of a vector from the x and y coordinate values of this vector. * * @param {Vector2} vec Another vector. * @returns {Vector2} Returns itself. */ subtract(vec) { this.x -= vec.x; this.y -= vec.y; return this; } /** * Divide the x and y coordinate values of this vector by a scalar. * * @param {Number} scalar The scalar. * @returns {Vector2} Returns itself. */ divide(scalar) { this.x /= scalar; this.y /= scalar; return this; } /** * Multiply the x and y coordinate values of this vector by the values of another vector. * * @param {Vector2} v A vector. * @returns {Vector2} Returns itself. */ multiply(v) { this.x *= v.x; this.y *= v.y; return this; } /** * Multiply the x and y coordinate values of this vector by a scalar. * * @param {Number} scalar The scalar. * @returns {Vector2} Returns itself. */ multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } /** * Inverts this vector. Same as multiply(-1.0). * * @returns {Vector2} Returns itself. */ invert() { this.x = -this.x; this.y = -this.y; return this; } /** * Returns the angle of this vector in relation to the coordinate system. * * @returns {Number} The angle in radians. */ angle() { return Math.atan2(this.y, this.x); } /** * Returns the euclidean distance between this vector and another vector. * * @param {Vector2} vec A vector. * @returns {Number} The euclidean distance between the two vectors. */ distance(vec) { return Math.sqrt((vec.x - this.x) * (vec.x - this.x) + (vec.y - this.y) * (vec.y - this.y)); } /** * Returns the squared euclidean distance between this vector and another vector. When only the relative distances of a set of vectors are needed, this is is less expensive than using distance(vec). * * @param {Vector2} vec Another vector. * @returns {Number} The squared euclidean distance of the two vectors. */ distanceSq(vec) { return (vec.x - this.x) * (vec.x - this.x) + (vec.y - this.y) * (vec.y - this.y); } /** * Checks whether or not this vector is in a clockwise or counter-clockwise rotational direction compared to another vector in relation to the coordinate system. * * @param {Vector2} vec Another vector. * @returns {Number} Returns -1, 0 or 1 if the vector supplied as an argument is clockwise, neutral or counter-clockwise respectively to this vector in relation to the coordinate system. */ clockwise(vec) { let a = this.y * vec.x; let b = this.x * vec.y; if (a > b) { return -1; } else if (a === b) { return 0; } return 1; } /** * Checks whether or not this vector is in a clockwise or counter-clockwise rotational direction compared to another vector in relation to an arbitrary third vector. * * @param {Vector2} center The central vector. * @param {Vector2} vec Another vector. * @returns {Number} Returns -1, 0 or 1 if the vector supplied as an argument is clockwise, neutral or counter-clockwise respectively to this vector in relation to an arbitrary third vector. */ relativeClockwise(center, vec) { let a = (this.y - center.y) * (vec.x - center.x); let b = (this.x - center.x) * (vec.y - center.y); if (a > b) { return -1; } else if (a === b) { return 0; } return 1; } /** * Rotates this vector by a given number of radians around the origin of the coordinate system. * * @param {Number} angle The angle in radians to rotate the vector. * @returns {Vector2} Returns itself. */ rotate(angle) { let tmp = new Vector2(0, 0); let cosAngle = Math.cos(angle); let sinAngle = Math.sin(angle); tmp.x = this.x * cosAngle - this.y * sinAngle; tmp.y = this.x * sinAngle + this.y * cosAngle; this.x = tmp.x; this.y = tmp.y; return this; } /** * Rotates this vector around another vector. * * @param {Number} angle The angle in radians to rotate the vector. * @param {Vector2} vec The vector which is used as the rotational center. * @returns {Vector2} Returns itself. */ rotateAround(angle, vec) { let s = Math.sin(angle); let c = Math.cos(angle); this.x -= vec.x; this.y -= vec.y; let x = this.x * c - this.y * s; let y = this.x * s + this.y * c; this.x = x + vec.x; this.y = y + vec.y; return this; } /** * Rotate a vector around a given center to the same angle as another vector (so that the two vectors and the center are in a line, with both vectors on one side of the center), keeps the distance from this vector to the center. * * @param {Vector2} vec The vector to rotate this vector to. * @param {Vector2} center The rotational center. * @param {Number} [offsetAngle=0.0] An additional amount of radians to rotate the vector. * @returns {Vector2} Returns itself. */ rotateTo(vec, center, offsetAngle = 0) { this.x += 1e-3; this.y -= 1e-3; let a = Vector2.subtract(this, center); let b = Vector2.subtract(vec, center); let angle = Vector2.angle(b, a); this.rotateAround(angle + offsetAngle, center); return this; } /** * Rotates the vector away from a specified vector around a center. * * @param {Vector2} vec The vector this one is rotated away from. * @param {Vector2} center The rotational center. * @param {Number} angle The angle by which to rotate. */ rotateAwayFrom(vec, center, angle) { this.rotateAround(angle, center); let distSqA = this.distanceSq(vec); this.rotateAround(-2 * angle, center); let distSqB = this.distanceSq(vec); if (distSqB < distSqA) { this.rotateAround(2 * angle, center); } } /** * Returns the angle in radians used to rotate this vector away from a given vector. * * @param {Vector2} vec The vector this one is rotated away from. * @param {Vector2} center The rotational center. * @param {Number} angle The angle by which to rotate. * @returns {Number} The angle in radians. */ getRotateAwayFromAngle(vec, center, angle) { let tmp = this.clone(); tmp.rotateAround(angle, center); let distSqA = tmp.distanceSq(vec); tmp.rotateAround(-2 * angle, center); let distSqB = tmp.distanceSq(vec); if (distSqB < distSqA) { return angle; } else { return -angle; } } /** * Returns the angle in radians used to rotate this vector towards a given vector. * * @param {Vector2} vec The vector this one is rotated towards to. * @param {Vector2} center The rotational center. * @param {Number} angle The angle by which to rotate. * @returns {Number} The angle in radians. */ getRotateTowardsAngle(vec, center, angle) { let tmp = this.clone(); tmp.rotateAround(angle, center); let distSqA = tmp.distanceSq(vec); tmp.rotateAround(-2 * angle, center); let distSqB = tmp.distanceSq(vec); if (distSqB > distSqA) { return angle; } else { return -angle; } } /** * Gets the angles between this vector and another vector around a common center of rotation. * * @param {Vector2} vec Another vector. * @param {Vector2} center The center of rotation. * @returns {Number} The angle between this vector and another vector around a center of rotation in radians. */ getRotateToAngle(vec, center) { let a = Vector2.subtract(this, center); let b = Vector2.subtract(vec, center); let angle = Vector2.angle(b, a); return Number.isNaN(angle) ? 0 : angle; } /** * Checks whether a vector lies within a polygon spanned by a set of vectors. * * @param {Vector2[]} polygon An array of vectors spanning the polygon. * @returns {Boolean} A boolean indicating whether or not this vector is within a polygon. */ isInPolygon(polygon) { let inside = false; for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { if (polygon[i].y > this.y != polygon[j].y > this.y && this.x < (polygon[j].x - polygon[i].x) * (this.y - polygon[i].y) / (polygon[j].y - polygon[i].y) + polygon[i].x) { inside = !inside; } } return inside; } /** * Returns the length of this vector. * * @returns {Number} The length of this vector. */ length() { return Math.sqrt(this.x * this.x + this.y * this.y); } /** * Returns the square of the length of this vector. * * @returns {Number} The square of the length of this vector. */ lengthSq() { return this.x * this.x + this.y * this.y; } /** * Normalizes this vector. * * @returns {Vector2} Returns itself. */ normalize() { this.divide(this.length()); return this; } /** * Returns a normalized copy of this vector. * * @returns {Vector2} A normalized copy of this vector. */ normalized() { return Vector2.divideScalar(this, this.length()); } /** * Calculates which side of a line spanned by two vectors this vector is. * * @param {Vector2} vecA A vector. * @param {Vector2} vecB A vector. * @returns {Number} A number indicating the side of this vector, given a line spanned by two other vectors. */ whichSide(vecA, vecB) { return (this.x - vecA.x) * (vecB.y - vecA.y) - (this.y - vecA.y) * (vecB.x - vecA.x); } /** * Checks whether or not this vector is on the same side of a line spanned by two vectors as another vector. * * @param {Vector2} vecA A vector spanning the line. * @param {Vector2} vecB A vector spanning the line. * @param {Vector2} vecC A vector to check whether or not it is on the same side as this vector. * @returns {Boolean} Returns a boolean indicating whether or not this vector is on the same side as another vector. */ sameSideAs(vecA, vecB, vecC) { let d = this.whichSide(vecA, vecB); let dRef = vecC.whichSide(vecA, vecB); return d < 0 && dRef < 0 || d == 0 && dRef == 0 || d > 0 && dRef > 0; } /** * Adds two vectors and returns the result as a new vector. * * @static * @param {Vector2} vecA A summand. * @param {Vector2} vecB A summand. * @returns {Vector2} Returns the sum of two vectors. */ static add(vecA, vecB) { return new Vector2(vecA.x + vecB.x, vecA.y + vecB.y); } /** * Subtracts one vector from another and returns the result as a new vector. * * @static * @param {Vector2} vecA The minuend. * @param {Vector2} vecB The subtrahend. * @returns {Vector2} Returns the difference of two vectors. */ static subtract(vecA, vecB) { return new Vector2(vecA.x - vecB.x, vecA.y - vecB.y); } /** * Multiplies two vectors (value by value) and returns the result. * * @static * @param {Vector2} vecA A vector. * @param {Vector2} vecB A vector. * @returns {Vector2} Returns the product of two vectors. */ static multiply(vecA, vecB) { return new Vector2(vecA.x * vecB.x, vecA.y * vecB.y); } /** * Multiplies two vectors (value by value) and returns the result. * * @static * @param {Vector2} vec A vector. * @param {Number} scalar A scalar. * @returns {Vector2} Returns the product of two vectors. */ static multiplyScalar(vec, scalar) { return new Vector2(vec.x, vec.y).multiplyScalar(scalar); } /** * Returns the midpoint of a line spanned by two vectors. * * @static * @param {Vector2} vecA A vector spanning the line. * @param {Vector2} vecB A vector spanning the line. * @returns {Vector2} The midpoint of the line spanned by two vectors. */ static midpoint(vecA, vecB) { return new Vector2((vecA.x + vecB.x) / 2, (vecA.y + vecB.y) / 2); } /** * Returns the normals of a line spanned by two vectors. * * @static * @param {Vector2} vecA A vector spanning the line. * @param {Vector2} vecB A vector spanning the line. * @returns {Vector2[]} An array containing the two normals, each represented by a vector. */ static normals(vecA, vecB) { let delta = Vector2.subtract(vecB, vecA); return [ new Vector2(-delta.y, delta.x), new Vector2(delta.y, -delta.x) ]; } /** * Returns the unit (normalized normal) vectors of a line spanned by two vectors. * * @static * @param {Vector2} vecA A vector spanning the line. * @param {Vector2} vecB A vector spanning the line. * @returns {Vector2[]} An array containing the two unit vectors. */ static units(vecA, vecB) { let delta = Vector2.subtract(vecB, vecA); return [ new Vector2(-delta.y, delta.x).normalize(), new Vector2(delta.y, -delta.x).normalize() ]; } /** * Divides a vector by another vector and returns the result as new vector. * * @static * @param {Vector2} vecA The dividend. * @param {Vector2} vecB The divisor. * @returns {Vector2} The fraction of the two vectors. */ static divide(vecA, vecB) { return new Vector2(vecA.x / vecB.x, vecA.y / vecB.y); } /** * Divides a vector by a scalar and returns the result as new vector. * * @static * @param {Vector2} vecA The dividend. * @param {Number} s The scalar. * @returns {Vector2} The fraction of the two vectors. */ static divideScalar(vecA, s) { return new Vector2(vecA.x / s, vecA.y / s); } /** * Returns the dot product of two vectors. * * @static * @param {Vector2} vecA A vector. * @param {Vector2} vecB A vector. * @returns {Number} The dot product of two vectors. */ static dot(vecA, vecB) { return vecA.x * vecB.x + vecA.y * vecB.y; } /** * Returns the angle between two vectors. * * @static * @param {Vector2} vecA A vector. * @param {Vector2} vecB A vector. * @returns {Number} The angle between two vectors in radians. */ static angle(vecA, vecB) { let dot = Vector2.dot(vecA, vecB); return Math.acos(dot / (vecA.length() * vecB.length())); } /** * Returns the angle between two vectors based on a third vector in between. * * @static * @param {Vector2} vecA A vector. * @param {Vector2} vecB A (central) vector. * @param {Vector2} vecC A vector. * @returns {Number} The angle in radians. */ static threePointangle(vecA, vecB, vecC) { let ab = Vector2.subtract(vecB, vecA); let bc = Vector2.subtract(vecC, vecB); let abLength = vecA.distance(vecB); let bcLength = vecB.distance(vecC); return Math.acos(Vector2.dot(ab, bc) / (abLength * bcLength)); } /** * Returns the scalar projection of a vector on another vector. * * @static * @param {Vector2} vecA The vector to be projected. * @param {Vector2} vecB The vector to be projection upon. * @returns {Number} The scalar component. */ static scalarProjection(vecA, vecB) { let unit = vecB.normalized(); return Vector2.dot(vecA, unit); } /** * Returns the average vector (normalized) of the input vectors. * * @static * @param {Array} vecs An array containing vectors. * @returns {Vector2} The resulting vector (normalized). */ static averageDirection(vecs) { let avg = new Vector2(0, 0); for (var i = 0; i < vecs.length; i++) { let vec = vecs[i]; avg.add(vec); } return avg.normalize(); } }; module2.exports = Vector2; } }); // node_modules/smiles-drawer/src/Vertex.js var require_Vertex = __commonJS({ "node_modules/smiles-drawer/src/Vertex.js"(exports, module2) { var MathHelper = require_MathHelper(); var ArrayHelper = require_ArrayHelper(); var Vector2 = require_Vector2(); var Atom = require_Atom(); var Vertex = class { /** * The constructor for the class Vertex. * * @param {Atom} value The value associated with this vertex. * @param {Number} [x=0] The initial x coordinate of the positional vector of this vertex. * @param {Number} [y=0] The initial y coordinate of the positional vector of this vertex. */ constructor(value, x = 0, y = 0) { this.id = null; this.value = value; this.position = new Vector2(x ? x : 0, y ? y : 0); this.previousPosition = new Vector2(0, 0); this.parentVertexId = null; this.children = Array(); this.spanningTreeChildren = Array(); this.edges = Array(); this.positioned = false; this.angle = null; this.dir = 1; this.neighbourCount = 0; this.neighbours = Array(); this.neighbouringElements = Array(); this.forcePositioned = false; } /** * Set the 2D coordinates of the vertex. * * @param {Number} x The x component of the coordinates. * @param {Number} y The y component of the coordinates. * */ setPosition(x, y) { this.position.x = x; this.position.y = y; } /** * Set the 2D coordinates of the vertex from a Vector2. * * @param {Vector2} v A 2D vector. * */ setPositionFromVector(v) { this.position.x = v.x; this.position.y = v.y; } /** * Add a child vertex id to this vertex. * @param {Number} vertexId The id of a vertex to be added as a child to this vertex. */ addChild(vertexId) { this.children.push(vertexId); this.neighbours.push(vertexId); this.neighbourCount++; } /** * Add a child vertex id to this vertex as the second child of the neighbours array, * except this vertex is the first vertex of the SMILE string, then it is added as the first. * This is used to get the correct ordering of neighbours for parity calculations. * If a hydrogen is implicitly attached to the chiral center, insert as the third child. * @param {Number} vertexId The id of a vertex to be added as a child to this vertex. * @param {Number} ringbondIndex The index of the ringbond. */ addRingbondChild(vertexId, ringbondIndex) { this.children.push(vertexId); if (this.value.bracket) { let index = 1; if (this.id === 0 && this.value.bracket.hcount === 0) { index = 0; } if (this.value.bracket.hcount === 1 && ringbondIndex === 0) { index = 2; } if (this.value.bracket.hcount === 1 && ringbondIndex === 1) { if (this.neighbours.length < 3) { index = 2; } else { index = 3; } } if (this.value.bracket.hcount === null && ringbondIndex === 0) { index = 1; } if (this.value.bracket.hcount === null && ringbondIndex === 1) { if (this.neighbours.length < 3) { index = 1; } else { index = 2; } } this.neighbours.splice(index, 0, vertexId); } else { this.neighbours.push(vertexId); } this.neighbourCount++; } /** * Set the vertex id of the parent. * * @param {Number} parentVertexId The parents vertex id. */ setParentVertexId(parentVertexId) { this.neighbourCount++; this.parentVertexId = parentVertexId; this.neighbours.push(parentVertexId); } /** * Returns true if this vertex is terminal (has no parent or child vertices), otherwise returns false. Always returns true if associated value has property hasAttachedPseudoElements set to true. * * @returns {Boolean} A boolean indicating whether or not this vertex is terminal. */ isTerminal() { if (this.value.hasAttachedPseudoElements) { return true; } return this.parentVertexId === null && this.children.length < 2 || this.children.length === 0; } /** * Clones this vertex and returns the clone. * * @returns {Vertex} A clone of this vertex. */ clone() { let clone = new Vertex(this.value, this.position.x, this.position.y); clone.id = this.id; clone.previousPosition = new Vector2(this.previousPosition.x, this.previousPosition.y); clone.parentVertexId = this.parentVertexId; clone.children = ArrayHelper.clone(this.children); clone.spanningTreeChildren = ArrayHelper.clone(this.spanningTreeChildren); clone.edges = ArrayHelper.clone(this.edges); clone.positioned = this.positioned; clone.angle = this.angle; clone.forcePositioned = this.forcePositioned; return clone; } /** * Returns true if this vertex and the supplied vertex both have the same id, else returns false. * * @param {Vertex} vertex The vertex to check. * @returns {Boolean} A boolean indicating whether or not the two vertices have the same id. */ equals(vertex) { return this.id === vertex.id; } /** * Returns the angle of this vertexes positional vector. If a reference vector is supplied in relations to this vector, else in relations to the coordinate system. * * @param {Vector2} [referenceVector=null] - The reference vector. * @param {Boolean} [returnAsDegrees=false] - If true, returns angle in degrees, else in radians. * @returns {Number} The angle of this vertex. */ getAngle(referenceVector = null, returnAsDegrees = false) { let u = null; if (!referenceVector) { u = Vector2.subtract(this.position, this.previousPosition); } else { u = Vector2.subtract(this.position, referenceVector); } if (returnAsDegrees) { return MathHelper.toDeg(u.angle()); } return u.angle(); } /** * Returns the suggested text direction when text is added at the position of this vertex. * * @param {Vertex[]} vertices The array of vertices for the current molecule. * @param {Boolean} onlyHorizontal In case the text direction should be limited to either left or right. * @returns {String} The suggested direction of the text. */ getTextDirection(vertices, onlyHorizontal = false) { let neighbours = this.getDrawnNeighbours(vertices); let angles = Array(); if (vertices.length === 1) { return "right"; } for (let i = 0; i < neighbours.length; i++) { angles.push(this.getAngle(vertices[neighbours[i]].position)); } let textAngle = MathHelper.meanAngle(angles); if (this.isTerminal() || onlyHorizontal) { if (Math.round(textAngle * 100) / 100 === 1.57) { textAngle = textAngle - 0.2; } textAngle = Math.round(Math.round(textAngle / Math.PI) * Math.PI); } else { let halfPi = Math.PI / 2; textAngle = Math.round(Math.round(textAngle / halfPi) * halfPi); } if (textAngle === 2) { return "down"; } else if (textAngle === -2) { return "up"; } else if (textAngle === 0 || textAngle === -0) { return "right"; } else if (textAngle === 3 || textAngle === -3) { return "left"; } else { return "down"; } } /** * Returns an array of ids of neighbouring vertices. * * @param {Number} [vertexId=null] If a value is supplied, the vertex with this id is excluded from the returned indices. * @returns {Number[]} An array containing the ids of neighbouring vertices. */ getNeighbours(vertexId = null) { if (vertexId === null) { return this.neighbours.slice(); } let arr = Array(); for (let i = 0; i < this.neighbours.length; i++) { if (this.neighbours[i] !== vertexId) { arr.push(this.neighbours[i]); } } return arr; } /** * Returns an array of ids of neighbouring vertices that will be drawn (vertex.value.isDrawn === true). * * @param {Vertex[]} vertices An array containing the vertices associated with the current molecule. * @returns {Number[]} An array containing the ids of neighbouring vertices that will be drawn. */ getDrawnNeighbours(vertices) { let arr = Array(); for (let i = 0; i < this.neighbours.length; i++) { if (vertices[this.neighbours[i]].value.isDrawn) { arr.push(this.neighbours[i]); } } return arr; } /** * Returns the number of neighbours of this vertex. * * @returns {Number} The number of neighbours. */ getNeighbourCount() { return this.neighbourCount; } /** * Returns a list of ids of vertices neighbouring this one in the original spanning tree, excluding the ringbond connections. * * @param {Number} [vertexId=null] If supplied, the vertex with this id is excluded from the array returned. * @returns {Number[]} An array containing the ids of the neighbouring vertices. */ getSpanningTreeNeighbours(vertexId = null) { let neighbours = Array(); for (let i = 0; i < this.spanningTreeChildren.length; i++) { if (vertexId === void 0 || vertexId != this.spanningTreeChildren[i]) { neighbours.push(this.spanningTreeChildren[i]); } } if (this.parentVertexId != null) { if (vertexId === void 0 || vertexId != this.parentVertexId) { neighbours.push(this.parentVertexId); } } return neighbours; } /** * Gets the next vertex in the ring in opposide direction to the supplied vertex id. * * @param {Vertex[]} vertices The array of vertices for the current molecule. * @param {Number} ringId The id of the ring containing this vertex. * @param {Number} previousVertexId The id of the previous vertex. The next vertex will be opposite from the vertex with this id as seen from this vertex. * @returns {Number} The id of the next vertex in the ring. */ getNextInRing(vertices, ringId, previousVertexId) { let neighbours = this.getNeighbours(); for (let i = 0; i < neighbours.length; i++) { if (ArrayHelper.contains(vertices[neighbours[i]].value.rings, { value: ringId }) && neighbours[i] != previousVertexId) { return neighbours[i]; } } return null; } }; module2.exports = Vertex; } }); // node_modules/smiles-drawer/src/RingConnection.js var require_RingConnection = __commonJS({ "node_modules/smiles-drawer/src/RingConnection.js"(exports, module2) { var Vertex = require_Vertex(); var Ring = require_Ring(); var RingConnection = class { /** * The constructor for the class RingConnection. * * @param {Ring} firstRing A ring. * @param {Ring} secondRing A ring. */ constructor(firstRing, secondRing) { this.id = null; this.firstRingId = firstRing.id; this.secondRingId = secondRing.id; this.vertices = /* @__PURE__ */ new Set(); for (var m = 0; m < firstRing.members.length; m++) { let c = firstRing.members[m]; for (let n = 0; n < secondRing.members.length; n++) { let d = secondRing.members[n]; if (c === d) { this.addVertex(c); } } } } /** * Adding a vertex to the ring connection. * * @param {Number} vertexId A vertex id. */ addVertex(vertexId) { this.vertices.add(vertexId); } /** * Update the ring id of this ring connection that is not the ring id supplied as the second argument. * * @param {Number} ringId A ring id. The new ring id to be set. * @param {Number} otherRingId A ring id. The id that is NOT to be updated. */ updateOther(ringId, otherRingId) { if (this.firstRingId === otherRingId) { this.secondRingId = ringId; } else { this.firstRingId = ringId; } } /** * Returns a boolean indicating whether or not a ring with a given id is participating in this ring connection. * * @param {Number} ringId A ring id. * @returns {Boolean} A boolean indicating whether or not a ring with a given id participates in this ring connection. */ containsRing(ringId) { return this.firstRingId === ringId || this.secondRingId === ringId; } /** * Checks whether or not this ring connection is a bridge in a bridged ring. * * @param {Vertex[]} vertices The array of vertices associated with the current molecule. * @returns {Boolean} A boolean indicating whether or not this ring connection is a bridge. */ isBridge(vertices) { if (this.vertices.size > 2) { return true; } for (let vertexId of this.vertices) { if (vertices[vertexId].value.rings.length > 2) { return true; } } return false; } /** * Checks whether or not two rings are connected by a bridged bond. * * @static * @param {RingConnection[]} ringConnections An array of ring connections containing the ring connections associated with the current molecule. * @param {Vertex[]} vertices An array of vertices containing the vertices associated with the current molecule. * @param {Number} firstRingId A ring id. * @param {Number} secondRingId A ring id. * @returns {Boolean} A boolean indicating whether or not two rings ar connected by a bridged bond. */ static isBridge(ringConnections, vertices, firstRingId, secondRingId) { let ringConnection = null; for (let i = 0; i < ringConnections.length; i++) { ringConnection = ringConnections[i]; if (ringConnection.firstRingId === firstRingId && ringConnection.secondRingId === secondRingId || ringConnection.firstRingId === secondRingId && ringConnection.secondRingId === firstRingId) { return ringConnection.isBridge(vertices); } } return false; } /** * Retruns the neighbouring rings of a given ring. * * @static * @param {RingConnection[]} ringConnections An array of ring connections containing ring connections associated with the current molecule. * @param {Number} ringId A ring id. * @returns {Number[]} An array of ring ids of neighbouring rings. */ static getNeighbours(ringConnections, ringId) { let neighbours = []; for (let i = 0; i < ringConnections.length; i++) { let ringConnection = ringConnections[i]; if (ringConnection.firstRingId === ringId) { neighbours.push(ringConnection.secondRingId); } else if (ringConnection.secondRingId === ringId) { neighbours.push(ringConnection.firstRingId); } } return neighbours; } /** * Returns an array of vertex ids associated with a given ring connection. * * @static * @param {RingConnection[]} ringConnections An array of ring connections containing ring connections associated with the current molecule. * @param {Number} firstRingId A ring id. * @param {Number} secondRingId A ring id. * @returns {Number[]} An array of vertex ids associated with the ring connection. */ static getVertices(ringConnections, firstRingId, secondRingId) { for (let i = 0; i < ringConnections.length; i++) { let ringConnection = ringConnections[i]; if (ringConnection.firstRingId === firstRingId && ringConnection.secondRingId === secondRingId || ringConnection.firstRingId === secondRingId && ringConnection.secondRingId === firstRingId) { return [...ringConnection.vertices]; } } } }; module2.exports = RingConnection; } }); // node_modules/smiles-drawer/src/Ring.js var require_Ring = __commonJS({ "node_modules/smiles-drawer/src/Ring.js"(exports, module2) { var ArrayHelper = require_ArrayHelper(); var Vector2 = require_Vector2(); var Vertex = require_Vertex(); var RingConnection = require_RingConnection(); var Ring = class { /** * The constructor for the class Ring. * * @param {Number[]} members An array containing the vertex ids of the members of the ring to be created. */ constructor(members) { this.id = null; this.members = members; this.edges = []; this.insiders = []; this.neighbours = []; this.positioned = false; this.center = new Vector2(0, 0); this.rings = []; this.isBridged = false; this.isPartOfBridged = false; this.isSpiro = false; this.isFused = false; this.centralAngle = 0; this.canFlip = true; } /** * Clones this ring and returns the clone. * * @returns {Ring} A clone of this ring. */ clone() { let clone = new Ring(this.members); clone.id = this.id; clone.insiders = ArrayHelper.clone(this.insiders); clone.neighbours = ArrayHelper.clone(this.neighbours); clone.positioned = this.positioned; clone.center = this.center.clone(); clone.rings = ArrayHelper.clone(this.rings); clone.isBridged = this.isBridged; clone.isPartOfBridged = this.isPartOfBridged; clone.isSpiro = this.isSpiro; clone.isFused = this.isFused; clone.centralAngle = this.centralAngle; clone.canFlip = this.canFlip; return clone; } /** * Returns the size (number of members) of this ring. * * @returns {Number} The size (number of members) of this ring. */ getSize() { return this.members.length; } /** * Gets the polygon representation (an array of the ring-members positional vectors) of this ring. * * @param {Vertex[]} vertices An array of vertices representing the current molecule. * @returns {Vector2[]} An array of the positional vectors of the ring members. */ getPolygon(vertices) { let polygon = []; for (let i = 0; i < this.members.length; i++) { polygon.push(vertices[this.members[i]].position); } return polygon; } /** * Returns the angle of this ring in relation to the coordinate system. * * @returns {Number} The angle in radians. */ getAngle() { return Math.PI - this.centralAngle; } /** * Loops over the members of this ring from a given start position in a direction opposite to the vertex id passed as the previousId. * * @param {Vertex[]} vertices The vertices associated with the current molecule. * @param {Function} callback A callback with the current vertex id as a parameter. * @param {Number} startVertexId The vertex id of the start vertex. * @param {Number} previousVertexId The vertex id of the previous vertex (the loop calling the callback function will run in the opposite direction of this vertex). */ eachMember(vertices, callback, startVertexId, previousVertexId) { startVertexId = startVertexId || startVertexId === 0 ? startVertexId : this.members[0]; let current = startVertexId; let max = 0; while (current != null && max < 100) { let prev = current; callback(prev); current = vertices[current].getNextInRing(vertices, this.id, previousVertexId); previousVertexId = prev; if (current == startVertexId) { current = null; } max++; } } /** * Returns an array containing the neighbouring rings of this ring ordered by ring size. * * @param {RingConnection[]} ringConnections An array of ring connections associated with the current molecule. * @returns {Object[]} An array of neighbouring rings sorted by ring size. Example: { n: 5, neighbour: 1 }. */ getOrderedNeighbours(ringConnections) { let orderedNeighbours = Array(this.neighbours.length); for (let i = 0; i < this.neighbours.length; i++) { let vertices = RingConnection.getVertices(ringConnections, this.id, this.neighbours[i]); orderedNeighbours[i] = { n: vertices.length, neighbour: this.neighbours[i] }; } orderedNeighbours.sort(function(a, b) { return b.n - a.n; }); return orderedNeighbours; } /** * Check whether this ring is an implicitly defined benzene-like (e.g. C1=CC=CC=C1) with 6 members and 3 double bonds. * * @param {Vertex[]} vertices An array of vertices associated with the current molecule. * @returns {Boolean} A boolean indicating whether or not this ring is an implicitly defined benzene-like. */ isBenzeneLike(vertices) { let db = this.getDoubleBondCount(vertices); let length = this.members.length; return db === 3 && length === 6 || db === 2 && length === 5; } /** * Get the number of double bonds inside this ring. * * @param {Vertex[]} vertices An array of vertices associated with the current molecule. * @returns {Number} The number of double bonds inside this ring. */ getDoubleBondCount(vertices) { let doubleBondCount = 0; for (let i = 0; i < this.members.length; i++) { let atom = vertices[this.members[i]].value; if (atom.bondType === "=" || atom.branchBond === "=") { doubleBondCount++; } } return doubleBondCount; } /** * Checks whether or not this ring contains a member with a given vertex id. * * @param {Number} vertexId A vertex id. * @returns {Boolean} A boolean indicating whether or not this ring contains a member with the given vertex id. */ contains(vertexId) { for (let i = 0; i < this.members.length; i++) { if (this.members[i] == vertexId) { return true; } } return false; } }; module2.exports = Ring; } }); // node_modules/smiles-drawer/src/Atom.js var require_Atom = __commonJS({ "node_modules/smiles-drawer/src/Atom.js"(exports, module2) { var ArrayHelper = require_ArrayHelper(); var Vertex = require_Vertex(); var Ring = require_Ring(); var Atom = class { /** * The constructor of the class Atom. * * @param {String} element The one-letter code of the element. * @param {String} [bondType='-'] The type of the bond associated with this atom. */ constructor(element, bondType = "-") { this.idx = null; this.element = element.length === 1 ? element.toUpperCase() : element; this.drawExplicit = false; this.ringbonds = Array(); this.rings = Array(); this.bondType = bondType; this.branchBond = null; this.isBridge = false; this.isBridgeNode = false; this.originalRings = Array(); this.bridgedRing = null; this.anchoredRings = Array(); this.bracket = null; this.plane = 0; this.attachedPseudoElements = {}; this.hasAttachedPseudoElements = false; this.isDrawn = true; this.isConnectedToRing = false; this.neighbouringElements = Array(); this.isPartOfAromaticRing = element !== this.element; this.bondCount = 0; this.chirality = ""; this.isStereoCenter = false; this.priority = 0; this.mainChain = false; this.hydrogenDirection = "down"; this.subtreeDepth = 1; this.hasHydrogen = false; this.class = void 0; } /** * Adds a neighbouring element to this atom. * * @param {String} element A string representing an element. */ addNeighbouringElement(element) { this.neighbouringElements.push(element); } /** * Attaches a pseudo element (e.g. Ac) to the atom. * @param {String} element The element identifier (e.g. Br, C, ...). * @param {String} previousElement The element that is part of the main chain (not the terminals that are converted to the pseudo element or concatinated). * @param {Number} [hydrogenCount=0] The number of hydrogens for the element. * @param {Number} [charge=0] The charge for the element. */ attachPseudoElement(element, previousElement, hydrogenCount = 0, charge = 0) { if (hydrogenCount === null) { hydrogenCount = 0; } if (charge === null) { charge = 0; } let key = hydrogenCount + element + charge; if (this.attachedPseudoElements[key]) { this.attachedPseudoElements[key].count += 1; } else { this.attachedPseudoElements[key] = { element, count: 1, hydrogenCount, previousElement, charge }; } this.hasAttachedPseudoElements = true; } /** * Returns the attached pseudo elements sorted by hydrogen count (ascending). * * @returns {Object} The sorted attached pseudo elements. */ getAttachedPseudoElements() { let ordered = {}; let that = this; Object.keys(this.attachedPseudoElements).sort().forEach(function(key) { ordered[key] = that.attachedPseudoElements[key]; }); return ordered; } /** * Returns the number of attached pseudo elements. * * @returns {Number} The number of attached pseudo elements. */ getAttachedPseudoElementsCount() { return Object.keys(this.attachedPseudoElements).length; } /** * Returns whether this atom is a heteroatom (not C and not H). * * @returns {Boolean} A boolean indicating whether this atom is a heteroatom. */ isHeteroAtom() { return this.element !== "C" && this.element !== "H"; } /** * Defines this atom as the anchor for a ring. When doing repositionings of the vertices and the vertex associated with this atom is moved, the center of this ring is moved as well. * * @param {Number} ringId A ring id. */ addAnchoredRing(ringId) { if (!ArrayHelper.contains(this.anchoredRings, { value: ringId })) { this.anchoredRings.push(ringId); } } /** * Returns the number of ringbonds (breaks in rings to generate the MST of the smiles) within this atom is connected to. * * @returns {Number} The number of ringbonds this atom is connected to. */ getRingbondCount() { return this.ringbonds.length; } /** * Backs up the current rings. */ backupRings() { this.originalRings = Array(this.rings.length); for (let i = 0; i < this.rings.length; i++) { this.originalRings[i] = this.rings[i]; } } /** * Restores the most recent backed up rings. */ restoreRings() { this.rings = Array(this.originalRings.length); for (let i = 0; i < this.originalRings.length; i++) { this.rings[i] = this.originalRings[i]; } } /** * Checks whether or not two atoms share a common ringbond id. A ringbond is a break in a ring created when generating the spanning tree of a structure. * * @param {Atom} atomA An atom. * @param {Atom} atomB An atom. * @returns {Boolean} A boolean indicating whether or not two atoms share a common ringbond. */ haveCommonRingbond(atomA, atomB) { for (let i = 0; i < atomA.ringbonds.length; i++) { for (let j = 0; j < atomB.ringbonds.length; j++) { if (atomA.ringbonds[i].id == atomB.ringbonds[j].id) { return true; } } } return false; } /** * Check whether or not the neighbouring elements of this atom equal the supplied array. * * @param {String[]} arr An array containing all the elements that are neighbouring this atom. E.g. ['C', 'O', 'O', 'N'] * @returns {Boolean} A boolean indicating whether or not the neighbours match the supplied array of elements. */ neighbouringElementsEqual(arr) { if (arr.length !== this.neighbouringElements.length) { return false; } arr.sort(); this.neighbouringElements.sort(); for (var i = 0; i < this.neighbouringElements.length; i++) { if (arr[i] !== this.neighbouringElements[i]) { return false; } } return true; } /** * Get the atomic number of this atom. * * @returns {Number} The atomic number of this atom. */ getAtomicNumber() { return Atom.atomicNumbers[this.element]; } /** * Get the maximum number of bonds for this atom. * * @returns {Number} The maximum number of bonds of this atom. */ getMaxBonds() { return Atom.maxBonds[this.element]; } /** * A map mapping element symbols to their maximum bonds. */ static get maxBonds() { return { "H": 1, "C": 4, "N": 3, "O": 2, "P": 3, "S": 2, "B": 3, "F": 1, "I": 1, "Cl": 1, "Br": 1 }; } /** * A map mapping element symbols to the atomic number. */ static get atomicNumbers() { return { "H": 1, "He": 2, "Li": 3, "Be": 4, "B": 5, "b": 5, "C": 6, "c": 6, "N": 7, "n": 7, "O": 8, "o": 8, "F": 9, "Ne": 10, "Na": 11, "Mg": 12, "Al": 13, "Si": 14, "P": 15, "p": 15, "S": 16, "s": 16, "Cl": 17, "Ar": 18, "K": 19, "Ca": 20, "Sc": 21, "Ti": 22, "V": 23, "Cr": 24, "Mn": 25, "Fe": 26, "Co": 27, "Ni": 28, "Cu": 29, "Zn": 30, "Ga": 31, "Ge": 32, "As": 33, "Se": 34, "Br": 35, "Kr": 36, "Rb": 37, "Sr": 38, "Y": 39, "Zr": 40, "Nb": 41, "Mo": 42, "Tc": 43, "Ru": 44, "Rh": 45, "Pd": 46, "Ag": 47, "Cd": 48, "In": 49, "Sn": 50, "Sb": 51, "Te": 52, "I": 53, "Xe": 54, "Cs": 55, "Ba": 56, "La": 57, "Ce": 58, "Pr": 59, "Nd": 60, "Pm": 61, "Sm": 62, "Eu": 63, "Gd": 64, "Tb": 65, "Dy": 66, "Ho": 67, "Er": 68, "Tm": 69, "Yb": 70, "Lu": 71, "Hf": 72, "Ta": 73, "W": 74, "Re": 75, "Os": 76, "Ir": 77, "Pt": 78, "Au": 79, "Hg": 80, "Tl": 81, "Pb": 82, "Bi": 83, "Po": 84, "At": 85, "Rn": 86, "Fr": 87, "Ra": 88, "Ac": 89, "Th": 90, "Pa": 91, "U": 92, "Np": 93, "Pu": 94, "Am": 95, "Cm": 96, "Bk": 97, "Cf": 98, "Es": 99, "Fm": 100, "Md": 101, "No": 102, "Lr": 103, "Rf": 104, "Db": 105, "Sg": 106, "Bh": 107, "Hs": 108, "Mt": 109, "Ds": 110, "Rg": 111, "Cn": 112, "Uut": 113, "Uuq": 114, "Uup": 115, "Uuh": 116, "Uus": 117, "Uuo": 118 }; } /** * A map mapping element symbols to the atomic mass. */ static get mass() { return { "H": 1, "He": 2, "Li": 3, "Be": 4, "B": 5, "b": 5, "C": 6, "c": 6, "N": 7, "n": 7, "O": 8, "o": 8, "F": 9, "Ne": 10, "Na": 11, "Mg": 12, "Al": 13, "Si": 14, "P": 15, "p": 15, "S": 16, "s": 16, "Cl": 17, "Ar": 18, "K": 19, "Ca": 20, "Sc": 21, "Ti": 22, "V": 23, "Cr": 24, "Mn": 25, "Fe": 26, "Co": 27, "Ni": 28, "Cu": 29, "Zn": 30, "Ga": 31, "Ge": 32, "As": 33, "Se": 34, "Br": 35, "Kr": 36, "Rb": 37, "Sr": 38, "Y": 39, "Zr": 40, "Nb": 41, "Mo": 42, "Tc": 43, "Ru": 44, "Rh": 45, "Pd": 46, "Ag": 47, "Cd": 48, "In": 49, "Sn": 50, "Sb": 51, "Te": 52, "I": 53, "Xe": 54, "Cs": 55, "Ba": 56, "La": 57, "Ce": 58, "Pr": 59, "Nd": 60, "Pm": 61, "Sm": 62, "Eu": 63, "Gd": 64, "Tb": 65, "Dy": 66, "Ho": 67, "Er": 68, "Tm": 69, "Yb": 70, "Lu": 71, "Hf": 72, "Ta": 73, "W": 74, "Re": 75, "Os": 76, "Ir": 77, "Pt": 78, "Au": 79, "Hg": 80, "Tl": 81, "Pb": 82, "Bi": 83, "Po": 84, "At": 85, "Rn": 86, "Fr": 87, "Ra": 88, "Ac": 89, "Th": 90, "Pa": 91, "U": 92, "Np": 93, "Pu": 94, "Am": 95, "Cm": 96, "Bk": 97, "Cf": 98, "Es": 99, "Fm": 100, "Md": 101, "No": 102, "Lr": 103, "Rf": 104, "Db": 105, "Sg": 106, "Bh": 107, "Hs": 108, "Mt": 109, "Ds": 110, "Rg": 111, "Cn": 112, "Uut": 113, "Uuq": 114, "Uup": 115, "Uuh": 116, "Uus": 117, "Uuo": 118 }; } }; module2.exports = Atom; } }); // node_modules/smiles-drawer/src/Line.js var require_Line = __commonJS({ "node_modules/smiles-drawer/src/Line.js"(exports, module2) { var Vector2 = require_Vector2(); var Line = class { /** * The constructor for the class Line. * * @param {Vector2} [from=new Vector2(0, 0)] A vector marking the beginning of the line. * @param {Vector2} [to=new Vector2(0, 0)] A vector marking the end of the line. * @param {string} [elementFrom=null] A one-letter representation of the element associated with the vector marking the beginning of the line. * @param {string} [elementTo=null] A one-letter representation of the element associated with the vector marking the end of the line. * @param {Boolean} [chiralFrom=false] Whether or not the from atom is a chiral center. * @param {Boolean} [chiralTo=false] Whether or not the to atom is a chiral center. */ constructor(from = new Vector2(0, 0), to = new Vector2(0, 0), elementFrom = null, elementTo = null, chiralFrom = false, chiralTo = false) { this.from = from; this.to = to; this.elementFrom = elementFrom; this.elementTo = elementTo; this.chiralFrom = chiralFrom; this.chiralTo = chiralTo; } /** * Clones this line and returns the clone. * * @returns {Line} A clone of this line. */ clone() { return new Line(this.from.clone(), this.to.clone(), this.elementFrom, this.elementTo); } /** * Returns the length of this line. * * @returns {Number} The length of this line. */ getLength() { return Math.sqrt(Math.pow(this.to.x - this.from.x, 2) + Math.pow(this.to.y - this.from.y, 2)); } /** * Returns the angle of the line in relation to the coordinate system (the x-axis). * * @returns {Number} The angle in radians. */ getAngle() { let diff = Vector2.subtract(this.getRightVector(), this.getLeftVector()); return diff.angle(); } /** * Returns the right vector (the vector with the larger x value). * * @returns {Vector2} The right vector. */ getRightVector() { if (this.from.x < this.to.x) { return this.to; } else { return this.from; } } /** * Returns the left vector (the vector with the smaller x value). * * @returns {Vector2} The left vector. */ getLeftVector() { if (this.from.x < this.to.x) { return this.from; } else { return this.to; } } /** * Returns the element associated with the right vector (the vector with the larger x value). * * @returns {String} The element associated with the right vector. */ getRightElement() { if (this.from.x < this.to.x) { return this.elementTo; } else { return this.elementFrom; } } /** * Returns the element associated with the left vector (the vector with the smaller x value). * * @returns {String} The element associated with the left vector. */ getLeftElement() { if (this.from.x < this.to.x) { return this.elementFrom; } else { return this.elementTo; } } /** * Returns whether or not the atom associated with the right vector (the vector with the larger x value) is a chiral center. * * @returns {Boolean} Whether or not the atom associated with the right vector is a chiral center. */ getRightChiral() { if (this.from.x < this.to.x) { return this.chiralTo; } else { return this.chiralFrom; } } /** * Returns whether or not the atom associated with the left vector (the vector with the smaller x value) is a chiral center. * * @returns {Boolean} Whether or not the atom associated with the left vector is a chiral center. */ getLeftChiral() { if (this.from.x < this.to.x) { return this.chiralFrom; } else { return this.chiralTo; } } /** * Set the value of the right vector. * * @param {Number} x The x value. * @param {Number} y The y value. * @returns {Line} This line. */ setRightVector(x, y) { if (this.from.x < this.to.x) { this.to.x = x; this.to.y = y; } else { this.from.x = x; this.from.y = y; } return this; } /** * Set the value of the left vector. * * @param {Number} x The x value. * @param {Number} y The y value. * @returns {Line} This line. */ setLeftVector(x, y) { if (this.from.x < this.to.x) { this.from.x = x; this.from.y = y; } else { this.to.x = x; this.to.y = y; } return this; } /** * Rotates this line to be aligned with the x-axis. The center of rotation is the left vector. * * @returns {Line} This line. */ rotateToXAxis() { let left = this.getLeftVector(); this.setRightVector(left.x + this.getLength(), left.y); return this; } /** * Rotate the line by a given value (in radians). The center of rotation is the left vector. * * @param {Number} theta The angle (in radians) to rotate the line. * @returns {Line} This line. */ rotate(theta) { let l = this.getLeftVector(); let r = this.getRightVector(); let sinTheta = Math.sin(theta); let cosTheta = Math.cos(theta); let x = cosTheta * (r.x - l.x) - sinTheta * (r.y - l.y) + l.x; let y = sinTheta * (r.x - l.x) - cosTheta * (r.y - l.y) + l.y; this.setRightVector(x, y); return this; } /** * Shortens this line from the "from" direction by a given value (in pixels). * * @param {Number} by The length in pixels to shorten the vector by. * @returns {Line} This line. */ shortenFrom(by) { let f = Vector2.subtract(this.to, this.from); f.normalize(); f.multiplyScalar(by); this.from.add(f); return this; } /** * Shortens this line from the "to" direction by a given value (in pixels). * * @param {Number} by The length in pixels to shorten the vector by. * @returns {Line} This line. */ shortenTo(by) { let f = Vector2.subtract(this.from, this.to); f.normalize(); f.multiplyScalar(by); this.to.add(f); return this; } /** * Shorten the right side. * * @param {Number} by The length in pixels to shorten the vector by. * @returns {Line} Returns itself. */ shortenRight(by) { if (this.from.x < this.to.x) { this.shortenTo(by); } else { this.shortenFrom(by); } return this; } /** * Shorten the left side. * * @param {Number} by The length in pixels to shorten the vector by. * @returns {Line} Returns itself. */ shortenLeft(by) { if (this.from.x < this.to.x) { this.shortenFrom(by); } else { this.shortenTo(by); } return this; } /** * Shortens this line from both directions by a given value (in pixels). * * @param {Number} by The length in pixels to shorten the vector by. * @returns {Line} This line. */ shorten(by) { let f = Vector2.subtract(this.from, this.to); f.normalize(); f.multiplyScalar(by / 2); this.to.add(f); this.from.subtract(f); return this; } }; module2.exports = Line; } }); // node_modules/smiles-drawer/src/Edge.js var require_Edge = __commonJS({ "node_modules/smiles-drawer/src/Edge.js"(exports, module2) { var Edge = class { /** * The constructor for the class Edge. * * @param {Number} sourceId A vertex id. * @param {Number} targetId A vertex id. * @param {Number} [weight=1] The weight of the edge. */ constructor(sourceId, targetId, weight = 1) { this.id = null; this.sourceId = sourceId; this.targetId = targetId; this.weight = weight; this.bondType = "-"; this.isPartOfAromaticRing = false; this.center = false; this.wedge = ""; } /** * Set the bond type of this edge. This also sets the edge weight. * @param {String} bondType */ setBondType(bondType) { this.bondType = bondType; this.weight = Edge.bonds[bondType]; } /** * An object mapping the bond type to the number of bonds. * * @returns {Object} The object containing the map. */ static get bonds() { return { "-": 1, "/": 1, "\\": 1, "=": 2, "#": 3, "$": 4 }; } }; module2.exports = Edge; } }); // node_modules/smiles-drawer/src/UtilityFunctions.js var require_UtilityFunctions = __commonJS({ "node_modules/smiles-drawer/src/UtilityFunctions.js"(exports, module2) { function getChargeText(charge) { if (charge === 1) { return "+"; } else if (charge === 2) { return "2+"; } else if (charge === -1) { return "-"; } else if (charge === -2) { return "2-"; } else { return ""; } } module2.exports = { getChargeText }; } }); // node_modules/smiles-drawer/src/CanvasWrapper.js var require_CanvasWrapper = __commonJS({ "node_modules/smiles-drawer/src/CanvasWrapper.js"(exports, module2) { var MathHelper = require_MathHelper(); var Vector2 = require_Vector2(); var Line = require_Line(); var Vertex = require_Vertex(); var Ring = require_Ring(); var { getChargeText } = require_UtilityFunctions(); var CanvasWrapper = class { /** * The constructor for the class CanvasWrapper. * * @param {(String|HTMLElement)} target The canvas id or the canvas HTMLElement. * @param {ThemeManager} themeManager Theme manager for setting proper colors. * @param {Object} options The smiles drawer options object. */ constructor(target, themeManager, options) { if (typeof target === "string" || target instanceof String) { this.canvas = document.getElementById(target); } else { this.canvas = target; } this.ctx = this.canvas.getContext("2d"); this.themeManager = themeManager; this.opts = options; this.drawingWidth = 0; this.drawingHeight = 0; this.offsetX = 0; this.offsetY = 0; this.fontLarge = this.opts.fontSizeLarge + "pt Helvetica, Arial, sans-serif"; this.fontSmall = this.opts.fontSizeSmall + "pt Helvetica, Arial, sans-serif"; this.updateSize(this.opts.width, this.opts.height); this.ctx.font = this.fontLarge; this.hydrogenWidth = this.ctx.measureText("H").width; this.halfHydrogenWidth = this.hydrogenWidth / 2; this.halfBondThickness = this.opts.bondThickness / 2; } /** * Update the width and height of the canvas * * @param {Number} width * @param {Number} height */ updateSize(width, height) { this.devicePixelRatio = window.devicePixelRatio || 1; this.backingStoreRatio = this.ctx.webkitBackingStorePixelRatio || this.ctx.mozBackingStorePixelRatio || this.ctx.msBackingStorePixelRatio || this.ctx.oBackingStorePixelRatio || this.ctx.backingStorePixelRatio || 1; this.ratio = this.devicePixelRatio / this.backingStoreRatio; if (this.ratio !== 1) { this.canvas.width = width * this.ratio; this.canvas.height = height * this.ratio; this.canvas.style.width = width + "px"; this.canvas.style.height = height + "px"; this.ctx.setTransform(this.ratio, 0, 0, this.ratio, 0, 0); } else { this.canvas.width = width * this.ratio; this.canvas.height = height * this.ratio; } } /** * Sets a provided theme. * * @param {Object} theme A theme from the smiles drawer options. */ setTheme(theme) { this.colors = theme; } /** * Scale the canvas based on vertex positions. * * @param {Vertex[]} vertices An array of vertices containing the vertices associated with the current molecule. */ scale(vertices) { let maxX = -Number.MAX_VALUE; let maxY = -Number.MAX_VALUE; let minX = Number.MAX_VALUE; let minY = Number.MAX_VALUE; for (var i = 0; i < vertices.length; i++) { if (!vertices[i].value.isDrawn) { continue; } let p = vertices[i].position; if (maxX < p.x) maxX = p.x; if (maxY < p.y) maxY = p.y; if (minX > p.x) minX = p.x; if (minY > p.y) minY = p.y; } var padding = this.opts.padding; maxX += padding; maxY += padding; minX -= padding; minY -= padding; this.drawingWidth = maxX - minX; this.drawingHeight = maxY - minY; var scaleX = this.canvas.offsetWidth / this.drawingWidth; var scaleY = this.canvas.offsetHeight / this.drawingHeight; var scale = scaleX < scaleY ? scaleX : scaleY; this.ctx.scale(scale, scale); this.offsetX = -minX; this.offsetY = -minY; if (scaleX < scaleY) { this.offsetY += this.canvas.offsetHeight / (2 * scale) - this.drawingHeight / 2; } else { this.offsetX += this.canvas.offsetWidth / (2 * scale) - this.drawingWidth / 2; } } /** * Resets the transform of the canvas. */ reset() { this.ctx.setTransform(1, 0, 0, 1, 0, 0); } /** * Returns the hex code of a color associated with a key from the current theme. * * @param {String} key The color key in the theme (e.g. C, N, BACKGROUND, ...). * @returns {String} A color hex value. */ getColor(key) { key = key.toUpperCase(); if (key in this.colors) { return this.colors[key]; } return this.colors["C"]; } /** * Draws a circle to a canvas context. * @param {Number} x The x coordinate of the circles center. * @param {Number} y The y coordinate of the circles center. * @param {Number} radius The radius of the circle * @param {String} color A hex encoded color. * @param {Boolean} [fill=true] Whether to fill or stroke the circle. * @param {Boolean} [debug=false] Draw in debug mode. * @param {String} [debugText=''] A debug message. */ drawCircle(x, y, radius, color, fill = true, debug = false, debugText = "") { let ctx = this.ctx; let offsetX = this.offsetX; let offsetY = this.offsetY; ctx.save(); ctx.lineWidth = 1.5; ctx.beginPath(); ctx.arc(x + offsetX, y + offsetY, radius, 0, MathHelper.twoPI, true); ctx.closePath(); if (debug) { if (fill) { ctx.fillStyle = "#f00"; ctx.fill(); } else { ctx.strokeStyle = "#f00"; ctx.stroke(); } this.drawDebugText(x, y, debugText); } else { if (fill) { ctx.fillStyle = color; ctx.fill(); } else { ctx.strokeStyle = color; ctx.stroke(); } } ctx.restore(); } /** * Draw a line to a canvas. * * @param {Line} line A line. * @param {Boolean} [dashed=false] Whether or not the line is dashed. * @param {Number} [alpha=1.0] The alpha value of the color. */ drawLine(line, dashed = false, alpha = 1) { let ctx = this.ctx; let offsetX = this.offsetX; let offsetY = this.offsetY; let shortLine = line.clone().shorten(4); let l = shortLine.getLeftVector().clone(); let r = shortLine.getRightVector().clone(); l.x += offsetX; l.y += offsetY; r.x += offsetX; r.y += offsetY; if (!dashed) { ctx.save(); ctx.globalCompositeOperation = "destination-out"; ctx.beginPath(); ctx.moveTo(l.x, l.y); ctx.lineTo(r.x, r.y); ctx.lineCap = "round"; ctx.lineWidth = this.opts.bondThickness + 1.2; ctx.strokeStyle = this.themeManager.getColor("BACKGROUND"); ctx.stroke(); ctx.globalCompositeOperation = "source-over"; ctx.restore(); } l = line.getLeftVector().clone(); r = line.getRightVector().clone(); l.x += offsetX; l.y += offsetY; r.x += offsetX; r.y += offsetY; ctx.save(); ctx.beginPath(); ctx.moveTo(l.x, l.y); ctx.lineTo(r.x, r.y); ctx.lineCap = "round"; ctx.lineWidth = this.opts.bondThickness; let gradient = this.ctx.createLinearGradient(l.x, l.y, r.x, r.y); gradient.addColorStop(0.4, this.themeManager.getColor(line.getLeftElement()) || this.themeManager.getColor("C")); gradient.addColorStop(0.6, this.themeManager.getColor(line.getRightElement()) || this.themeManager.getColor("C")); if (dashed) { ctx.setLineDash([1, 1.5]); ctx.lineWidth = this.opts.bondThickness / 1.5; } if (alpha < 1) { ctx.globalAlpha = alpha; } ctx.strokeStyle = gradient; ctx.stroke(); ctx.restore(); } /** * Draw a wedge on the canvas. * * @param {Line} line A line. * @param {Number} width The wedge width. */ drawWedge(line, width = 1) { if (isNaN(line.from.x) || isNaN(line.from.y) || isNaN(line.to.x) || isNaN(line.to.y)) { return; } let ctx = this.ctx; let offsetX = this.offsetX; let offsetY = this.offsetY; let shortLine = line.clone().shorten(5); let l = shortLine.getLeftVector().clone(); let r = shortLine.getRightVector().clone(); l.x += offsetX; l.y += offsetY; r.x += offsetX; r.y += offsetY; l = line.getLeftVector().clone(); r = line.getRightVector().clone(); l.x += offsetX; l.y += offsetY; r.x += offsetX; r.y += offsetY; ctx.save(); let normals = Vector2.normals(l, r); normals[0].normalize(); normals[1].normalize(); let isRightChiralCenter = line.getRightChiral(); let start = l; let end = r; if (isRightChiralCenter) { start = r; end = l; } let t2 = Vector2.add(start, Vector2.multiplyScalar(normals[0], this.halfBondThickness)); let u = Vector2.add(end, Vector2.multiplyScalar(normals[0], 1.5 + this.halfBondThickness)); let v = Vector2.add(end, Vector2.multiplyScalar(normals[1], 1.5 + this.halfBondThickness)); let w = Vector2.add(start, Vector2.multiplyScalar(normals[1], this.halfBondThickness)); ctx.beginPath(); ctx.moveTo(t2.x, t2.y); ctx.lineTo(u.x, u.y); ctx.lineTo(v.x, v.y); ctx.lineTo(w.x, w.y); let gradient = this.ctx.createRadialGradient(r.x, r.y, this.opts.bondLength, r.x, r.y, 0); gradient.addColorStop(0.4, this.themeManager.getColor(line.getLeftElement()) || this.themeManager.getColor("C")); gradient.addColorStop(0.6, this.themeManager.getColor(line.getRightElement()) || this.themeManager.getColor("C")); ctx.fillStyle = gradient; ctx.fill(); ctx.restore(); } /** * Draw a dashed wedge on the canvas. * * @param {Line} line A line. */ drawDashedWedge(line) { if (isNaN(line.from.x) || isNaN(line.from.y) || isNaN(line.to.x) || isNaN(line.to.y)) { return; } let ctx = this.ctx; let offsetX = this.offsetX; let offsetY = this.offsetY; let l = line.getLeftVector().clone(); let r = line.getRightVector().clone(); l.x += offsetX; l.y += offsetY; r.x += offsetX; r.y += offsetY; ctx.save(); let normals = Vector2.normals(l, r); normals[0].normalize(); normals[1].normalize(); let isRightChiralCenter = line.getRightChiral(); let start; let end; let sStart; let sEnd; let shortLine = line.clone(); if (isRightChiralCenter) { start = r; end = l; shortLine.shortenRight(1); sStart = shortLine.getRightVector().clone(); sEnd = shortLine.getLeftVector().clone(); } else { start = l; end = r; shortLine.shortenLeft(1); sStart = shortLine.getLeftVector().clone(); sEnd = shortLine.getRightVector().clone(); } sStart.x += offsetX; sStart.y += offsetY; sEnd.x += offsetX; sEnd.y += offsetY; let dir2 = Vector2.subtract(end, start).normalize(); ctx.strokeStyle = this.themeManager.getColor("C"); ctx.lineCap = "round"; ctx.lineWidth = this.opts.bondThickness; ctx.beginPath(); let length = line.getLength(); let step = 1.25 / (length / (this.opts.bondThickness * 3)); let changed = false; for (var t2 = 0; t2 < 1; t2 += step) { let to = Vector2.multiplyScalar(dir2, t2 * length); let startDash = Vector2.add(start, to); let width = 1.5 * t2; let dashOffset = Vector2.multiplyScalar(normals[0], width); if (!changed && t2 > 0.5) { ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = this.themeManager.getColor(line.getRightElement()) || this.themeManager.getColor("C"); changed = true; } startDash.subtract(dashOffset); ctx.moveTo(startDash.x, startDash.y); startDash.add(Vector2.multiplyScalar(dashOffset, 2)); ctx.lineTo(startDash.x, startDash.y); } ctx.stroke(); ctx.restore(); } /** * Draws a debug text message at a given position * * @param {Number} x The x coordinate. * @param {Number} y The y coordinate. * @param {String} text The debug text. */ drawDebugText(x, y, text) { let ctx = this.ctx; ctx.save(); ctx.font = "5px Droid Sans, sans-serif"; ctx.textAlign = "start"; ctx.textBaseline = "top"; ctx.fillStyle = "#ff0000"; ctx.fillText(text, x + this.offsetX, y + this.offsetY); ctx.restore(); } /** * Draw a ball to the canvas. * * @param {Number} x The x position of the text. * @param {Number} y The y position of the text. * @param {String} elementName The name of the element (single-letter). */ drawBall(x, y, elementName) { let ctx = this.ctx; ctx.save(); ctx.beginPath(); ctx.arc(x + this.offsetX, y + this.offsetY, this.opts.bondLength / 4.5, 0, MathHelper.twoPI, false); ctx.fillStyle = this.themeManager.getColor(elementName); ctx.fill(); ctx.restore(); } /** * Draw a point to the canvas. * * @param {Number} x The x position of the point. * @param {Number} y The y position of the point. * @param {String} elementName The name of the element (single-letter). */ drawPoint(x, y, elementName) { let ctx = this.ctx; let offsetX = this.offsetX; let offsetY = this.offsetY; ctx.save(); ctx.globalCompositeOperation = "destination-out"; ctx.beginPath(); ctx.arc(x + offsetX, y + offsetY, 1.5, 0, MathHelper.twoPI, true); ctx.closePath(); ctx.fill(); ctx.globalCompositeOperation = "source-over"; ctx.beginPath(); ctx.arc(x + this.offsetX, y + this.offsetY, 0.75, 0, MathHelper.twoPI, false); ctx.fillStyle = this.themeManager.getColor(elementName); ctx.fill(); ctx.restore(); } /** * Draw a text to the canvas. * * @param {Number} x The x position of the text. * @param {Number} y The y position of the text. * @param {String} elementName The name of the element (single-letter). * @param {Number} hydrogens The number of hydrogen atoms. * @param {String} direction The direction of the text in relation to the associated vertex. * @param {Boolean} isTerminal A boolean indicating whether or not the vertex is terminal. * @param {Number} charge The charge of the atom. * @param {Number} isotope The isotope number. * @param {Number} vertexCount The number of vertices in the molecular graph. * @param {Object} attachedPseudoElement A map with containing information for pseudo elements or concatinated elements. The key is comprised of the element symbol and the hydrogen count. * @param {String} attachedPseudoElement.element The element symbol. * @param {Number} attachedPseudoElement.count The number of occurences that match the key. * @param {Number} attachedPseudoElement.hyrogenCount The number of hydrogens attached to each atom matching the key. */ drawText(x, y, elementName, hydrogens, direction, isTerminal, charge, isotope, vertexCount, attachedPseudoElement = {}) { let ctx = this.ctx; let offsetX = this.offsetX; let offsetY = this.offsetY; ctx.save(); ctx.textAlign = "start"; ctx.textBaseline = "alphabetic"; let pseudoElementHandled = false; let chargeText = ""; let chargeWidth = 0; if (charge) { chargeText = getChargeText(charge); ctx.font = this.fontSmall; chargeWidth = ctx.measureText(chargeText).width; } let isotopeText = "0"; let isotopeWidth = 0; if (isotope > 0) { isotopeText = isotope.toString(); ctx.font = this.fontSmall; isotopeWidth = ctx.measureText(isotopeText).width; } if (charge === 1 && elementName === "N" && attachedPseudoElement.hasOwnProperty("0O") && attachedPseudoElement.hasOwnProperty("0O-1")) { attachedPseudoElement = { "0O": { element: "O", count: 2, hydrogenCount: 0, previousElement: "C", charge: "" } }; charge = 0; } ctx.font = this.fontLarge; ctx.fillStyle = this.themeManager.getColor("BACKGROUND"); let dim = ctx.measureText(elementName); dim.totalWidth = dim.width + chargeWidth; dim.height = parseInt(this.fontLarge, 10); let r = dim.width > this.opts.fontSizeLarge ? dim.width : this.opts.fontSizeLarge; r /= 1.5; ctx.globalCompositeOperation = "destination-out"; ctx.beginPath(); ctx.arc(x + offsetX, y + offsetY, r, 0, MathHelper.twoPI, true); ctx.closePath(); ctx.fill(); ctx.globalCompositeOperation = "source-over"; let cursorPos = -dim.width / 2; let cursorPosLeft = -dim.width / 2; ctx.fillStyle = this.themeManager.getColor(elementName); ctx.fillText(elementName, x + offsetX + cursorPos, y + this.opts.halfFontSizeLarge + offsetY); cursorPos += dim.width; if (charge) { ctx.font = this.fontSmall; ctx.fillText(chargeText, x + offsetX + cursorPos, y - this.opts.fifthFontSizeSmall + offsetY); cursorPos += chargeWidth; } if (isotope > 0) { ctx.font = this.fontSmall; ctx.fillText(isotopeText, x + offsetX + cursorPosLeft - isotopeWidth, y - this.opts.fifthFontSizeSmall + offsetY); cursorPosLeft -= isotopeWidth; } ctx.font = this.fontLarge; let hydrogenWidth = 0; let hydrogenCountWidth = 0; if (hydrogens === 1) { let hx = x + offsetX; let hy = y + offsetY + this.opts.halfFontSizeLarge; hydrogenWidth = this.hydrogenWidth; cursorPosLeft -= hydrogenWidth; if (direction === "left") { hx += cursorPosLeft; } else if (direction === "right") { hx += cursorPos; } else if (direction === "up" && isTerminal) { hx += cursorPos; } else if (direction === "down" && isTerminal) { hx += cursorPos; } else if (direction === "up" && !isTerminal) { hy -= this.opts.fontSizeLarge + this.opts.quarterFontSizeLarge; hx -= this.halfHydrogenWidth; } else if (direction === "down" && !isTerminal) { hy += this.opts.fontSizeLarge + this.opts.quarterFontSizeLarge; hx -= this.halfHydrogenWidth; } ctx.fillText("H", hx, hy); cursorPos += hydrogenWidth; } else if (hydrogens > 1) { let hx = x + offsetX; let hy = y + offsetY + this.opts.halfFontSizeLarge; hydrogenWidth = this.hydrogenWidth; ctx.font = this.fontSmall; hydrogenCountWidth = ctx.measureText(hydrogens).width; cursorPosLeft -= hydrogenWidth + hydrogenCountWidth; if (direction === "left") { hx += cursorPosLeft; } else if (direction === "right") { hx += cursorPos; } else if (direction === "up" && isTerminal) { hx += cursorPos; } else if (direction === "down" && isTerminal) { hx += cursorPos; } else if (direction === "up" && !isTerminal) { hy -= this.opts.fontSizeLarge + this.opts.quarterFontSizeLarge; hx -= this.halfHydrogenWidth; } else if (direction === "down" && !isTerminal) { hy += this.opts.fontSizeLarge + this.opts.quarterFontSizeLarge; hx -= this.halfHydrogenWidth; } ctx.font = this.fontLarge; ctx.fillText("H", hx, hy); ctx.font = this.fontSmall; ctx.fillText(hydrogens, hx + this.halfHydrogenWidth + hydrogenCountWidth, hy + this.opts.fifthFontSizeSmall); cursorPos += hydrogenWidth + this.halfHydrogenWidth + hydrogenCountWidth; } if (pseudoElementHandled) { ctx.restore(); return; } for (let key in attachedPseudoElement) { if (!attachedPseudoElement.hasOwnProperty(key)) { continue; } let openParenthesisWidth = 0; let closeParenthesisWidth = 0; let element = attachedPseudoElement[key].element; let elementCount = attachedPseudoElement[key].count; let hydrogenCount = attachedPseudoElement[key].hydrogenCount; let elementCharge = attachedPseudoElement[key].charge; ctx.font = this.fontLarge; if (elementCount > 1 && hydrogenCount > 0) { openParenthesisWidth = ctx.measureText("(").width; closeParenthesisWidth = ctx.measureText(")").width; } let elementWidth = ctx.measureText(element).width; let elementCountWidth = 0; let elementChargeText = ""; let elementChargeWidth = 0; hydrogenWidth = 0; if (hydrogenCount > 0) { hydrogenWidth = this.hydrogenWidth; } ctx.font = this.fontSmall; if (elementCount > 1) { elementCountWidth = ctx.measureText(elementCount).width; } if (elementCharge !== 0) { elementChargeText = getChargeText(elementCharge); elementChargeWidth = ctx.measureText(elementChargeText).width; } hydrogenCountWidth = 0; if (hydrogenCount > 1) { hydrogenCountWidth = ctx.measureText(hydrogenCount).width; } ctx.font = this.fontLarge; let hx = x + offsetX; let hy = y + offsetY + this.opts.halfFontSizeLarge; ctx.fillStyle = this.themeManager.getColor(element); if (elementCount > 0) { cursorPosLeft -= elementCountWidth; } if (elementCount > 1 && hydrogenCount > 0) { if (direction === "left") { cursorPosLeft -= closeParenthesisWidth; ctx.fillText(")", hx + cursorPosLeft, hy); } else { ctx.fillText("(", hx + cursorPos, hy); cursorPos += openParenthesisWidth; } } if (direction === "left") { cursorPosLeft -= elementWidth; ctx.fillText(element, hx + cursorPosLeft, hy); } else { ctx.fillText(element, hx + cursorPos, hy); cursorPos += elementWidth; } if (hydrogenCount > 0) { if (direction === "left") { cursorPosLeft -= hydrogenWidth + hydrogenCountWidth; ctx.fillText("H", hx + cursorPosLeft, hy); if (hydrogenCount > 1) { ctx.font = this.fontSmall; ctx.fillText(hydrogenCount, hx + cursorPosLeft + hydrogenWidth, hy + this.opts.fifthFontSizeSmall); } } else { ctx.fillText("H", hx + cursorPos, hy); cursorPos += hydrogenWidth; if (hydrogenCount > 1) { ctx.font = this.fontSmall; ctx.fillText(hydrogenCount, hx + cursorPos, hy + this.opts.fifthFontSizeSmall); cursorPos += hydrogenCountWidth; } } } ctx.font = this.fontLarge; if (elementCount > 1 && hydrogenCount > 0) { if (direction === "left") { cursorPosLeft -= openParenthesisWidth; ctx.fillText("(", hx + cursorPosLeft, hy); } else { ctx.fillText(")", hx + cursorPos, hy); cursorPos += closeParenthesisWidth; } } ctx.font = this.fontSmall; if (elementCount > 1) { if (direction === "left") { ctx.fillText(elementCount, hx + cursorPosLeft + openParenthesisWidth + closeParenthesisWidth + hydrogenWidth + hydrogenCountWidth + elementWidth, hy + this.opts.fifthFontSizeSmall); } else { ctx.fillText(elementCount, hx + cursorPos, hy + this.opts.fifthFontSizeSmall); cursorPos += elementCountWidth; } } if (elementCharge !== 0) { if (direction === "left") { ctx.fillText(elementChargeText, hx + cursorPosLeft + openParenthesisWidth + closeParenthesisWidth + hydrogenWidth + hydrogenCountWidth + elementWidth, y - this.opts.fifthFontSizeSmall + offsetY); } else { ctx.fillText(elementChargeText, hx + cursorPos, y - this.opts.fifthFontSizeSmall + offsetY); cursorPos += elementChargeWidth; } } } ctx.restore(); } /** * Translate the integer indicating the charge to the appropriate text. * @param {Number} charge The integer indicating the charge. * @returns {String} A string representing a charge. */ getChargeText(charge) { if (charge === 1) { return "+"; } else if (charge === 2) { return "2+"; } else if (charge === -1) { return "-"; } else if (charge === -2) { return "2-"; } else { return ""; } } /** * Draws a dubug dot at a given coordinate and adds text. * * @param {Number} x The x coordinate. * @param {Number} y The y coordindate. * @param {String} [debugText=''] A string. * @param {String} [color='#f00'] A color in hex form. */ drawDebugPoint(x, y, debugText = "", color = "#f00") { this.drawCircle(x, y, 2, color, true, true, debugText); } /** * Draws a ring inside a provided ring, indicating aromaticity. * * @param {Ring} ring A ring. */ drawAromaticityRing(ring) { let ctx = this.ctx; let radius = MathHelper.apothemFromSideLength(this.opts.bondLength, ring.getSize()); ctx.save(); ctx.strokeStyle = this.themeManager.getColor("C"); ctx.lineWidth = this.opts.bondThickness; ctx.beginPath(); ctx.arc( ring.center.x + this.offsetX, ring.center.y + this.offsetY, radius - this.opts.bondSpacing, 0, Math.PI * 2, true ); ctx.closePath(); ctx.stroke(); ctx.restore(); } /** * Clear the canvas. * */ clear() { this.ctx.clearRect(0, 0, this.canvas.offsetWidth, this.canvas.offsetHeight); } }; module2.exports = CanvasWrapper; } }); // node_modules/smiles-drawer/src/Graph.js var require_Graph = __commonJS({ "node_modules/smiles-drawer/src/Graph.js"(exports, module2) { var MathHelper = require_MathHelper(); var Vector2 = require_Vector2(); var Vertex = require_Vertex(); var Edge = require_Edge(); var Ring = require_Ring(); var Atom = require_Atom(); var Graph = class { /** * The constructor of the class Graph. * * @param {Object} parseTree A SMILES parse tree. * @param {Boolean} [isomeric=false] A boolean specifying whether or not the SMILES is isomeric. */ constructor(parseTree, isomeric = false) { this.vertices = Array(); this.edges = Array(); this.atomIdxToVertexId = Array(); this.vertexIdsToEdgeId = {}; this.isomeric = isomeric; this._atomIdx = 0; this._time = 0; this._init(parseTree); } /** * PRIVATE FUNCTION. Initializing the graph from the parse tree. * * @param {Object} node The current node in the parse tree. * @param {?Number} parentVertexId=null The id of the previous vertex. * @param {Boolean} isBranch=false Whether or not the bond leading to this vertex is a branch bond. Branches are represented by parentheses in smiles (e.g. CC(O)C). */ _init(node, order = 0, parentVertexId = null, isBranch = false) { const element = node.atom.element ? node.atom.element : node.atom; let atom = new Atom(element, node.bond); if (element !== "H" || !node.hasNext && parentVertexId === null) { atom.idx = this._atomIdx; this._atomIdx++; } atom.branchBond = node.branchBond; atom.ringbonds = node.ringbonds; atom.bracket = node.atom.element ? node.atom : null; atom.class = node.atom.class; let vertex = new Vertex(atom); let parentVertex = this.vertices[parentVertexId]; this.addVertex(vertex); if (atom.idx !== null) { this.atomIdxToVertexId.push(vertex.id); } if (parentVertexId !== null) { vertex.setParentVertexId(parentVertexId); vertex.value.addNeighbouringElement(parentVertex.value.element); parentVertex.addChild(vertex.id); parentVertex.value.addNeighbouringElement(atom.element); parentVertex.spanningTreeChildren.push(vertex.id); let edge = new Edge(parentVertexId, vertex.id, 1); let vertexId = null; if (isBranch) { edge.setBondType(vertex.value.branchBond || "-"); vertexId = vertex.id; edge.setBondType(vertex.value.branchBond || "-"); vertexId = vertex.id; } else { edge.setBondType(parentVertex.value.bondType || "-"); vertexId = parentVertex.id; } let edgeId = this.addEdge(edge); } let offset = node.ringbondCount + 1; if (atom.bracket) { offset += atom.bracket.hcount; } let stereoHydrogens = 0; if (atom.bracket && atom.bracket.chirality) { atom.isStereoCenter = true; stereoHydrogens = atom.bracket.hcount; for (var i = 0; i < stereoHydrogens; i++) { this._init({ atom: "H", isBracket: "false", branches: Array(), branchCount: 0, ringbonds: Array(), ringbondCount: false, next: null, hasNext: false, bond: "-" }, i, vertex.id, true); } } for (var i = 0; i < node.branchCount; i++) { this._init(node.branches[i], i + offset, vertex.id, true); } if (node.hasNext) { this._init(node.next, node.branchCount + offset, vertex.id); } } /** * Clears all the elements in this graph (edges and vertices). */ clear() { this.vertices = Array(); this.edges = Array(); this.vertexIdsToEdgeId = {}; } /** * Add a vertex to the graph. * * @param {Vertex} vertex A new vertex. * @returns {Number} The vertex id of the new vertex. */ addVertex(vertex) { vertex.id = this.vertices.length; this.vertices.push(vertex); return vertex.id; } /** * Add an edge to the graph. * * @param {Edge} edge A new edge. * @returns {Number} The edge id of the new edge. */ addEdge(edge) { let source = this.vertices[edge.sourceId]; let target = this.vertices[edge.targetId]; edge.id = this.edges.length; this.edges.push(edge); this.vertexIdsToEdgeId[edge.sourceId + "_" + edge.targetId] = edge.id; this.vertexIdsToEdgeId[edge.targetId + "_" + edge.sourceId] = edge.id; edge.isPartOfAromaticRing = source.value.isPartOfAromaticRing && target.value.isPartOfAromaticRing; source.value.bondCount += edge.weight; target.value.bondCount += edge.weight; source.edges.push(edge.id); target.edges.push(edge.id); return edge.id; } /** * Returns the edge between two given vertices. * * @param {Number} vertexIdA A vertex id. * @param {Number} vertexIdB A vertex id. * @returns {(Edge|null)} The edge or, if no edge can be found, null. */ getEdge(vertexIdA, vertexIdB) { let edgeId = this.vertexIdsToEdgeId[vertexIdA + "_" + vertexIdB]; return edgeId === void 0 ? null : this.edges[edgeId]; } /** * Returns the ids of edges connected to a vertex. * * @param {Number} vertexId A vertex id. * @returns {Number[]} An array containing the ids of edges connected to the vertex. */ getEdges(vertexId) { let edgeIds = Array(); let vertex = this.vertices[vertexId]; for (var i = 0; i < vertex.neighbours.length; i++) { edgeIds.push(this.vertexIdsToEdgeId[vertexId + "_" + vertex.neighbours[i]]); } return edgeIds; } /** * Check whether or not two vertices are connected by an edge. * * @param {Number} vertexIdA A vertex id. * @param {Number} vertexIdB A vertex id. * @returns {Boolean} A boolean indicating whether or not two vertices are connected by an edge. */ hasEdge(vertexIdA, vertexIdB) { return this.vertexIdsToEdgeId[vertexIdA + "_" + vertexIdB] !== void 0; } /** * Returns an array containing the vertex ids of this graph. * * @returns {Number[]} An array containing all vertex ids of this graph. */ getVertexList() { let arr = [this.vertices.length]; for (var i = 0; i < this.vertices.length; i++) { arr[i] = this.vertices[i].id; } return arr; } /** * Returns an array containing source, target arrays of this graphs edges. * * @returns {Array[]} An array containing source, target arrays of this graphs edges. Example: [ [ 2, 5 ], [ 6, 9 ] ]. */ getEdgeList() { let arr = Array(this.edges.length); for (var i = 0; i < this.edges.length; i++) { arr[i] = [this.edges[i].sourceId, this.edges[i].targetId]; } return arr; } /** * Get the adjacency matrix of the graph. * * @returns {Array[]} The adjancency matrix of the molecular graph. */ getAdjacencyMatrix() { let length = this.vertices.length; let adjacencyMatrix = Array(length); for (var i = 0; i < length; i++) { adjacencyMatrix[i] = new Array(length); adjacencyMatrix[i].fill(0); } for (var i = 0; i < this.edges.length; i++) { let edge = this.edges[i]; adjacencyMatrix[edge.sourceId][edge.targetId] = 1; adjacencyMatrix[edge.targetId][edge.sourceId] = 1; } return adjacencyMatrix; } /** * Get the adjacency matrix of the graph with all bridges removed (thus the components). Thus the remaining vertices are all part of ring systems. * * @returns {Array[]} The adjancency matrix of the molecular graph with all bridges removed. */ getComponentsAdjacencyMatrix() { let length = this.vertices.length; let adjacencyMatrix = Array(length); let bridges = this.getBridges(); for (var i = 0; i < length; i++) { adjacencyMatrix[i] = new Array(length); adjacencyMatrix[i].fill(0); } for (var i = 0; i < this.edges.length; i++) { let edge = this.edges[i]; adjacencyMatrix[edge.sourceId][edge.targetId] = 1; adjacencyMatrix[edge.targetId][edge.sourceId] = 1; } for (var i = 0; i < bridges.length; i++) { adjacencyMatrix[bridges[i][0]][bridges[i][1]] = 0; adjacencyMatrix[bridges[i][1]][bridges[i][0]] = 0; } return adjacencyMatrix; } /** * Get the adjacency matrix of a subgraph. * * @param {Number[]} vertexIds An array containing the vertex ids contained within the subgraph. * @returns {Array[]} The adjancency matrix of the subgraph. */ getSubgraphAdjacencyMatrix(vertexIds) { let length = vertexIds.length; let adjacencyMatrix = Array(length); for (var i = 0; i < length; i++) { adjacencyMatrix[i] = new Array(length); adjacencyMatrix[i].fill(0); for (var j = 0; j < length; j++) { if (i === j) { continue; } if (this.hasEdge(vertexIds[i], vertexIds[j])) { adjacencyMatrix[i][j] = 1; } } } return adjacencyMatrix; } /** * Get the distance matrix of the graph. * * @returns {Array[]} The distance matrix of the graph. */ getDistanceMatrix() { let length = this.vertices.length; let adja = this.getAdjacencyMatrix(); let dist = Array(length); for (var i = 0; i < length; i++) { dist[i] = Array(length); dist[i].fill(Infinity); } for (var i = 0; i < length; i++) { for (var j = 0; j < length; j++) { if (adja[i][j] === 1) { dist[i][j] = 1; } } } for (var k = 0; k < length; k++) { for (var i = 0; i < length; i++) { for (var j = 0; j < length; j++) { if (dist[i][j] > dist[i][k] + dist[k][j]) { dist[i][j] = dist[i][k] + dist[k][j]; } } } } return dist; } /** * Get the distance matrix of a subgraph. * * @param {Number[]} vertexIds An array containing the vertex ids contained within the subgraph. * @returns {Array[]} The distance matrix of the subgraph. */ getSubgraphDistanceMatrix(vertexIds) { let length = vertexIds.length; let adja = this.getSubgraphAdjacencyMatrix(vertexIds); let dist = Array(length); for (var i = 0; i < length; i++) { dist[i] = Array(length); dist[i].fill(Infinity); } for (var i = 0; i < length; i++) { for (var j = 0; j < length; j++) { if (adja[i][j] === 1) { dist[i][j] = 1; } } } for (var k = 0; k < length; k++) { for (var i = 0; i < length; i++) { for (var j = 0; j < length; j++) { if (dist[i][j] > dist[i][k] + dist[k][j]) { dist[i][j] = dist[i][k] + dist[k][j]; } } } } return dist; } /** * Get the adjacency list of the graph. * * @returns {Array[]} The adjancency list of the graph. */ getAdjacencyList() { let length = this.vertices.length; let adjacencyList = Array(length); for (var i = 0; i < length; i++) { adjacencyList[i] = []; for (var j = 0; j < length; j++) { if (i === j) { continue; } if (this.hasEdge(this.vertices[i].id, this.vertices[j].id)) { adjacencyList[i].push(j); } } } return adjacencyList; } /** * Get the adjacency list of a subgraph. * * @param {Number[]} vertexIds An array containing the vertex ids contained within the subgraph. * @returns {Array[]} The adjancency list of the subgraph. */ getSubgraphAdjacencyList(vertexIds) { let length = vertexIds.length; let adjacencyList = Array(length); for (var i = 0; i < length; i++) { adjacencyList[i] = Array(); for (var j = 0; j < length; j++) { if (i === j) { continue; } if (this.hasEdge(vertexIds[i], vertexIds[j])) { adjacencyList[i].push(j); } } } return adjacencyList; } /** * Returns an array containing the edge ids of bridges. A bridge splits the graph into multiple components when removed. * * @returns {Number[]} An array containing the edge ids of the bridges. */ getBridges() { let length = this.vertices.length; let visited = new Array(length); let disc = new Array(length); let low = new Array(length); let parent = new Array(length); let adj = this.getAdjacencyList(); let outBridges = Array(); visited.fill(false); parent.fill(null); this._time = 0; for (var i = 0; i < length; i++) { if (!visited[i]) { this._bridgeDfs(i, visited, disc, low, parent, adj, outBridges); } } return outBridges; } /** * Traverses the graph in breadth-first order. * * @param {Number} startVertexId The id of the starting vertex. * @param {Function} callback The callback function to be called on every vertex. */ traverseBF(startVertexId, callback) { let length = this.vertices.length; let visited = new Array(length); visited.fill(false); var queue = [startVertexId]; while (queue.length > 0) { let u = queue.shift(); let vertex = this.vertices[u]; callback(vertex); for (var i = 0; i < vertex.neighbours.length; i++) { let v = vertex.neighbours[i]; if (!visited[v]) { visited[v] = true; queue.push(v); } } } } /** * Get the depth of a subtree in the direction opposite to the vertex specified as the parent vertex. * * @param {Number} vertexId A vertex id. * @param {Number} parentVertexId The id of a neighbouring vertex. * @returns {Number} The depth of the sub-tree. */ getTreeDepth(vertexId, parentVertexId) { if (vertexId === null || parentVertexId === null) { return 0; } let neighbours = this.vertices[vertexId].getSpanningTreeNeighbours(parentVertexId); let max = 0; for (var i = 0; i < neighbours.length; i++) { let childId = neighbours[i]; let d = this.getTreeDepth(childId, vertexId); if (d > max) { max = d; } } return max + 1; } /** * Traverse a sub-tree in the graph. * * @param {Number} vertexId A vertex id. * @param {Number} parentVertexId A neighbouring vertex. * @param {Function} callback The callback function that is called with each visited as an argument. * @param {Number} [maxDepth=999999] The maximum depth of the recursion. * @param {Boolean} [ignoreFirst=false] Whether or not to ignore the starting vertex supplied as vertexId in the callback. * @param {Number} [depth=1] The current depth in the tree. * @param {Uint8Array} [visited=null] An array holding a flag on whether or not a node has been visited. */ traverseTree(vertexId, parentVertexId, callback, maxDepth = 999999, ignoreFirst = false, depth = 1, visited = null) { if (visited === null) { visited = new Uint8Array(this.vertices.length); } if (depth > maxDepth + 1 || visited[vertexId] === 1) { return; } visited[vertexId] = 1; let vertex = this.vertices[vertexId]; let neighbours = vertex.getNeighbours(parentVertexId); if (!ignoreFirst || depth > 1) { callback(vertex); } for (var i = 0; i < neighbours.length; i++) { this.traverseTree(neighbours[i], vertexId, callback, maxDepth, ignoreFirst, depth + 1, visited); } } /** * Positiones the (sub)graph using Kamada and Kawais algorithm for drawing general undirected graphs. https://pdfs.semanticscholar.org/b8d3/bca50ccc573c5cb99f7d201e8acce6618f04.pdf * There are undocumented layout parameters. They are undocumented for a reason, so be very careful. * * @param {Number[]} vertexIds An array containing vertexIds to be placed using the force based layout. * @param {Vector2} center The center of the layout. * @param {Number} startVertexId A vertex id. Should be the starting vertex - e.g. the first to be positioned and connected to a previously place vertex. * @param {Ring} ring The bridged ring associated with this force-based layout. */ kkLayout(vertexIds, center, startVertexId, ring, bondLength, threshold = 0.1, innerThreshold = 0.1, maxIteration = 2e3, maxInnerIteration = 50, maxEnergy = 1e9) { let edgeStrength = bondLength; var i = vertexIds.length; while (i--) { let vertex = this.vertices[vertexIds[i]]; var j = vertex.neighbours.length; } let matDist = this.getSubgraphDistanceMatrix(vertexIds); let length = vertexIds.length; let radius = MathHelper.polyCircumradius(500, length); let angle = MathHelper.centralAngle(length); let a = 0; let arrPositionX = new Float32Array(length); let arrPositionY = new Float32Array(length); let arrPositioned = Array(length); i = length; while (i--) { let vertex = this.vertices[vertexIds[i]]; if (!vertex.positioned) { arrPositionX[i] = center.x + Math.cos(a) * radius; arrPositionY[i] = center.y + Math.sin(a) * radius; } else { arrPositionX[i] = vertex.position.x; arrPositionY[i] = vertex.position.y; } arrPositioned[i] = vertex.positioned; a += angle; } let matLength = Array(length); i = length; while (i--) { matLength[i] = new Array(length); var j = length; while (j--) { matLength[i][j] = bondLength * matDist[i][j]; } } let matStrength = Array(length); i = length; while (i--) { matStrength[i] = Array(length); var j = length; while (j--) { matStrength[i][j] = edgeStrength * Math.pow(matDist[i][j], -2); } } let matEnergy = Array(length); let arrEnergySumX = new Float32Array(length); let arrEnergySumY = new Float32Array(length); i = length; while (i--) { matEnergy[i] = Array(length); } i = length; let ux, uy, dEx, dEy, vx, vy, denom; while (i--) { ux = arrPositionX[i]; uy = arrPositionY[i]; dEx = 0; dEy = 0; let j2 = length; while (j2--) { if (i === j2) { continue; } vx = arrPositionX[j2]; vy = arrPositionY[j2]; denom = 1 / Math.sqrt((ux - vx) * (ux - vx) + (uy - vy) * (uy - vy)); matEnergy[i][j2] = [ matStrength[i][j2] * (ux - vx - matLength[i][j2] * (ux - vx) * denom), matStrength[i][j2] * (uy - vy - matLength[i][j2] * (uy - vy) * denom) ]; matEnergy[j2][i] = matEnergy[i][j2]; dEx += matEnergy[i][j2][0]; dEy += matEnergy[i][j2][1]; } arrEnergySumX[i] = dEx; arrEnergySumY[i] = dEy; } let energy = function(index) { return [arrEnergySumX[index] * arrEnergySumX[index] + arrEnergySumY[index] * arrEnergySumY[index], arrEnergySumX[index], arrEnergySumY[index]]; }; let highestEnergy = function() { let maxEnergy2 = 0; let maxEnergyId2 = 0; let maxDEX = 0; let maxDEY = 0; i = length; while (i--) { let [delta2, dEX2, dEY2] = energy(i); if (delta2 > maxEnergy2 && arrPositioned[i] === false) { maxEnergy2 = delta2; maxEnergyId2 = i; maxDEX = dEX2; maxDEY = dEY2; } } return [maxEnergyId2, maxEnergy2, maxDEX, maxDEY]; }; let update = function(index, dEX2, dEY2) { let dxx = 0; let dyy = 0; let dxy = 0; let ux2 = arrPositionX[index]; let uy2 = arrPositionY[index]; let arrL = matLength[index]; let arrK = matStrength[index]; i = length; while (i--) { if (i === index) { continue; } let vx3 = arrPositionX[i]; let vy3 = arrPositionY[i]; let l = arrL[i]; let k = arrK[i]; let m = (ux2 - vx3) * (ux2 - vx3); let denom3 = 1 / Math.pow(m + (uy2 - vy3) * (uy2 - vy3), 1.5); dxx += k * (1 - l * (uy2 - vy3) * (uy2 - vy3) * denom3); dyy += k * (1 - l * m * denom3); dxy += k * (l * (ux2 - vx3) * (uy2 - vy3) * denom3); } if (dxx === 0) { dxx = 0.1; } if (dyy === 0) { dyy = 0.1; } if (dxy === 0) { dxy = 0.1; } let dy = dEX2 / dxx + dEY2 / dxy; dy /= dxy / dxx - dyy / dxy; let dx = -(dxy * dy + dEX2) / dxx; arrPositionX[index] += dx; arrPositionY[index] += dy; let arrE = matEnergy[index]; dEX2 = 0; dEY2 = 0; ux2 = arrPositionX[index]; uy2 = arrPositionY[index]; let vx2, vy2, prevEx, prevEy, denom2; i = length; while (i--) { if (index === i) { continue; } vx2 = arrPositionX[i]; vy2 = arrPositionY[i]; prevEx = arrE[i][0]; prevEy = arrE[i][1]; denom2 = 1 / Math.sqrt((ux2 - vx2) * (ux2 - vx2) + (uy2 - vy2) * (uy2 - vy2)); dx = arrK[i] * (ux2 - vx2 - arrL[i] * (ux2 - vx2) * denom2); dy = arrK[i] * (uy2 - vy2 - arrL[i] * (uy2 - vy2) * denom2); arrE[i] = [dx, dy]; dEX2 += dx; dEY2 += dy; arrEnergySumX[i] += dx - prevEx; arrEnergySumY[i] += dy - prevEy; } arrEnergySumX[index] = dEX2; arrEnergySumY[index] = dEY2; }; let maxEnergyId = 0; let dEX = 0; let dEY = 0; let delta = 0; let iteration = 0; let innerIteration = 0; while (maxEnergy > threshold && maxIteration > iteration) { iteration++; [maxEnergyId, maxEnergy, dEX, dEY] = highestEnergy(); delta = maxEnergy; innerIteration = 0; while (delta > innerThreshold && maxInnerIteration > innerIteration) { innerIteration++; update(maxEnergyId, dEX, dEY); [delta, dEX, dEY] = energy(maxEnergyId); } } i = length; while (i--) { let index = vertexIds[i]; let vertex = this.vertices[index]; vertex.position.x = arrPositionX[i]; vertex.position.y = arrPositionY[i]; vertex.positioned = true; vertex.forcePositioned = true; } } /** * PRIVATE FUNCTION used by getBridges(). */ _bridgeDfs(u, visited, disc, low, parent, adj, outBridges) { visited[u] = true; disc[u] = low[u] = ++this._time; for (var i = 0; i < adj[u].length; i++) { let v = adj[u][i]; if (!visited[v]) { parent[v] = u; this._bridgeDfs(v, visited, disc, low, parent, adj, outBridges); low[u] = Math.min(low[u], low[v]); if (low[v] > disc[u]) { outBridges.push([u, v]); } } else if (v !== parent[u]) { low[u] = Math.min(low[u], disc[v]); } } } /** * Returns the connected components of the graph. * * @param {Array[]} adjacencyMatrix An adjacency matrix. * @returns {Set[]} Connected components as sets. */ static getConnectedComponents(adjacencyMatrix) { let length = adjacencyMatrix.length; let visited = new Array(length); let components = new Array(); let count = 0; visited.fill(false); for (var u = 0; u < length; u++) { if (!visited[u]) { let component = Array(); visited[u] = true; component.push(u); count++; Graph._ccGetDfs(u, visited, adjacencyMatrix, component); if (component.length > 1) { components.push(component); } } } return components; } /** * Returns the number of connected components for the graph. * * @param {Array[]} adjacencyMatrix An adjacency matrix. * @returns {Number} The number of connected components of the supplied graph. */ static getConnectedComponentCount(adjacencyMatrix) { let length = adjacencyMatrix.length; let visited = new Array(length); let count = 0; visited.fill(false); for (var u = 0; u < length; u++) { if (!visited[u]) { visited[u] = true; count++; Graph._ccCountDfs(u, visited, adjacencyMatrix); } } return count; } /** * PRIVATE FUNCTION used by getConnectedComponentCount(). */ static _ccCountDfs(u, visited, adjacencyMatrix) { for (var v = 0; v < adjacencyMatrix[u].length; v++) { let c = adjacencyMatrix[u][v]; if (!c || visited[v] || u === v) { continue; } visited[v] = true; Graph._ccCountDfs(v, visited, adjacencyMatrix); } } /** * PRIVATE FUNCTION used by getConnectedComponents(). */ static _ccGetDfs(u, visited, adjacencyMatrix, component) { for (var v = 0; v < adjacencyMatrix[u].length; v++) { let c = adjacencyMatrix[u][v]; if (!c || visited[v] || u === v) { continue; } visited[v] = true; component.push(v); Graph._ccGetDfs(v, visited, adjacencyMatrix, component); } } }; module2.exports = Graph; } }); // node_modules/smiles-drawer/src/SSSR.js var require_SSSR = __commonJS({ "node_modules/smiles-drawer/src/SSSR.js"(exports, module2) { var Graph = require_Graph(); var SSSR = class { /** * Returns an array containing arrays, each representing a ring from the smallest set of smallest rings in the graph. * * @param {Graph} graph A Graph object. * @param {Boolean} [experimental=false] Whether or not to use experimental SSSR. * @returns {Array[]} An array containing arrays, each representing a ring from the smallest set of smallest rings in the group. */ static getRings(graph, experimental = false) { let adjacencyMatrix = graph.getComponentsAdjacencyMatrix(); if (adjacencyMatrix.length === 0) { return null; } let connectedComponents = Graph.getConnectedComponents(adjacencyMatrix); let rings = Array(); for (var i = 0; i < connectedComponents.length; i++) { let connectedComponent = connectedComponents[i]; let ccAdjacencyMatrix = graph.getSubgraphAdjacencyMatrix([...connectedComponent]); let arrBondCount = new Uint16Array(ccAdjacencyMatrix.length); let arrRingCount = new Uint16Array(ccAdjacencyMatrix.length); for (var j = 0; j < ccAdjacencyMatrix.length; j++) { arrRingCount[j] = 0; arrBondCount[j] = 0; for (var k = 0; k < ccAdjacencyMatrix[j].length; k++) { arrBondCount[j] += ccAdjacencyMatrix[j][k]; } } let nEdges = 0; for (var j = 0; j < ccAdjacencyMatrix.length; j++) { for (var k = j + 1; k < ccAdjacencyMatrix.length; k++) { nEdges += ccAdjacencyMatrix[j][k]; } } let nSssr = nEdges - ccAdjacencyMatrix.length + 1; let allThree = true; for (var j = 0; j < arrBondCount.length; j++) { if (arrBondCount[j] !== 3) { allThree = false; } } if (allThree) { nSssr = 2 + nEdges - ccAdjacencyMatrix.length; } if (nSssr === 1) { rings.push([...connectedComponent]); continue; } if (experimental) { nSssr = 999; } let { d, pe, pe_prime } = SSSR.getPathIncludedDistanceMatrices(ccAdjacencyMatrix); let c = SSSR.getRingCandidates(d, pe, pe_prime); let sssr = SSSR.getSSSR(c, d, ccAdjacencyMatrix, pe, pe_prime, arrBondCount, arrRingCount, nSssr); for (var j = 0; j < sssr.length; j++) { let ring = Array(sssr[j].size); let index = 0; for (let val of sssr[j]) { ring[index++] = connectedComponent[val]; } rings.push(ring); } } return rings; } /** * Creates a printable string from a matrix (2D array). * * @param {Array[]} matrix A 2D array. * @returns {String} A string representing the matrix. */ static matrixToString(matrix) { let str = ""; for (var i = 0; i < matrix.length; i++) { for (var j = 0; j < matrix[i].length; j++) { str += matrix[i][j] + " "; } str += "\n"; } return str; } /** * Returnes the two path-included distance matrices used to find the sssr. * * @param {Array[]} adjacencyMatrix An adjacency matrix. * @returns {Object} The path-included distance matrices. { p1, p2 } */ static getPathIncludedDistanceMatrices(adjacencyMatrix) { let length = adjacencyMatrix.length; let d = Array(length); let pe = Array(length); let pe_prime = Array(length); var l = 0; var m = 0; var n = 0; var i = length; while (i--) { d[i] = Array(length); pe[i] = Array(length); pe_prime[i] = Array(length); var j = length; while (j--) { d[i][j] = i === j || adjacencyMatrix[i][j] === 1 ? adjacencyMatrix[i][j] : Number.POSITIVE_INFINITY; if (d[i][j] === 1) { pe[i][j] = [[[i, j]]]; } else { pe[i][j] = Array(); } pe_prime[i][j] = Array(); } } var k = length; var j; while (k--) { i = length; while (i--) { j = length; while (j--) { const previousPathLength = d[i][j]; const newPathLength = d[i][k] + d[k][j]; if (previousPathLength > newPathLength) { var l, m, n; if (previousPathLength === newPathLength + 1) { pe_prime[i][j] = [pe[i][j].length]; l = pe[i][j].length; while (l--) { pe_prime[i][j][l] = [pe[i][j][l].length]; m = pe[i][j][l].length; while (m--) { pe_prime[i][j][l][m] = [pe[i][j][l][m].length]; n = pe[i][j][l][m].length; while (n--) { pe_prime[i][j][l][m][n] = [pe[i][j][l][m][0], pe[i][j][l][m][1]]; } } } } else { pe_prime[i][j] = Array(); } d[i][j] = newPathLength; pe[i][j] = [[]]; l = pe[i][k][0].length; while (l--) { pe[i][j][0].push(pe[i][k][0][l]); } l = pe[k][j][0].length; while (l--) { pe[i][j][0].push(pe[k][j][0][l]); } } else if (previousPathLength === newPathLength) { if (pe[i][k].length && pe[k][j].length) { var l; if (pe[i][j].length) { let tmp = Array(); l = pe[i][k][0].length; while (l--) { tmp.push(pe[i][k][0][l]); } l = pe[k][j][0].length; while (l--) { tmp.push(pe[k][j][0][l]); } pe[i][j].push(tmp); } else { let tmp = Array(); l = pe[i][k][0].length; while (l--) { tmp.push(pe[i][k][0][l]); } l = pe[k][j][0].length; while (l--) { tmp.push(pe[k][j][0][l]); } pe[i][j][0] = tmp; } } } else if (previousPathLength === newPathLength - 1) { var l; if (pe_prime[i][j].length) { let tmp = Array(); l = pe[i][k][0].length; while (l--) { tmp.push(pe[i][k][0][l]); } l = pe[k][j][0].length; while (l--) { tmp.push(pe[k][j][0][l]); } pe_prime[i][j].push(tmp); } else { let tmp = Array(); l = pe[i][k][0].length; while (l--) { tmp.push(pe[i][k][0][l]); } l = pe[k][j][0].length; while (l--) { tmp.push(pe[k][j][0][l]); } pe_prime[i][j][0] = tmp; } } } } } return { d, pe, pe_prime }; } /** * Get the ring candidates from the path-included distance matrices. * * @param {Array[]} d The distance matrix. * @param {Array[]} pe A matrix containing the shortest paths. * @param {Array[]} pe_prime A matrix containing the shortest paths + one vertex. * @returns {Array[]} The ring candidates. */ static getRingCandidates(d, pe, pe_prime) { let length = d.length; let candidates = Array(); let c = 0; for (let i = 0; i < length; i++) { for (let j = 0; j < length; j++) { if (d[i][j] === 0 || pe[i][j].length === 1 && pe_prime[i][j] === 0) { continue; } else { if (pe_prime[i][j].length !== 0) { c = 2 * (d[i][j] + 0.5); } else { c = 2 * d[i][j]; } if (c !== Infinity) { candidates.push([c, pe[i][j], pe_prime[i][j]]); } } } } candidates.sort(function(a, b) { return a[0] - b[0]; }); return candidates; } /** * Searches the candidates for the smallest set of smallest rings. * * @param {Array[]} c The candidates. * @param {Array[]} d The distance matrix. * @param {Array[]} adjacencyMatrix An adjacency matrix. * @param {Array[]} pe A matrix containing the shortest paths. * @param {Array[]} pe_prime A matrix containing the shortest paths + one vertex. * @param {Uint16Array} arrBondCount A matrix containing the bond count of each vertex. * @param {Uint16Array} arrRingCount A matrix containing the number of rings associated with each vertex. * @param {Number} nsssr The theoretical number of rings in the graph. * @returns {Set[]} The smallest set of smallest rings. */ static getSSSR(c, d, adjacencyMatrix, pe, pe_prime, arrBondCount, arrRingCount, nsssr) { let cSssr = Array(); let allBonds = Array(); for (let i = 0; i < c.length; i++) { if (c[i][0] % 2 !== 0) { for (let j = 0; j < c[i][2].length; j++) { let bonds = c[i][1][0].concat(c[i][2][j]); for (var k = 0; k < bonds.length; k++) { if (bonds[k][0].constructor === Array) bonds[k] = bonds[k][0]; } let atoms = SSSR.bondsToAtoms(bonds); if (SSSR.getBondCount(atoms, adjacencyMatrix) === atoms.size && !SSSR.pathSetsContain(cSssr, atoms, bonds, allBonds, arrBondCount, arrRingCount)) { cSssr.push(atoms); allBonds = allBonds.concat(bonds); } if (cSssr.length > nsssr) { return cSssr; } } } else { for (let j = 0; j < c[i][1].length - 1; j++) { let bonds = c[i][1][j].concat(c[i][1][j + 1]); for (var k = 0; k < bonds.length; k++) { if (bonds[k][0].constructor === Array) bonds[k] = bonds[k][0]; } let atoms = SSSR.bondsToAtoms(bonds); if (SSSR.getBondCount(atoms, adjacencyMatrix) === atoms.size && !SSSR.pathSetsContain(cSssr, atoms, bonds, allBonds, arrBondCount, arrRingCount)) { cSssr.push(atoms); allBonds = allBonds.concat(bonds); } if (cSssr.length > nsssr) { return cSssr; } } } } return cSssr; } /** * Returns the number of edges in a graph defined by an adjacency matrix. * * @param {Array[]} adjacencyMatrix An adjacency matrix. * @returns {Number} The number of edges in the graph defined by the adjacency matrix. */ static getEdgeCount(adjacencyMatrix) { let edgeCount = 0; let length = adjacencyMatrix.length; var i = length - 1; while (i--) { var j = length; while (j--) { if (adjacencyMatrix[i][j] === 1) { edgeCount++; } } } return edgeCount; } /** * Returns an edge list constructed form an adjacency matrix. * * @param {Array[]} adjacencyMatrix An adjacency matrix. * @returns {Array[]} An edge list. E.g. [ [ 0, 1 ], ..., [ 16, 2 ] ] */ static getEdgeList(adjacencyMatrix) { let length = adjacencyMatrix.length; let edgeList = Array(); var i = length - 1; while (i--) { var j = length; while (j--) { if (adjacencyMatrix[i][j] === 1) { edgeList.push([i, j]); } } } return edgeList; } /** * Return a set of vertex indices contained in an array of bonds. * * @param {Array} bonds An array of bonds. A bond is defined as [ sourceVertexId, targetVertexId ]. * @returns {Set} An array of vertices. */ static bondsToAtoms(bonds) { let atoms = /* @__PURE__ */ new Set(); var i = bonds.length; while (i--) { atoms.add(bonds[i][0]); atoms.add(bonds[i][1]); } return atoms; } /** * Returns the number of bonds within a set of atoms. * * @param {Set} atoms An array of atom ids. * @param {Array[]} adjacencyMatrix An adjacency matrix. * @returns {Number} The number of bonds in a set of atoms. */ static getBondCount(atoms, adjacencyMatrix) { let count = 0; for (let u of atoms) { for (let v of atoms) { if (u === v) { continue; } count += adjacencyMatrix[u][v]; } } return count / 2; } /** * Checks whether or not a given path already exists in an array of paths. * * @param {Set[]} pathSets An array of sets each representing a path. * @param {Set} pathSet A set representing a path. * @param {Array[]} bonds The bonds associated with the current path. * @param {Array[]} allBonds All bonds currently associated with rings in the SSSR set. * @param {Uint16Array} arrBondCount A matrix containing the bond count of each vertex. * @param {Uint16Array} arrRingCount A matrix containing the number of rings associated with each vertex. * @returns {Boolean} A boolean indicating whether or not a give path is contained within a set. */ static pathSetsContain(pathSets, pathSet, bonds, allBonds, arrBondCount, arrRingCount) { var i = pathSets.length; while (i--) { if (SSSR.isSupersetOf(pathSet, pathSets[i])) { return true; } if (pathSets[i].size !== pathSet.size) { continue; } if (SSSR.areSetsEqual(pathSets[i], pathSet)) { return true; } } let count = 0; let allContained = false; i = bonds.length; while (i--) { var j = allBonds.length; while (j--) { if (bonds[i][0] === allBonds[j][0] && bonds[i][1] === allBonds[j][1] || bonds[i][1] === allBonds[j][0] && bonds[i][0] === allBonds[j][1]) { count++; } if (count === bonds.length) { allContained = true; } } } let specialCase = false; if (allContained) { for (let element of pathSet) { if (arrRingCount[element] < arrBondCount[element]) { specialCase = true; break; } } } if (allContained && !specialCase) { return true; } for (let element of pathSet) { arrRingCount[element]++; } return false; } /** * Checks whether or not two sets are equal (contain the same elements). * * @param {Set} setA A set. * @param {Set} setB A set. * @returns {Boolean} A boolean indicating whether or not the two sets are equal. */ static areSetsEqual(setA, setB) { if (setA.size !== setB.size) { return false; } for (let element of setA) { if (!setB.has(element)) { return false; } } return true; } /** * Checks whether or not a set (setA) is a superset of another set (setB). * * @param {Set} setA A set. * @param {Set} setB A set. * @returns {Boolean} A boolean indicating whether or not setB is a superset of setA. */ static isSupersetOf(setA, setB) { for (var element of setB) { if (!setA.has(element)) { return false; } } return true; } }; module2.exports = SSSR; } }); // node_modules/smiles-drawer/src/ThemeManager.js var require_ThemeManager = __commonJS({ "node_modules/smiles-drawer/src/ThemeManager.js"(exports, module2) { var ThemeManager = class { constructor(colors, theme) { this.colors = colors; this.theme = this.colors[theme]; } /** * Returns the hex code of a color associated with a key from the current theme. * * @param {String} key The color key in the theme (e.g. C, N, BACKGROUND, ...). * @returns {String} A color hex value. */ getColor(key) { if (key) { key = key.toUpperCase(); if (key in this.theme) { return this.theme[key]; } } return this.theme["C"]; } /** * Sets the theme to the specified string if it exists. If it does not, this * does nothing. * * @param {String} theme the name of the theme to switch to */ setTheme(theme) { if (this.colors.hasOwnProperty(theme)) { this.theme = this.colors[theme]; } } }; module2.exports = ThemeManager; } }); // node_modules/smiles-drawer/src/Options.js var require_Options = __commonJS({ "node_modules/smiles-drawer/src/Options.js"(exports, module2) { var Options = class { /** * A helper method to extend the default options with user supplied ones. */ static extend() { let that = this; let extended = {}; let deep = false; let i = 0; let length = arguments.length; if (Object.prototype.toString.call(arguments[0]) === "[object Boolean]") { deep = arguments[0]; i++; } let merge = function(obj) { for (var prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) { if (deep && Object.prototype.toString.call(obj[prop]) === "[object Object]") { extended[prop] = that.extend(true, extended[prop], obj[prop]); } else { extended[prop] = obj[prop]; } } } }; for (; i < length; i++) { let obj = arguments[i]; merge(obj); } return extended; } }; module2.exports = Options; } }); // node_modules/smiles-drawer/src/DrawerBase.js var require_DrawerBase = __commonJS({ "node_modules/smiles-drawer/src/DrawerBase.js"(exports, module2) { var MathHelper = require_MathHelper(); var ArrayHelper = require_ArrayHelper(); var Vector2 = require_Vector2(); var Line = require_Line(); var Vertex = require_Vertex(); var Edge = require_Edge(); var Atom = require_Atom(); var Ring = require_Ring(); var RingConnection = require_RingConnection(); var CanvasWrapper = require_CanvasWrapper(); var Graph = require_Graph(); var SSSR = require_SSSR(); var ThemeManager = require_ThemeManager(); var Options = require_Options(); var DrawerBase = class { /** * The constructor for the class SmilesDrawer. * * @param {Object} options An object containing custom values for different options. It is merged with the default options. */ constructor(options) { this.graph = null; this.doubleBondConfigCount = 0; this.doubleBondConfig = null; this.ringIdCounter = 0; this.ringConnectionIdCounter = 0; this.canvasWrapper = null; this.totalOverlapScore = 0; this.defaultOptions = { width: 500, height: 500, scale: 0, bondThickness: 1, bondLength: 30, shortBondLength: 0.8, bondSpacing: 0.17 * 30, atomVisualization: "default", isomeric: true, debug: false, terminalCarbons: false, explicitHydrogens: true, overlapSensitivity: 0.42, overlapResolutionIterations: 1, compactDrawing: true, fontFamily: "Arial, Helvetica, sans-serif", fontSizeLarge: 11, fontSizeSmall: 3, padding: 10, experimentalSSSR: false, kkThreshold: 0.1, kkInnerThreshold: 0.1, kkMaxIteration: 2e4, kkMaxInnerIteration: 50, kkMaxEnergy: 1e9, weights: { colormap: null, additionalPadding: 20, sigma: 10, interval: 0, opacity: 1 }, themes: { dark: { C: "#fff", O: "#e74c3c", N: "#3498db", F: "#27ae60", CL: "#16a085", BR: "#d35400", I: "#8e44ad", P: "#d35400", S: "#f1c40f", B: "#e67e22", SI: "#e67e22", H: "#aaa", BACKGROUND: "#141414" }, light: { C: "#222", O: "#e74c3c", N: "#3498db", F: "#27ae60", CL: "#16a085", BR: "#d35400", I: "#8e44ad", P: "#d35400", S: "#f1c40f", B: "#e67e22", SI: "#e67e22", H: "#666", BACKGROUND: "#fff" }, oldschool: { C: "#000", O: "#000", N: "#000", F: "#000", CL: "#000", BR: "#000", I: "#000", P: "#000", S: "#000", B: "#000", SI: "#000", H: "#000", BACKGROUND: "#fff" }, "solarized": { C: "#586e75", O: "#dc322f", N: "#268bd2", F: "#859900", CL: "#16a085", BR: "#cb4b16", I: "#6c71c4", P: "#d33682", S: "#b58900", B: "#2aa198", SI: "#2aa198", H: "#657b83", BACKGROUND: "#fff" }, "solarized-dark": { C: "#93a1a1", O: "#dc322f", N: "#268bd2", F: "#859900", CL: "#16a085", BR: "#cb4b16", I: "#6c71c4", P: "#d33682", S: "#b58900", B: "#2aa198", SI: "#2aa198", H: "#839496", BACKGROUND: "#fff" }, "matrix": { C: "#678c61", O: "#2fc079", N: "#4f7e7e", F: "#90d762", CL: "#82d967", BR: "#23755a", I: "#409931", P: "#c1ff8a", S: "#faff00", B: "#50b45a", SI: "#409931", H: "#426644", BACKGROUND: "#fff" }, "github": { C: "#24292f", O: "#cf222e", N: "#0969da", F: "#2da44e", CL: "#6fdd8b", BR: "#bc4c00", I: "#8250df", P: "#bf3989", S: "#d4a72c", B: "#fb8f44", SI: "#bc4c00", H: "#57606a", BACKGROUND: "#fff" }, "carbon": { C: "#161616", O: "#da1e28", N: "#0f62fe", F: "#198038", CL: "#007d79", BR: "#fa4d56", I: "#8a3ffc", P: "#ff832b", S: "#f1c21b", B: "#8a3800", SI: "#e67e22", H: "#525252", BACKGROUND: "#fff" }, "cyberpunk": { C: "#ea00d9", O: "#ff3131", N: "#0abdc6", F: "#00ff9f", CL: "#00fe00", BR: "#fe9f20", I: "#ff00ff", P: "#fe7f00", S: "#fcee0c", B: "#ff00ff", SI: "#ffffff", H: "#913cb1", BACKGROUND: "#fff" }, "gruvbox": { C: "#665c54", O: "#cc241d", N: "#458588", F: "#98971a", CL: "#79740e", BR: "#d65d0e", I: "#b16286", P: "#af3a03", S: "#d79921", B: "#689d6a", SI: "#427b58", H: "#7c6f64", BACKGROUND: "#fbf1c7" }, "gruvbox-dark": { C: "#ebdbb2", O: "#cc241d", N: "#458588", F: "#98971a", CL: "#b8bb26", BR: "#d65d0e", I: "#b16286", P: "#fe8019", S: "#d79921", B: "#8ec07c", SI: "#83a598", H: "#bdae93", BACKGROUND: "#282828" }, custom: { C: "#222", O: "#e74c3c", N: "#3498db", F: "#27ae60", CL: "#16a085", BR: "#d35400", I: "#8e44ad", P: "#d35400", S: "#f1c40f", B: "#e67e22", SI: "#e67e22", H: "#666", BACKGROUND: "#fff" } } }; this.opts = Options.extend(true, this.defaultOptions, options); this.opts.halfBondSpacing = this.opts.bondSpacing / 2; this.opts.bondLengthSq = this.opts.bondLength * this.opts.bondLength; this.opts.halfFontSizeLarge = this.opts.fontSizeLarge / 2; this.opts.quarterFontSizeLarge = this.opts.fontSizeLarge / 4; this.opts.fifthFontSizeSmall = this.opts.fontSizeSmall / 5; this.theme = this.opts.themes.dark; } /** * Draws the parsed smiles data to a canvas element. * * @param {Object} data The tree returned by the smiles parser. * @param {(String|HTMLCanvasElement)} target The id of the HTML canvas element the structure is drawn to - or the element itself. * @param {String} themeName='dark' The name of the theme to use. Built-in themes are 'light' and 'dark'. * @param {Boolean} infoOnly=false Only output info on the molecule without drawing anything to the canvas. */ draw(data, target, themeName = "light", infoOnly = false) { this.initDraw(data, themeName, infoOnly); if (!this.infoOnly) { this.themeManager = new ThemeManager(this.opts.themes, themeName); this.canvasWrapper = new CanvasWrapper(target, this.themeManager, this.opts); } if (!infoOnly) { this.processGraph(); this.canvasWrapper.scale(this.graph.vertices); this.drawEdges(this.opts.debug); this.drawVertices(this.opts.debug); this.canvasWrapper.reset(); if (this.opts.debug) { console.log(this.graph); console.log(this.rings); console.log(this.ringConnections); } } } /** * Returns the number of rings this edge is a part of. * * @param {Number} edgeId The id of an edge. * @returns {Number} The number of rings the provided edge is part of. */ edgeRingCount(edgeId) { let edge = this.graph.edges[edgeId]; let a = this.graph.vertices[edge.sourceId]; let b = this.graph.vertices[edge.targetId]; return Math.min(a.value.rings.length, b.value.rings.length); } /** * Returns an array containing the bridged rings associated with this molecule. * * @returns {Ring[]} An array containing all bridged rings associated with this molecule. */ getBridgedRings() { let bridgedRings = Array(); for (var i = 0; i < this.rings.length; i++) { if (this.rings[i].isBridged) { bridgedRings.push(this.rings[i]); } } return bridgedRings; } /** * Returns an array containing all fused rings associated with this molecule. * * @returns {Ring[]} An array containing all fused rings associated with this molecule. */ getFusedRings() { let fusedRings = Array(); for (var i = 0; i < this.rings.length; i++) { if (this.rings[i].isFused) { fusedRings.push(this.rings[i]); } } return fusedRings; } /** * Returns an array containing all spiros associated with this molecule. * * @returns {Ring[]} An array containing all spiros associated with this molecule. */ getSpiros() { let spiros = Array(); for (var i = 0; i < this.rings.length; i++) { if (this.rings[i].isSpiro) { spiros.push(this.rings[i]); } } return spiros; } /** * Returns a string containing a semicolon and new-line separated list of ring properties: Id; Members Count; Neighbours Count; IsSpiro; IsFused; IsBridged; Ring Count (subrings of bridged rings) * * @returns {String} A string as described in the method description. */ printRingInfo() { let result = ""; for (var i = 0; i < this.rings.length; i++) { const ring = this.rings[i]; result += ring.id + ";"; result += ring.members.length + ";"; result += ring.neighbours.length + ";"; result += ring.isSpiro ? "true;" : "false;"; result += ring.isFused ? "true;" : "false;"; result += ring.isBridged ? "true;" : "false;"; result += ring.rings.length + ";"; result += "\n"; } return result; } /** * Rotates the drawing to make the widest dimension horizontal. */ rotateDrawing() { let a = 0; let b = 0; let maxDist = 0; for (var i = 0; i < this.graph.vertices.length; i++) { let vertexA = this.graph.vertices[i]; if (!vertexA.value.isDrawn) { continue; } for (var j = i + 1; j < this.graph.vertices.length; j++) { let vertexB = this.graph.vertices[j]; if (!vertexB.value.isDrawn) { continue; } let dist = vertexA.position.distanceSq(vertexB.position); if (dist > maxDist) { maxDist = dist; a = i; b = j; } } } let angle = -Vector2.subtract(this.graph.vertices[a].position, this.graph.vertices[b].position).angle(); if (!isNaN(angle)) { let remainder = angle % 0.523599; if (remainder < 0.2617995) { angle = angle - remainder; } else { angle += 0.523599 - remainder; } for (var i = 0; i < this.graph.vertices.length; i++) { if (i === b) { continue; } this.graph.vertices[i].position.rotateAround(angle, this.graph.vertices[b].position); } for (var i = 0; i < this.rings.length; i++) { this.rings[i].center.rotateAround(angle, this.graph.vertices[b].position); } } } /** * Returns the total overlap score of the current molecule. * * @returns {Number} The overlap score. */ getTotalOverlapScore() { return this.totalOverlapScore; } /** * Returns the ring count of the current molecule. * * @returns {Number} The ring count. */ getRingCount() { return this.rings.length; } /** * Checks whether or not the current molecule a bridged ring. * * @returns {Boolean} A boolean indicating whether or not the current molecule a bridged ring. */ hasBridgedRing() { return this.bridgedRing; } /** * Returns the number of heavy atoms (non-hydrogen) in the current molecule. * * @returns {Number} The heavy atom count. */ getHeavyAtomCount() { let hac = 0; for (var i = 0; i < this.graph.vertices.length; i++) { if (this.graph.vertices[i].value.element !== "H") { hac++; } } return hac; } /** * Returns the molecular formula of the loaded molecule as a string. * * @returns {String} The molecular formula. */ getMolecularFormula(data = null) { let molecularFormula = ""; let counts = /* @__PURE__ */ new Map(); let graph = data === null ? this.graph : new Graph(data, this.opts.isomeric); for (var i = 0; i < graph.vertices.length; i++) { let atom = graph.vertices[i].value; if (counts.has(atom.element)) { counts.set(atom.element, counts.get(atom.element) + 1); } else { counts.set(atom.element, 1); } if (atom.bracket && !atom.bracket.chirality) { if (counts.has("H")) { counts.set("H", counts.get("H") + atom.bracket.hcount); } else { counts.set("H", atom.bracket.hcount); } } if (!atom.bracket) { let nHydrogens = Atom.maxBonds[atom.element] - atom.bondCount; if (atom.isPartOfAromaticRing) { nHydrogens--; } if (counts.has("H")) { counts.set("H", counts.get("H") + nHydrogens); } else { counts.set("H", nHydrogens); } } } if (counts.has("C")) { let count = counts.get("C"); molecularFormula += "C" + (count > 1 ? count : ""); counts.delete("C"); } if (counts.has("H")) { let count = counts.get("H"); molecularFormula += "H" + (count > 1 ? count : ""); counts.delete("H"); } let elements = Object.keys(Atom.atomicNumbers).sort(); elements.map((e) => { if (counts.has(e)) { let count = counts.get(e); molecularFormula += e + (count > 1 ? count : ""); } }); return molecularFormula; } /** * Returns the type of the ringbond (e.g. '=' for a double bond). The ringbond represents the break in a ring introduced when creating the MST. If the two vertices supplied as arguments are not part of a common ringbond, the method returns null. * * @param {Vertex} vertexA A vertex. * @param {Vertex} vertexB A vertex. * @returns {(String|null)} Returns the ringbond type or null, if the two supplied vertices are not connected by a ringbond. */ getRingbondType(vertexA, vertexB) { if (vertexA.value.getRingbondCount() < 1 || vertexB.value.getRingbondCount() < 1) { return null; } for (var i = 0; i < vertexA.value.ringbonds.length; i++) { for (var j = 0; j < vertexB.value.ringbonds.length; j++) { if (vertexA.value.ringbonds[i].id === vertexB.value.ringbonds[j].id) { if (vertexA.value.ringbonds[i].bondType === "-") { return vertexB.value.ringbonds[j].bond; } else { return vertexA.value.ringbonds[i].bond; } } } } return null; } initDraw(data, themeName, infoOnly, highlight_atoms) { this.data = data; this.infoOnly = infoOnly; this.ringIdCounter = 0; this.ringConnectionIdCounter = 0; this.graph = new Graph(data, this.opts.isomeric); this.rings = Array(); this.ringConnections = Array(); this.originalRings = Array(); this.originalRingConnections = Array(); this.bridgedRing = false; this.doubleBondConfigCount = null; this.doubleBondConfig = null; this.highlight_atoms = highlight_atoms; this.initRings(); this.initHydrogens(); } processGraph() { this.position(); this.restoreRingInformation(); this.resolvePrimaryOverlaps(); let overlapScore = this.getOverlapScore(); this.totalOverlapScore = this.getOverlapScore().total; for (var o = 0; o < this.opts.overlapResolutionIterations; o++) { for (var i = 0; i < this.graph.edges.length; i++) { let edge = this.graph.edges[i]; if (this.isEdgeRotatable(edge)) { let subTreeDepthA = this.graph.getTreeDepth(edge.sourceId, edge.targetId); let subTreeDepthB = this.graph.getTreeDepth(edge.targetId, edge.sourceId); let a = edge.targetId; let b = edge.sourceId; if (subTreeDepthA > subTreeDepthB) { a = edge.sourceId; b = edge.targetId; } let subTreeOverlap = this.getSubtreeOverlapScore(b, a, overlapScore.vertexScores); if (subTreeOverlap.value > this.opts.overlapSensitivity) { let vertexA = this.graph.vertices[a]; let vertexB = this.graph.vertices[b]; let neighboursB = vertexB.getNeighbours(a); if (neighboursB.length === 1) { let neighbour = this.graph.vertices[neighboursB[0]]; let angle = neighbour.position.getRotateAwayFromAngle(vertexA.position, vertexB.position, MathHelper.toRad(120)); this.rotateSubtree(neighbour.id, vertexB.id, angle, vertexB.position); let newTotalOverlapScore = this.getOverlapScore().total; if (newTotalOverlapScore > this.totalOverlapScore) { this.rotateSubtree(neighbour.id, vertexB.id, -angle, vertexB.position); } else { this.totalOverlapScore = newTotalOverlapScore; } } else if (neighboursB.length === 2) { if (vertexB.value.rings.length !== 0 && vertexA.value.rings.length !== 0) { continue; } let neighbourA = this.graph.vertices[neighboursB[0]]; let neighbourB = this.graph.vertices[neighboursB[1]]; if (neighbourA.value.rings.length === 1 && neighbourB.value.rings.length === 1) { if (neighbourA.value.rings[0] !== neighbourB.value.rings[0]) { continue; } } else if (neighbourA.value.rings.length !== 0 || neighbourB.value.rings.length !== 0) { continue; } else { let angleA = neighbourA.position.getRotateAwayFromAngle(vertexA.position, vertexB.position, MathHelper.toRad(120)); let angleB = neighbourB.position.getRotateAwayFromAngle(vertexA.position, vertexB.position, MathHelper.toRad(120)); this.rotateSubtree(neighbourA.id, vertexB.id, angleA, vertexB.position); this.rotateSubtree(neighbourB.id, vertexB.id, angleB, vertexB.position); let newTotalOverlapScore = this.getOverlapScore().total; if (newTotalOverlapScore > this.totalOverlapScore) { this.rotateSubtree(neighbourA.id, vertexB.id, -angleA, vertexB.position); this.rotateSubtree(neighbourB.id, vertexB.id, -angleB, vertexB.position); } else { this.totalOverlapScore = newTotalOverlapScore; } } } overlapScore = this.getOverlapScore(); } } } } this.resolveSecondaryOverlaps(overlapScore.scores); if (this.opts.isomeric) { this.annotateStereochemistry(); } if (this.opts.compactDrawing && this.opts.atomVisualization === "default") { this.initPseudoElements(); } this.rotateDrawing(); } /** * Initializes rings and ringbonds for the current molecule. */ initRings() { let openBonds = /* @__PURE__ */ new Map(); for (var i = this.graph.vertices.length - 1; i >= 0; i--) { let vertex = this.graph.vertices[i]; if (vertex.value.ringbonds.length === 0) { continue; } for (var j = 0; j < vertex.value.ringbonds.length; j++) { let ringbondId = vertex.value.ringbonds[j].id; let ringbondBond = vertex.value.ringbonds[j].bond; if (!openBonds.has(ringbondId)) { openBonds.set(ringbondId, [vertex.id, ringbondBond]); } else { let sourceVertexId = vertex.id; let targetVertexId = openBonds.get(ringbondId)[0]; let targetRingbondBond = openBonds.get(ringbondId)[1]; let edge = new Edge(sourceVertexId, targetVertexId, 1); edge.setBondType(targetRingbondBond || ringbondBond || "-"); let edgeId = this.graph.addEdge(edge); let targetVertex = this.graph.vertices[targetVertexId]; vertex.addRingbondChild(targetVertexId, j); vertex.value.addNeighbouringElement(targetVertex.value.element); targetVertex.addRingbondChild(sourceVertexId, j); targetVertex.value.addNeighbouringElement(vertex.value.element); vertex.edges.push(edgeId); targetVertex.edges.push(edgeId); openBonds.delete(ringbondId); } } } let rings = SSSR.getRings(this.graph, this.opts.experimentalSSSR); if (rings === null) { return; } for (var i = 0; i < rings.length; i++) { let ringVertices = [...rings[i]]; let ringId = this.addRing(new Ring(ringVertices)); for (var j = 0; j < ringVertices.length; j++) { this.graph.vertices[ringVertices[j]].value.rings.push(ringId); } } for (var i = 0; i < this.rings.length - 1; i++) { for (var j = i + 1; j < this.rings.length; j++) { let a = this.rings[i]; let b = this.rings[j]; let ringConnection = new RingConnection(a, b); if (ringConnection.vertices.size > 0) { this.addRingConnection(ringConnection); } } } for (var i = 0; i < this.rings.length; i++) { let ring = this.rings[i]; ring.neighbours = RingConnection.getNeighbours(this.ringConnections, ring.id); } for (var i = 0; i < this.rings.length; i++) { let ring = this.rings[i]; this.graph.vertices[ring.members[0]].value.addAnchoredRing(ring.id); } this.backupRingInformation(); while (this.rings.length > 0) { let id = -1; for (var i = 0; i < this.rings.length; i++) { let ring2 = this.rings[i]; if (this.isPartOfBridgedRing(ring2.id) && !ring2.isBridged) { id = ring2.id; } } if (id === -1) { break; } let ring = this.getRing(id); let involvedRings = this.getBridgedRingRings(ring.id); this.bridgedRing = true; this.createBridgedRing(involvedRings, ring.members[0]); for (var i = 0; i < involvedRings.length; i++) { this.removeRing(involvedRings[i]); } } } initHydrogens() { if (!this.opts.explicitHydrogens) { for (var i = 0; i < this.graph.vertices.length; i++) { let vertex = this.graph.vertices[i]; if (vertex.value.element !== "H") { continue; } let neighbour = this.graph.vertices[vertex.neighbours[0]]; neighbour.value.hasHydrogen = true; if (!neighbour.value.isStereoCenter || neighbour.value.rings.length < 2 && !neighbour.value.bridgedRing || neighbour.value.bridgedRing && neighbour.value.originalRings.length < 2) { vertex.value.isDrawn = false; } } } } /** * Returns all rings connected by bridged bonds starting from the ring with the supplied ring id. * * @param {Number} ringId A ring id. * @returns {Number[]} An array containing all ring ids of rings part of a bridged ring system. */ getBridgedRingRings(ringId) { let involvedRings = Array(); let that = this; let recurse = function(r) { let ring = that.getRing(r); involvedRings.push(r); for (var i = 0; i < ring.neighbours.length; i++) { let n = ring.neighbours[i]; if (involvedRings.indexOf(n) === -1 && n !== r && RingConnection.isBridge(that.ringConnections, that.graph.vertices, r, n)) { recurse(n); } } }; recurse(ringId); return ArrayHelper.unique(involvedRings); } /** * Checks whether or not a ring is part of a bridged ring. * * @param {Number} ringId A ring id. * @returns {Boolean} A boolean indicating whether or not the supplied ring (by id) is part of a bridged ring system. */ isPartOfBridgedRing(ringId) { for (var i = 0; i < this.ringConnections.length; i++) { if (this.ringConnections[i].containsRing(ringId) && this.ringConnections[i].isBridge(this.graph.vertices)) { return true; } } return false; } /** * Creates a bridged ring. * * @param {Number[]} ringIds An array of ids of rings involved in the bridged ring. * @param {Number} sourceVertexId The vertex id to start the bridged ring discovery from. * @returns {Ring} The bridged ring. */ createBridgedRing(ringIds, sourceVertexId) { let ringMembers = /* @__PURE__ */ new Set(); let vertices = /* @__PURE__ */ new Set(); let neighbours = /* @__PURE__ */ new Set(); for (var i = 0; i < ringIds.length; i++) { let ring2 = this.getRing(ringIds[i]); ring2.isPartOfBridged = true; for (var j = 0; j < ring2.members.length; j++) { vertices.add(ring2.members[j]); } for (var j = 0; j < ring2.neighbours.length; j++) { let id = ring2.neighbours[j]; if (ringIds.indexOf(id) === -1) { neighbours.add(ring2.neighbours[j]); } } } let leftovers = /* @__PURE__ */ new Set(); for (let id of vertices) { let vertex = this.graph.vertices[id]; let intersection = ArrayHelper.intersection(ringIds, vertex.value.rings); if (vertex.value.rings.length === 1 || intersection.length === 1) { ringMembers.add(vertex.id); } else { leftovers.add(vertex.id); } } let tmp = Array(); let insideRing = Array(); for (let id of leftovers) { let vertex = this.graph.vertices[id]; let onRing = false; for (let j2 = 0; j2 < vertex.edges.length; j2++) { if (this.edgeRingCount(vertex.edges[j2]) === 1) { onRing = true; } } if (onRing) { vertex.value.isBridgeNode = true; ringMembers.add(vertex.id); } else { vertex.value.isBridge = true; ringMembers.add(vertex.id); } } let ring = new Ring([...ringMembers]); this.addRing(ring); ring.isBridged = true; ring.neighbours = [...neighbours]; for (var i = 0; i < ringIds.length; i++) { ring.rings.push(this.getRing(ringIds[i]).clone()); } for (var i = 0; i < ring.members.length; i++) { this.graph.vertices[ring.members[i]].value.bridgedRing = ring.id; } for (var i = 0; i < insideRing.length; i++) { let vertex = this.graph.vertices[insideRing[i]]; vertex.value.rings = Array(); } for (let id of ringMembers) { let vertex = this.graph.vertices[id]; vertex.value.rings = ArrayHelper.removeAll(vertex.value.rings, ringIds); vertex.value.rings.push(ring.id); } for (var i = 0; i < ringIds.length; i++) { for (var j = i + 1; j < ringIds.length; j++) { this.removeRingConnectionsBetween(ringIds[i], ringIds[j]); } } for (let id of neighbours) { let connections = this.getRingConnections(id, ringIds); for (var j = 0; j < connections.length; j++) { this.getRingConnection(connections[j]).updateOther(ring.id, id); } this.getRing(id).neighbours.push(ring.id); } return ring; } /** * Checks whether or not two vertices are in the same ring. * * @param {Vertex} vertexA A vertex. * @param {Vertex} vertexB A vertex. * @returns {Boolean} A boolean indicating whether or not the two vertices are in the same ring. */ areVerticesInSameRing(vertexA, vertexB) { for (var i = 0; i < vertexA.value.rings.length; i++) { for (var j = 0; j < vertexB.value.rings.length; j++) { if (vertexA.value.rings[i] === vertexB.value.rings[j]) { return true; } } } return false; } /** * Returns an array of ring ids shared by both vertices. * * @param {Vertex} vertexA A vertex. * @param {Vertex} vertexB A vertex. * @returns {Number[]} An array of ids of rings shared by the two vertices. */ getCommonRings(vertexA, vertexB) { let commonRings = Array(); for (var i = 0; i < vertexA.value.rings.length; i++) { for (var j = 0; j < vertexB.value.rings.length; j++) { if (vertexA.value.rings[i] == vertexB.value.rings[j]) { commonRings.push(vertexA.value.rings[i]); } } } return commonRings; } /** * Returns the aromatic or largest ring shared by the two vertices. * * @param {Vertex} vertexA A vertex. * @param {Vertex} vertexB A vertex. * @returns {(Ring|null)} If an aromatic common ring exists, that ring, else the largest (non-aromatic) ring, else null. */ getLargestOrAromaticCommonRing(vertexA, vertexB) { let commonRings = this.getCommonRings(vertexA, vertexB); let maxSize = 0; let largestCommonRing = null; for (var i = 0; i < commonRings.length; i++) { let ring = this.getRing(commonRings[i]); let size = ring.getSize(); if (ring.isBenzeneLike(this.graph.vertices)) { return ring; } else if (size > maxSize) { maxSize = size; largestCommonRing = ring; } } return largestCommonRing; } /** * Returns an array of vertices positioned at a specified location. * * @param {Vector2} position The position to search for vertices. * @param {Number} radius The radius within to search. * @param {Number} excludeVertexId A vertex id to be excluded from the search results. * @returns {Number[]} An array containing vertex ids in a given location. */ getVerticesAt(position, radius, excludeVertexId) { let locals = Array(); for (var i = 0; i < this.graph.vertices.length; i++) { let vertex = this.graph.vertices[i]; if (vertex.id === excludeVertexId || !vertex.positioned) { continue; } let distance = position.distanceSq(vertex.position); if (distance <= radius * radius) { locals.push(vertex.id); } } return locals; } /** * Returns the closest vertex (connected as well as unconnected). * * @param {Vertex} vertex The vertex of which to find the closest other vertex. * @returns {Vertex} The closest vertex. */ getClosestVertex(vertex) { let minDist = 99999; let minVertex = null; for (var i = 0; i < this.graph.vertices.length; i++) { let v = this.graph.vertices[i]; if (v.id === vertex.id) { continue; } let distSq = vertex.position.distanceSq(v.position); if (distSq < minDist) { minDist = distSq; minVertex = v; } } return minVertex; } /** * Add a ring to this representation of a molecule. * * @param {Ring} ring A new ring. * @returns {Number} The ring id of the new ring. */ addRing(ring) { ring.id = this.ringIdCounter++; this.rings.push(ring); return ring.id; } /** * Removes a ring from the array of rings associated with the current molecule. * * @param {Number} ringId A ring id. */ removeRing(ringId) { this.rings = this.rings.filter(function(item) { return item.id !== ringId; }); this.ringConnections = this.ringConnections.filter(function(item) { return item.firstRingId !== ringId && item.secondRingId !== ringId; }); for (var i = 0; i < this.rings.length; i++) { let r = this.rings[i]; r.neighbours = r.neighbours.filter(function(item) { return item !== ringId; }); } } /** * Gets a ring object from the array of rings associated with the current molecule by its id. The ring id is not equal to the index, since rings can be added and removed when processing bridged rings. * * @param {Number} ringId A ring id. * @returns {Ring} A ring associated with the current molecule. */ getRing(ringId) { for (var i = 0; i < this.rings.length; i++) { if (this.rings[i].id == ringId) { return this.rings[i]; } } } /** * Add a ring connection to this representation of a molecule. * * @param {RingConnection} ringConnection A new ringConnection. * @returns {Number} The ring connection id of the new ring connection. */ addRingConnection(ringConnection) { ringConnection.id = this.ringConnectionIdCounter++; this.ringConnections.push(ringConnection); return ringConnection.id; } /** * Removes a ring connection from the array of rings connections associated with the current molecule. * * @param {Number} ringConnectionId A ring connection id. */ removeRingConnection(ringConnectionId) { this.ringConnections = this.ringConnections.filter(function(item) { return item.id !== ringConnectionId; }); } /** * Removes all ring connections between two vertices. * * @param {Number} vertexIdA A vertex id. * @param {Number} vertexIdB A vertex id. */ removeRingConnectionsBetween(vertexIdA, vertexIdB) { let toRemove = Array(); for (var i = 0; i < this.ringConnections.length; i++) { let ringConnection = this.ringConnections[i]; if (ringConnection.firstRingId === vertexIdA && ringConnection.secondRingId === vertexIdB || ringConnection.firstRingId === vertexIdB && ringConnection.secondRingId === vertexIdA) { toRemove.push(ringConnection.id); } } for (var i = 0; i < toRemove.length; i++) { this.removeRingConnection(toRemove[i]); } } /** * Get a ring connection with a given id. * * @param {Number} id * @returns {RingConnection} The ring connection with the specified id. */ getRingConnection(id) { for (var i = 0; i < this.ringConnections.length; i++) { if (this.ringConnections[i].id == id) { return this.ringConnections[i]; } } } /** * Get the ring connections between a ring and a set of rings. * * @param {Number} ringId A ring id. * @param {Number[]} ringIds An array of ring ids. * @returns {Number[]} An array of ring connection ids. */ getRingConnections(ringId, ringIds) { let ringConnections = Array(); for (var i = 0; i < this.ringConnections.length; i++) { let rc = this.ringConnections[i]; for (var j = 0; j < ringIds.length; j++) { let id = ringIds[j]; if (rc.firstRingId === ringId && rc.secondRingId === id || rc.firstRingId === id && rc.secondRingId === ringId) { ringConnections.push(rc.id); } } } return ringConnections; } /** * Returns the overlap score of the current molecule based on its positioned vertices. The higher the score, the more overlaps occur in the structure drawing. * * @returns {Object} Returns the total overlap score and the overlap score of each vertex sorted by score (higher to lower). Example: { total: 99, scores: [ { id: 0, score: 22 }, ... ] } */ getOverlapScore() { let total = 0; let overlapScores = new Float32Array(this.graph.vertices.length); for (var i = 0; i < this.graph.vertices.length; i++) { overlapScores[i] = 0; } for (var i = 0; i < this.graph.vertices.length; i++) { var j = this.graph.vertices.length; while (--j > i) { let a = this.graph.vertices[i]; let b = this.graph.vertices[j]; if (!a.value.isDrawn || !b.value.isDrawn) { continue; } let dist = Vector2.subtract(a.position, b.position).lengthSq(); if (dist < this.opts.bondLengthSq) { let weighted = (this.opts.bondLength - Math.sqrt(dist)) / this.opts.bondLength; total += weighted; overlapScores[i] += weighted; overlapScores[j] += weighted; } } } let sortable = Array(); for (var i = 0; i < this.graph.vertices.length; i++) { sortable.push({ id: i, score: overlapScores[i] }); } sortable.sort(function(a, b) { return b.score - a.score; }); return { total, scores: sortable, vertexScores: overlapScores }; } /** * When drawing a double bond, choose the side to place the double bond. E.g. a double bond should always been drawn inside a ring. * * @param {Vertex} vertexA A vertex. * @param {Vertex} vertexB A vertex. * @param {Vector2[]} sides An array containing the two normals of the line spanned by the two provided vertices. * @returns {Object} Returns an object containing the following information: { totalSideCount: Counts the sides of each vertex in the molecule, is an array [ a, b ], totalPosition: Same as position, but based on entire molecule, sideCount: Counts the sides of each neighbour, is an array [ a, b ], position: which side to position the second bond, is 0 or 1, represents the index in the normal array. This is based on only the neighbours anCount: the number of neighbours of vertexA, bnCount: the number of neighbours of vertexB } */ chooseSide(vertexA, vertexB, sides) { let an = vertexA.getNeighbours(vertexB.id); let bn = vertexB.getNeighbours(vertexA.id); let anCount = an.length; let bnCount = bn.length; let tn = ArrayHelper.merge(an, bn); let sideCount = [0, 0]; for (var i = 0; i < tn.length; i++) { let v = this.graph.vertices[tn[i]].position; if (v.sameSideAs(vertexA.position, vertexB.position, sides[0])) { sideCount[0]++; } else { sideCount[1]++; } } let totalSideCount = [0, 0]; for (var i = 0; i < this.graph.vertices.length; i++) { let v = this.graph.vertices[i].position; if (v.sameSideAs(vertexA.position, vertexB.position, sides[0])) { totalSideCount[0]++; } else { totalSideCount[1]++; } } return { totalSideCount, totalPosition: totalSideCount[0] > totalSideCount[1] ? 0 : 1, sideCount, position: sideCount[0] > sideCount[1] ? 0 : 1, anCount, bnCount }; } /** * Sets the center for a ring. * * @param {Ring} ring A ring. */ setRingCenter(ring) { let ringSize = ring.getSize(); let total = new Vector2(0, 0); for (var i = 0; i < ringSize; i++) { total.add(this.graph.vertices[ring.members[i]].position); } ring.center = total.divide(ringSize); } /** * Gets the center of a ring contained within a bridged ring and containing a given vertex. * * @param {Ring} ring A bridged ring. * @param {Vertex} vertex A vertex. * @returns {Vector2} The center of the subring that containing the vertex. */ getSubringCenter(ring, vertex) { let rings = vertex.value.originalRings; let center = ring.center; let smallest = Number.MAX_VALUE; for (var i = 0; i < rings.length; i++) { for (var j = 0; j < ring.rings.length; j++) { if (rings[i] === ring.rings[j].id) { if (ring.rings[j].getSize() < smallest) { center = ring.rings[j].center; smallest = ring.rings[j].getSize(); } } } } return center; } /** * Draw the actual edges as bonds to the canvas. * * @param {Boolean} debug A boolean indicating whether or not to draw debug helpers. */ drawEdges(debug) { let that = this; let drawn = Array(this.graph.edges.length); drawn.fill(false); this.graph.traverseBF(0, function(vertex) { let edges = that.graph.getEdges(vertex.id); for (var i2 = 0; i2 < edges.length; i2++) { let edgeId = edges[i2]; if (!drawn[edgeId]) { drawn[edgeId] = true; that.drawEdge(edgeId, debug); } } }); if (!this.bridgedRing) { for (var i = 0; i < this.rings.length; i++) { let ring = this.rings[i]; if (this.isRingAromatic(ring)) { this.canvasWrapper.drawAromaticityRing(ring); } } } } /** * Draw the an edge as a bonds to the canvas. * * @param {Number} edgeId An edge id. * @param {Boolean} debug A boolean indicating whether or not to draw debug helpers. */ drawEdge(edgeId, debug) { let that = this; let edge = this.graph.edges[edgeId]; let vertexA = this.graph.vertices[edge.sourceId]; let vertexB = this.graph.vertices[edge.targetId]; let elementA = vertexA.value.element; let elementB = vertexB.value.element; if ((!vertexA.value.isDrawn || !vertexB.value.isDrawn) && this.opts.atomVisualization === "default") { return; } let a = vertexA.position; let b = vertexB.position; let normals = this.getEdgeNormals(edge); let sides = ArrayHelper.clone(normals); sides[0].multiplyScalar(10).add(a); sides[1].multiplyScalar(10).add(a); if (edge.bondType === "=" || this.getRingbondType(vertexA, vertexB) === "=" || edge.isPartOfAromaticRing && this.bridgedRing) { let inRing = this.areVerticesInSameRing(vertexA, vertexB); let s = this.chooseSide(vertexA, vertexB, sides); if (inRing) { let lcr = this.getLargestOrAromaticCommonRing(vertexA, vertexB); let center = lcr.center; normals[0].multiplyScalar(that.opts.bondSpacing); normals[1].multiplyScalar(that.opts.bondSpacing); let line = null; if (center.sameSideAs(vertexA.position, vertexB.position, Vector2.add(a, normals[0]))) { line = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); } else { line = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); } line.shorten(this.opts.bondLength - this.opts.shortBondLength * this.opts.bondLength); if (edge.isPartOfAromaticRing) { this.canvasWrapper.drawLine(line, true); } else { this.canvasWrapper.drawLine(line); } this.canvasWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (edge.center || vertexA.isTerminal() && vertexB.isTerminal()) { normals[0].multiplyScalar(that.opts.halfBondSpacing); normals[1].multiplyScalar(that.opts.halfBondSpacing); let lineA = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); let lineB = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); this.canvasWrapper.drawLine(lineA); this.canvasWrapper.drawLine(lineB); } else if (s.anCount == 0 && s.bnCount > 1 || s.bnCount == 0 && s.anCount > 1) { normals[0].multiplyScalar(that.opts.halfBondSpacing); normals[1].multiplyScalar(that.opts.halfBondSpacing); let lineA = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); let lineB = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); this.canvasWrapper.drawLine(lineA); this.canvasWrapper.drawLine(lineB); } else if (s.sideCount[0] > s.sideCount[1]) { normals[0].multiplyScalar(that.opts.bondSpacing); normals[1].multiplyScalar(that.opts.bondSpacing); let line = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); line.shorten(this.opts.bondLength - this.opts.shortBondLength * this.opts.bondLength); this.canvasWrapper.drawLine(line); this.canvasWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (s.sideCount[0] < s.sideCount[1]) { normals[0].multiplyScalar(that.opts.bondSpacing); normals[1].multiplyScalar(that.opts.bondSpacing); let line = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); line.shorten(this.opts.bondLength - this.opts.shortBondLength * this.opts.bondLength); this.canvasWrapper.drawLine(line); this.canvasWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (s.totalSideCount[0] > s.totalSideCount[1]) { normals[0].multiplyScalar(that.opts.bondSpacing); normals[1].multiplyScalar(that.opts.bondSpacing); let line = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); line.shorten(this.opts.bondLength - this.opts.shortBondLength * this.opts.bondLength); this.canvasWrapper.drawLine(line); this.canvasWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (s.totalSideCount[0] <= s.totalSideCount[1]) { normals[0].multiplyScalar(that.opts.bondSpacing); normals[1].multiplyScalar(that.opts.bondSpacing); let line = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); line.shorten(this.opts.bondLength - this.opts.shortBondLength * this.opts.bondLength); this.canvasWrapper.drawLine(line); this.canvasWrapper.drawLine(new Line(a, b, elementA, elementB)); } else { } } else if (edge.bondType === "#") { normals[0].multiplyScalar(that.opts.bondSpacing / 1.5); normals[1].multiplyScalar(that.opts.bondSpacing / 1.5); let lineA = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); let lineB = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); this.canvasWrapper.drawLine(lineA); this.canvasWrapper.drawLine(lineB); this.canvasWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (edge.bondType === ".") { } else { let isChiralCenterA = vertexA.value.isStereoCenter; let isChiralCenterB = vertexB.value.isStereoCenter; if (edge.wedge === "up") { this.canvasWrapper.drawWedge(new Line(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB)); } else if (edge.wedge === "down") { this.canvasWrapper.drawDashedWedge(new Line(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB)); } else { this.canvasWrapper.drawLine(new Line(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB)); } } if (debug) { let midpoint = Vector2.midpoint(a, b); this.canvasWrapper.drawDebugText(midpoint.x, midpoint.y, "e: " + edgeId); } } /** * Draws the vertices representing atoms to the canvas. * * @param {Boolean} debug A boolean indicating whether or not to draw debug messages to the canvas. */ drawVertices(debug) { var i = this.graph.vertices.length; for (var i = 0; i < this.graph.vertices.length; i++) { let vertex = this.graph.vertices[i]; let atom = vertex.value; let charge = 0; let isotope = 0; let bondCount = vertex.value.bondCount; let element = atom.element; let hydrogens = Atom.maxBonds[element] - bondCount; let dir2 = vertex.getTextDirection(this.graph.vertices); let isTerminal = this.opts.terminalCarbons || element !== "C" || atom.hasAttachedPseudoElements ? vertex.isTerminal() : false; let isCarbon = atom.element === "C"; if (atom.element === "N" && atom.isPartOfAromaticRing) { hydrogens = 0; } if (atom.bracket) { hydrogens = atom.bracket.hcount; charge = atom.bracket.charge; isotope = atom.bracket.isotope; } if (this.opts.atomVisualization === "allballs") { this.canvasWrapper.drawBall(vertex.position.x, vertex.position.y, element); } else if (atom.isDrawn && (!isCarbon || atom.drawExplicit || isTerminal || atom.hasAttachedPseudoElements) || this.graph.vertices.length === 1) { if (this.opts.atomVisualization === "default") { this.canvasWrapper.drawText( vertex.position.x, vertex.position.y, element, hydrogens, dir2, isTerminal, charge, isotope, this.graph.vertices.length, atom.getAttachedPseudoElements() ); } else if (this.opts.atomVisualization === "balls") { this.canvasWrapper.drawBall(vertex.position.x, vertex.position.y, element); } } else if (vertex.getNeighbourCount() === 2 && vertex.forcePositioned == true) { let a = this.graph.vertices[vertex.neighbours[0]].position; let b = this.graph.vertices[vertex.neighbours[1]].position; let angle = Vector2.threePointangle(vertex.position, a, b); if (Math.abs(Math.PI - angle) < 0.1) { this.canvasWrapper.drawPoint(vertex.position.x, vertex.position.y, element); } } if (debug) { let value = "v: " + vertex.id + " " + ArrayHelper.print(atom.ringbonds); this.canvasWrapper.drawDebugText(vertex.position.x, vertex.position.y, value); } else { } } if (this.opts.debug) { for (var i = 0; i < this.rings.length; i++) { let center = this.rings[i].center; this.canvasWrapper.drawDebugPoint(center.x, center.y, "r: " + this.rings[i].id); } } } /** * Position the vertices according to their bonds and properties. */ position() { let startVertex = null; for (var i = 0; i < this.graph.vertices.length; i++) { if (this.graph.vertices[i].value.bridgedRing !== null) { startVertex = this.graph.vertices[i]; break; } } for (var i = 0; i < this.rings.length; i++) { if (this.rings[i].isBridged) { startVertex = this.graph.vertices[this.rings[i].members[0]]; } } if (this.rings.length > 0 && startVertex === null) { startVertex = this.graph.vertices[this.rings[0].members[0]]; } if (startVertex === null) { startVertex = this.graph.vertices[0]; } this.createNextBond(startVertex, null, 0); } /** * Stores the current information associated with rings. */ backupRingInformation() { this.originalRings = Array(); this.originalRingConnections = Array(); for (var i = 0; i < this.rings.length; i++) { this.originalRings.push(this.rings[i]); } for (var i = 0; i < this.ringConnections.length; i++) { this.originalRingConnections.push(this.ringConnections[i]); } for (var i = 0; i < this.graph.vertices.length; i++) { this.graph.vertices[i].value.backupRings(); } } /** * Restores the most recently backed up information associated with rings. */ restoreRingInformation() { let bridgedRings = this.getBridgedRings(); this.rings = Array(); this.ringConnections = Array(); for (var i = 0; i < bridgedRings.length; i++) { let bridgedRing = bridgedRings[i]; for (var j = 0; j < bridgedRing.rings.length; j++) { let ring = bridgedRing.rings[j]; this.originalRings[ring.id].center = ring.center; } } for (var i = 0; i < this.originalRings.length; i++) { this.rings.push(this.originalRings[i]); } for (var i = 0; i < this.originalRingConnections.length; i++) { this.ringConnections.push(this.originalRingConnections[i]); } for (var i = 0; i < this.graph.vertices.length; i++) { this.graph.vertices[i].value.restoreRings(); } } // TODO: This needs some cleaning up /** * Creates a new ring, that is, positiones all the vertices inside a ring. * * @param {Ring} ring The ring to position. * @param {(Vector2|null)} [center=null] The center of the ring to be created. * @param {(Vertex|null)} [startVertex=null] The first vertex to be positioned inside the ring. * @param {(Vertex|null)} [previousVertex=null] The last vertex that was positioned. * @param {Boolean} [previousVertex=false] A boolean indicating whether or not this ring was force positioned already - this is needed after force layouting a ring, in order to draw rings connected to it. */ createRing(ring, center = null, startVertex = null, previousVertex = null) { if (ring.positioned) { return; } center = center ? center : new Vector2(0, 0); let orderedNeighbours = ring.getOrderedNeighbours(this.ringConnections); let startingAngle = startVertex ? Vector2.subtract(startVertex.position, center).angle() : 0; let radius = MathHelper.polyCircumradius(this.opts.bondLength, ring.getSize()); let angle = MathHelper.centralAngle(ring.getSize()); ring.centralAngle = angle; let a = startingAngle; let that = this; let startVertexId = startVertex ? startVertex.id : null; if (ring.members.indexOf(startVertexId) === -1) { if (startVertex) { startVertex.positioned = false; } startVertexId = ring.members[0]; } if (ring.isBridged) { this.graph.kkLayout( ring.members.slice(), center, startVertex.id, ring, this.opts.bondLength, this.opts.kkThreshold, this.opts.kkInnerThreshold, this.opts.kkMaxIteration, this.opts.kkMaxInnerIteration, this.opts.kkMaxEnergy ); ring.positioned = true; this.setRingCenter(ring); center = ring.center; for (var i = 0; i < ring.rings.length; i++) { this.setRingCenter(ring.rings[i]); } } else { ring.eachMember(this.graph.vertices, function(v) { let vertex = that.graph.vertices[v]; if (!vertex.positioned) { vertex.setPosition(center.x + Math.cos(a) * radius, center.y + Math.sin(a) * radius); } a += angle; if (!ring.isBridged || ring.rings.length < 3) { vertex.angle = a; vertex.positioned = true; } }, startVertexId, previousVertex ? previousVertex.id : null); } ring.positioned = true; ring.center = center; for (var i = 0; i < orderedNeighbours.length; i++) { let neighbour = this.getRing(orderedNeighbours[i].neighbour); if (neighbour.positioned) { continue; } let vertices = RingConnection.getVertices(this.ringConnections, ring.id, neighbour.id); if (vertices.length === 2) { ring.isFused = true; neighbour.isFused = true; let vertexA = this.graph.vertices[vertices[0]]; let vertexB = this.graph.vertices[vertices[1]]; let midpoint = Vector2.midpoint(vertexA.position, vertexB.position); let normals = Vector2.normals(vertexA.position, vertexB.position); normals[0].normalize(); normals[1].normalize(); let r = MathHelper.polyCircumradius(this.opts.bondLength, neighbour.getSize()); let apothem = MathHelper.apothem(r, neighbour.getSize()); normals[0].multiplyScalar(apothem).add(midpoint); normals[1].multiplyScalar(apothem).add(midpoint); let nextCenter = normals[0]; if (Vector2.subtract(center, normals[1]).lengthSq() > Vector2.subtract(center, normals[0]).lengthSq()) { nextCenter = normals[1]; } let posA = Vector2.subtract(vertexA.position, nextCenter); let posB = Vector2.subtract(vertexB.position, nextCenter); if (posA.clockwise(posB) === -1) { if (!neighbour.positioned) { this.createRing(neighbour, nextCenter, vertexA, vertexB); } } else { if (!neighbour.positioned) { this.createRing(neighbour, nextCenter, vertexB, vertexA); } } } else if (vertices.length === 1) { ring.isSpiro = true; neighbour.isSpiro = true; let vertexA = this.graph.vertices[vertices[0]]; let nextCenter = Vector2.subtract(center, vertexA.position); nextCenter.invert(); nextCenter.normalize(); let r = MathHelper.polyCircumradius(this.opts.bondLength, neighbour.getSize()); nextCenter.multiplyScalar(r); nextCenter.add(vertexA.position); if (!neighbour.positioned) { this.createRing(neighbour, nextCenter, vertexA); } } } for (var i = 0; i < ring.members.length; i++) { let ringMember = this.graph.vertices[ring.members[i]]; let ringMemberNeighbours = ringMember.neighbours; for (var j = 0; j < ringMemberNeighbours.length; j++) { let v = this.graph.vertices[ringMemberNeighbours[j]]; if (v.positioned) { continue; } v.value.isConnectedToRing = true; this.createNextBond(v, ringMember, 0); } } } /** * Rotate an entire subtree by an angle around a center. * * @param {Number} vertexId A vertex id (the root of the sub-tree). * @param {Number} parentVertexId A vertex id in the previous direction of the subtree that is to rotate. * @param {Number} angle An angle in randians. * @param {Vector2} center The rotational center. */ rotateSubtree(vertexId, parentVertexId, angle, center) { let that = this; this.graph.traverseTree(vertexId, parentVertexId, function(vertex) { vertex.position.rotateAround(angle, center); for (var i = 0; i < vertex.value.anchoredRings.length; i++) { let ring = that.rings[vertex.value.anchoredRings[i]]; if (ring) { ring.center.rotateAround(angle, center); } } }); } /** * Gets the overlap score of a subtree. * * @param {Number} vertexId A vertex id (the root of the sub-tree). * @param {Number} parentVertexId A vertex id in the previous direction of the subtree. * @param {Number[]} vertexOverlapScores An array containing the vertex overlap scores indexed by vertex id. * @returns {Object} An object containing the total overlap score and the center of mass of the subtree weighted by overlap score { value: 0.2, center: new Vector2() }. */ getSubtreeOverlapScore(vertexId, parentVertexId, vertexOverlapScores) { let that = this; let score = 0; let center = new Vector2(0, 0); let count = 0; this.graph.traverseTree(vertexId, parentVertexId, function(vertex) { if (!vertex.value.isDrawn) { return; } let s = vertexOverlapScores[vertex.id]; if (s > that.opts.overlapSensitivity) { score += s; count++; } let position = that.graph.vertices[vertex.id].position.clone(); position.multiplyScalar(s); center.add(position); }); center.divide(score); return { value: score / count, center }; } /** * Returns the current (positioned vertices so far) center of mass. * * @returns {Vector2} The current center of mass. */ getCurrentCenterOfMass() { let total = new Vector2(0, 0); let count = 0; for (var i = 0; i < this.graph.vertices.length; i++) { let vertex = this.graph.vertices[i]; if (vertex.positioned) { total.add(vertex.position); count++; } } return total.divide(count); } /** * Returns the current (positioned vertices so far) center of mass in the neighbourhood of a given position. * * @param {Vector2} vec The point at which to look for neighbours. * @param {Number} [r=currentBondLength*2.0] The radius of vertices to include. * @returns {Vector2} The current center of mass. */ getCurrentCenterOfMassInNeigbourhood(vec, r = this.opts.bondLength * 2) { let total = new Vector2(0, 0); let count = 0; let rSq = r * r; for (var i = 0; i < this.graph.vertices.length; i++) { let vertex = this.graph.vertices[i]; if (vertex.positioned && vec.distanceSq(vertex.position) < rSq) { total.add(vertex.position); count++; } } return total.divide(count); } /** * Resolve primary (exact) overlaps, such as two vertices that are connected to the same ring vertex. */ resolvePrimaryOverlaps() { let overlaps = Array(); let done = Array(this.graph.vertices.length); for (var i = 0; i < this.rings.length; i++) { let ring = this.rings[i]; for (var j = 0; j < ring.members.length; j++) { let vertex = this.graph.vertices[ring.members[j]]; if (done[vertex.id]) { continue; } done[vertex.id] = true; let nonRingNeighbours = this.getNonRingNeighbours(vertex.id); if (nonRingNeighbours.length > 1) { let rings = Array(); for (var k = 0; k < vertex.value.rings.length; k++) { rings.push(vertex.value.rings[k]); } overlaps.push({ common: vertex, rings, vertices: nonRingNeighbours }); } else if (nonRingNeighbours.length === 1 && vertex.value.rings.length === 2) { let rings = Array(); for (var k = 0; k < vertex.value.rings.length; k++) { rings.push(vertex.value.rings[k]); } overlaps.push({ common: vertex, rings, vertices: nonRingNeighbours }); } } } for (var i = 0; i < overlaps.length; i++) { let overlap = overlaps[i]; if (overlap.vertices.length === 2) { let a = overlap.vertices[0]; let b = overlap.vertices[1]; if (!a.value.isDrawn || !b.value.isDrawn) { continue; } let angle = (2 * Math.PI - this.getRing(overlap.rings[0]).getAngle()) / 6; this.rotateSubtree(a.id, overlap.common.id, angle, overlap.common.position); this.rotateSubtree(b.id, overlap.common.id, -angle, overlap.common.position); let overlapScore = this.getOverlapScore(); let subTreeOverlapA = this.getSubtreeOverlapScore(a.id, overlap.common.id, overlapScore.vertexScores); let subTreeOverlapB = this.getSubtreeOverlapScore(b.id, overlap.common.id, overlapScore.vertexScores); let total = subTreeOverlapA.value + subTreeOverlapB.value; this.rotateSubtree(a.id, overlap.common.id, -2 * angle, overlap.common.position); this.rotateSubtree(b.id, overlap.common.id, 2 * angle, overlap.common.position); overlapScore = this.getOverlapScore(); subTreeOverlapA = this.getSubtreeOverlapScore(a.id, overlap.common.id, overlapScore.vertexScores); subTreeOverlapB = this.getSubtreeOverlapScore(b.id, overlap.common.id, overlapScore.vertexScores); if (subTreeOverlapA.value + subTreeOverlapB.value > total) { this.rotateSubtree(a.id, overlap.common.id, 2 * angle, overlap.common.position); this.rotateSubtree(b.id, overlap.common.id, -2 * angle, overlap.common.position); } } else if (overlap.vertices.length === 1) { if (overlap.rings.length === 2) { } } } } /** * Resolve secondary overlaps. Those overlaps are due to the structure turning back on itself. * * @param {Object[]} scores An array of objects sorted descending by score. * @param {Number} scores[].id A vertex id. * @param {Number} scores[].score The overlap score associated with the vertex id. */ resolveSecondaryOverlaps(scores) { for (var i = 0; i < scores.length; i++) { if (scores[i].score > this.opts.overlapSensitivity) { let vertex = this.graph.vertices[scores[i].id]; if (vertex.isTerminal()) { let closest = this.getClosestVertex(vertex); if (closest) { let closestPosition = null; if (closest.isTerminal()) { closestPosition = closest.id === 0 ? this.graph.vertices[1].position : closest.previousPosition; } else { closestPosition = closest.id === 0 ? this.graph.vertices[1].position : closest.position; } let vertexPreviousPosition = vertex.id === 0 ? this.graph.vertices[1].position : vertex.previousPosition; vertex.position.rotateAwayFrom(closestPosition, vertexPreviousPosition, MathHelper.toRad(20)); } } } } } /** * Get the last non-null or 0 angle vertex. * @param {Number} vertexId A vertex id. * @returns {Vertex} The last vertex with an angle that was not 0 or null. */ getLastVertexWithAngle(vertexId) { let angle = 0; let vertex = null; while (!angle && vertexId) { vertex = this.graph.vertices[vertexId]; angle = vertex.angle; vertexId = vertex.parentVertexId; } return vertex; } /** * Positiones the next vertex thus creating a bond. * * @param {Vertex} vertex A vertex. * @param {Vertex} [previousVertex=null] The previous vertex which has been positioned. * @param {Number} [angle=0.0] The (global) angle of the vertex. * @param {Boolean} [originShortest=false] Whether the origin is the shortest subtree in the branch. * @param {Boolean} [skipPositioning=false] Whether or not to skip positioning and just check the neighbours. */ createNextBond(vertex, previousVertex = null, angle = 0, originShortest = false, skipPositioning = false) { if (vertex.positioned && !skipPositioning) { return; } let doubleBondConfigSet = false; if (previousVertex) { let edge = this.graph.getEdge(vertex.id, previousVertex.id); if ((edge.bondType === "/" || edge.bondType === "\\") && ++this.doubleBondConfigCount % 2 === 1) { if (this.doubleBondConfig === null) { this.doubleBondConfig = edge.bondType; doubleBondConfigSet = true; if (previousVertex.parentVertexId === null && vertex.value.branchBond) { if (this.doubleBondConfig === "/") { this.doubleBondConfig = "\\"; } else if (this.doubleBondConfig === "\\") { this.doubleBondConfig = "/"; } } } } } if (!skipPositioning) { if (!previousVertex) { let dummy = new Vector2(this.opts.bondLength, 0); dummy.rotate(MathHelper.toRad(-60)); vertex.previousPosition = dummy; vertex.setPosition(this.opts.bondLength, 0); vertex.angle = MathHelper.toRad(-60); if (vertex.value.bridgedRing === null) { vertex.positioned = true; } } else if (previousVertex.value.rings.length > 0) { let neighbours = previousVertex.neighbours; let joinedVertex = null; let pos = new Vector2(0, 0); if (previousVertex.value.bridgedRing === null && previousVertex.value.rings.length > 1) { for (var i = 0; i < neighbours.length; i++) { let neighbour = this.graph.vertices[neighbours[i]]; if (ArrayHelper.containsAll(neighbour.value.rings, previousVertex.value.rings)) { joinedVertex = neighbour; break; } } } if (joinedVertex === null) { for (var i = 0; i < neighbours.length; i++) { let v = this.graph.vertices[neighbours[i]]; if (v.positioned && this.areVerticesInSameRing(v, previousVertex)) { pos.add(Vector2.subtract(v.position, previousVertex.position)); } } pos.invert().normalize().multiplyScalar(this.opts.bondLength).add(previousVertex.position); } else { pos = joinedVertex.position.clone().rotateAround(Math.PI, previousVertex.position); } vertex.previousPosition = previousVertex.position; vertex.setPositionFromVector(pos); vertex.positioned = true; } else { let v = new Vector2(this.opts.bondLength, 0); v.rotate(angle); v.add(previousVertex.position); vertex.setPositionFromVector(v); vertex.previousPosition = previousVertex.position; vertex.positioned = true; } } if (vertex.value.bridgedRing !== null) { let nextRing = this.getRing(vertex.value.bridgedRing); if (!nextRing.positioned) { let nextCenter = Vector2.subtract(vertex.previousPosition, vertex.position); nextCenter.invert(); nextCenter.normalize(); let r = MathHelper.polyCircumradius(this.opts.bondLength, nextRing.members.length); nextCenter.multiplyScalar(r); nextCenter.add(vertex.position); this.createRing(nextRing, nextCenter, vertex); } } else if (vertex.value.rings.length > 0) { let nextRing = this.getRing(vertex.value.rings[0]); if (!nextRing.positioned) { let nextCenter = Vector2.subtract(vertex.previousPosition, vertex.position); nextCenter.invert(); nextCenter.normalize(); let r = MathHelper.polyCircumradius(this.opts.bondLength, nextRing.getSize()); nextCenter.multiplyScalar(r); nextCenter.add(vertex.position); this.createRing(nextRing, nextCenter, vertex); } } else { let isStereoCenter = vertex.value.isStereoCenter; let tmpNeighbours = vertex.getNeighbours(); let neighbours = Array(); for (var i = 0; i < tmpNeighbours.length; i++) { if (this.graph.vertices[tmpNeighbours[i]].value.isDrawn) { neighbours.push(tmpNeighbours[i]); } } if (previousVertex) { neighbours = ArrayHelper.remove(neighbours, previousVertex.id); } let previousAngle = vertex.getAngle(); if (neighbours.length === 1) { let nextVertex = this.graph.vertices[neighbours[0]]; if (vertex.value.bondType === "#" || previousVertex && previousVertex.value.bondType === "#" || vertex.value.bondType === "=" && previousVertex && previousVertex.value.rings.length === 0 && previousVertex.value.bondType === "=" && vertex.value.branchBond !== "-") { vertex.value.drawExplicit = false; if (previousVertex) { let straightEdge1 = this.graph.getEdge(vertex.id, previousVertex.id); straightEdge1.center = true; } let straightEdge2 = this.graph.getEdge(vertex.id, nextVertex.id); straightEdge2.center = true; if (vertex.value.bondType === "#" || previousVertex && previousVertex.value.bondType === "#") { nextVertex.angle = 0; } nextVertex.drawExplicit = true; this.createNextBond(nextVertex, vertex, previousAngle + nextVertex.angle); } else if (previousVertex && previousVertex.value.rings.length > 0) { let proposedAngleA = MathHelper.toRad(60); let proposedAngleB = -proposedAngleA; let proposedVectorA = new Vector2(this.opts.bondLength, 0); let proposedVectorB = new Vector2(this.opts.bondLength, 0); proposedVectorA.rotate(proposedAngleA).add(vertex.position); proposedVectorB.rotate(proposedAngleB).add(vertex.position); let centerOfMass = this.getCurrentCenterOfMass(); let distanceA = proposedVectorA.distanceSq(centerOfMass); let distanceB = proposedVectorB.distanceSq(centerOfMass); nextVertex.angle = distanceA < distanceB ? proposedAngleB : proposedAngleA; this.createNextBond(nextVertex, vertex, previousAngle + nextVertex.angle); } else { let a = vertex.angle; if (previousVertex && previousVertex.neighbours.length > 3) { if (a > 0) { a = Math.min(1.0472, a); } else if (a < 0) { a = Math.max(-1.0472, a); } else { a = 1.0472; } } else if (!a) { let v = this.getLastVertexWithAngle(vertex.id); a = v.angle; if (!a) { a = 1.0472; } } if (previousVertex && !doubleBondConfigSet) { let bondType = this.graph.getEdge(vertex.id, nextVertex.id).bondType; if (bondType === "/") { if (this.doubleBondConfig === "/") { } else if (this.doubleBondConfig === "\\") { a = -a; } this.doubleBondConfig = null; } else if (bondType === "\\") { if (this.doubleBondConfig === "/") { a = -a; } else if (this.doubleBondConfig === "\\") { } this.doubleBondConfig = null; } } if (originShortest) { nextVertex.angle = a; } else { nextVertex.angle = -a; } this.createNextBond(nextVertex, vertex, previousAngle + nextVertex.angle); } } else if (neighbours.length === 2) { let a = vertex.angle; if (!a) { a = 1.0472; } let subTreeDepthA = this.graph.getTreeDepth(neighbours[0], vertex.id); let subTreeDepthB = this.graph.getTreeDepth(neighbours[1], vertex.id); let l = this.graph.vertices[neighbours[0]]; let r = this.graph.vertices[neighbours[1]]; l.value.subtreeDepth = subTreeDepthA; r.value.subtreeDepth = subTreeDepthB; let subTreeDepthC = this.graph.getTreeDepth(previousVertex ? previousVertex.id : null, vertex.id); if (previousVertex) { previousVertex.value.subtreeDepth = subTreeDepthC; } let cis = 0; let trans = 1; if (r.value.element === "C" && l.value.element !== "C" && subTreeDepthB > 1 && subTreeDepthA < 5) { cis = 1; trans = 0; } else if (r.value.element !== "C" && l.value.element === "C" && subTreeDepthA > 1 && subTreeDepthB < 5) { cis = 0; trans = 1; } else if (subTreeDepthB > subTreeDepthA) { cis = 1; trans = 0; } let cisVertex = this.graph.vertices[neighbours[cis]]; let transVertex = this.graph.vertices[neighbours[trans]]; let edgeCis = this.graph.getEdge(vertex.id, cisVertex.id); let edgeTrans = this.graph.getEdge(vertex.id, transVertex.id); let originShortest2 = false; if (subTreeDepthC < subTreeDepthA && subTreeDepthC < subTreeDepthB) { originShortest2 = true; } transVertex.angle = a; cisVertex.angle = -a; if (this.doubleBondConfig === "\\") { if (transVertex.value.branchBond === "\\") { transVertex.angle = -a; cisVertex.angle = a; } } else if (this.doubleBondConfig === "/") { if (transVertex.value.branchBond === "/") { transVertex.angle = -a; cisVertex.angle = a; } } this.createNextBond(transVertex, vertex, previousAngle + transVertex.angle, originShortest2); this.createNextBond(cisVertex, vertex, previousAngle + cisVertex.angle, originShortest2); } else if (neighbours.length === 3) { let d1 = this.graph.getTreeDepth(neighbours[0], vertex.id); let d2 = this.graph.getTreeDepth(neighbours[1], vertex.id); let d3 = this.graph.getTreeDepth(neighbours[2], vertex.id); let s = this.graph.vertices[neighbours[0]]; let l = this.graph.vertices[neighbours[1]]; let r = this.graph.vertices[neighbours[2]]; s.value.subtreeDepth = d1; l.value.subtreeDepth = d2; r.value.subtreeDepth = d3; if (d2 > d1 && d2 > d3) { s = this.graph.vertices[neighbours[1]]; l = this.graph.vertices[neighbours[0]]; r = this.graph.vertices[neighbours[2]]; } else if (d3 > d1 && d3 > d2) { s = this.graph.vertices[neighbours[2]]; l = this.graph.vertices[neighbours[0]]; r = this.graph.vertices[neighbours[1]]; } if (previousVertex && previousVertex.value.rings.length < 1 && s.value.rings.length < 1 && l.value.rings.length < 1 && r.value.rings.length < 1 && this.graph.getTreeDepth(l.id, vertex.id) === 1 && this.graph.getTreeDepth(r.id, vertex.id) === 1 && this.graph.getTreeDepth(s.id, vertex.id) > 1) { s.angle = -vertex.angle; if (vertex.angle >= 0) { l.angle = MathHelper.toRad(30); r.angle = MathHelper.toRad(90); } else { l.angle = -MathHelper.toRad(30); r.angle = -MathHelper.toRad(90); } this.createNextBond(s, vertex, previousAngle + s.angle); this.createNextBond(l, vertex, previousAngle + l.angle); this.createNextBond(r, vertex, previousAngle + r.angle); } else { s.angle = 0; l.angle = MathHelper.toRad(90); r.angle = -MathHelper.toRad(90); this.createNextBond(s, vertex, previousAngle + s.angle); this.createNextBond(l, vertex, previousAngle + l.angle); this.createNextBond(r, vertex, previousAngle + r.angle); } } else if (neighbours.length === 4) { let d1 = this.graph.getTreeDepth(neighbours[0], vertex.id); let d2 = this.graph.getTreeDepth(neighbours[1], vertex.id); let d3 = this.graph.getTreeDepth(neighbours[2], vertex.id); let d4 = this.graph.getTreeDepth(neighbours[3], vertex.id); let w = this.graph.vertices[neighbours[0]]; let x = this.graph.vertices[neighbours[1]]; let y = this.graph.vertices[neighbours[2]]; let z = this.graph.vertices[neighbours[3]]; w.value.subtreeDepth = d1; x.value.subtreeDepth = d2; y.value.subtreeDepth = d3; z.value.subtreeDepth = d4; if (d2 > d1 && d2 > d3 && d2 > d4) { w = this.graph.vertices[neighbours[1]]; x = this.graph.vertices[neighbours[0]]; y = this.graph.vertices[neighbours[2]]; z = this.graph.vertices[neighbours[3]]; } else if (d3 > d1 && d3 > d2 && d3 > d4) { w = this.graph.vertices[neighbours[2]]; x = this.graph.vertices[neighbours[0]]; y = this.graph.vertices[neighbours[1]]; z = this.graph.vertices[neighbours[3]]; } else if (d4 > d1 && d4 > d2 && d4 > d3) { w = this.graph.vertices[neighbours[3]]; x = this.graph.vertices[neighbours[0]]; y = this.graph.vertices[neighbours[1]]; z = this.graph.vertices[neighbours[2]]; } w.angle = -MathHelper.toRad(36); x.angle = MathHelper.toRad(36); y.angle = -MathHelper.toRad(108); z.angle = MathHelper.toRad(108); this.createNextBond(w, vertex, previousAngle + w.angle); this.createNextBond(x, vertex, previousAngle + x.angle); this.createNextBond(y, vertex, previousAngle + y.angle); this.createNextBond(z, vertex, previousAngle + z.angle); } } } /** * Gets the vetex sharing the edge that is the common bond of two rings. * * @param {Vertex} vertex A vertex. * @returns {(Number|null)} The id of a vertex sharing the edge that is the common bond of two rings with the vertex provided or null, if none. */ getCommonRingbondNeighbour(vertex) { let neighbours = vertex.neighbours; for (var i = 0; i < neighbours.length; i++) { let neighbour = this.graph.vertices[neighbours[i]]; if (ArrayHelper.containsAll(neighbour.value.rings, vertex.value.rings)) { return neighbour; } } return null; } /** * Check if a vector is inside any ring. * * @param {Vector2} vec A vector. * @returns {Boolean} A boolean indicating whether or not the point (vector) is inside any of the rings associated with the current molecule. */ isPointInRing(vec) { for (var i = 0; i < this.rings.length; i++) { let ring = this.rings[i]; if (!ring.positioned) { continue; } let radius = MathHelper.polyCircumradius(this.opts.bondLength, ring.getSize()); let radiusSq = radius * radius; if (vec.distanceSq(ring.center) < radiusSq) { return true; } } return false; } /** * Check whether or not an edge is part of a ring. * * @param {Edge} edge An edge. * @returns {Boolean} A boolean indicating whether or not the edge is part of a ring. */ isEdgeInRing(edge) { let source = this.graph.vertices[edge.sourceId]; let target = this.graph.vertices[edge.targetId]; return this.areVerticesInSameRing(source, target); } /** * Check whether or not an edge is rotatable. * * @param {Edge} edge An edge. * @returns {Boolean} A boolean indicating whether or not the edge is rotatable. */ isEdgeRotatable(edge) { let vertexA = this.graph.vertices[edge.sourceId]; let vertexB = this.graph.vertices[edge.targetId]; if (edge.bondType !== "-") { return false; } if (vertexA.isTerminal() || vertexB.isTerminal()) { return false; } if (vertexA.value.rings.length > 0 && vertexB.value.rings.length > 0 && this.areVerticesInSameRing(vertexA, vertexB)) { return false; } return true; } /** * Check whether or not a ring is an implicitly defined aromatic ring (lower case smiles). * * @param {Ring} ring A ring. * @returns {Boolean} A boolean indicating whether or not a ring is implicitly defined as aromatic. */ isRingAromatic(ring) { for (var i = 0; i < ring.members.length; i++) { let vertex = this.graph.vertices[ring.members[i]]; if (!vertex.value.isPartOfAromaticRing) { return false; } } return true; } /** * Get the normals of an edge. * * @param {Edge} edge An edge. * @returns {Vector2[]} An array containing two vectors, representing the normals. */ getEdgeNormals(edge) { let v1 = this.graph.vertices[edge.sourceId].position; let v2 = this.graph.vertices[edge.targetId].position; let normals = Vector2.units(v1, v2); return normals; } /** * Returns an array of vertices that are neighbouring a vertix but are not members of a ring (including bridges). * * @param {Number} vertexId A vertex id. * @returns {Vertex[]} An array of vertices. */ getNonRingNeighbours(vertexId) { let nrneighbours = Array(); let vertex = this.graph.vertices[vertexId]; let neighbours = vertex.neighbours; for (var i = 0; i < neighbours.length; i++) { let neighbour = this.graph.vertices[neighbours[i]]; let nIntersections = ArrayHelper.intersection(vertex.value.rings, neighbour.value.rings).length; if (nIntersections === 0 && neighbour.value.isBridge == false) { nrneighbours.push(neighbour); } } return nrneighbours; } /** * Annotaed stereochemistry information for visualization. */ annotateStereochemistry() { let maxDepth = 10; for (var i = 0; i < this.graph.vertices.length; i++) { let vertex = this.graph.vertices[i]; if (!vertex.value.isStereoCenter) { continue; } let neighbours = vertex.getNeighbours(); let nNeighbours = neighbours.length; let priorities = Array(nNeighbours); for (var j = 0; j < nNeighbours; j++) { let visited = new Uint8Array(this.graph.vertices.length); let priority = Array(Array()); visited[vertex.id] = 1; this.visitStereochemistry(neighbours[j], vertex.id, visited, priority, maxDepth, 0); for (var k = 0; k < priority.length; k++) { priority[k].sort(function(a, b) { return b - a; }); } priorities[j] = [j, priority]; } let maxLevels = 0; let maxEntries = 0; for (var j = 0; j < priorities.length; j++) { if (priorities[j][1].length > maxLevels) { maxLevels = priorities[j][1].length; } for (var k = 0; k < priorities[j][1].length; k++) { if (priorities[j][1][k].length > maxEntries) { maxEntries = priorities[j][1][k].length; } } } for (var j = 0; j < priorities.length; j++) { let diff = maxLevels - priorities[j][1].length; for (var k = 0; k < diff; k++) { priorities[j][1].push([]); } priorities[j][1].push([neighbours[j]]); for (var k = 0; k < priorities[j][1].length; k++) { let diff2 = maxEntries - priorities[j][1][k].length; for (var l = 0; l < diff2; l++) { priorities[j][1][k].push(0); } } } priorities.sort(function(a, b) { for (var j2 = 0; j2 < a[1].length; j2++) { for (var k2 = 0; k2 < a[1][j2].length; k2++) { if (a[1][j2][k2] > b[1][j2][k2]) { return -1; } else if (a[1][j2][k2] < b[1][j2][k2]) { return 1; } } } return 0; }); let order = new Uint8Array(nNeighbours); for (var j = 0; j < nNeighbours; j++) { order[j] = priorities[j][0]; vertex.value.priority = j; } let posA = this.graph.vertices[neighbours[order[0]]].position; let posB = this.graph.vertices[neighbours[order[1]]].position; let posC = this.graph.vertices[neighbours[order[2]]].position; let cwA = posA.relativeClockwise(posB, vertex.position); let cwB = posA.relativeClockwise(posC, vertex.position); let isCw = cwA === -1; let rotation = vertex.value.bracket.chirality === "@" ? -1 : 1; let rs = MathHelper.parityOfPermutation(order) * rotation === 1 ? "R" : "S"; let wedgeA = "down"; let wedgeB = "up"; if (isCw && rs !== "R" || !isCw && rs !== "S") { vertex.value.hydrogenDirection = "up"; wedgeA = "up"; wedgeB = "down"; } if (vertex.value.hasHydrogen) { this.graph.getEdge(vertex.id, neighbours[order[order.length - 1]]).wedge = wedgeA; } let wedgeOrder = new Array(neighbours.length - 1); let showHydrogen = vertex.value.rings.length > 1 && vertex.value.hasHydrogen; let offset = vertex.value.hasHydrogen ? 1 : 0; for (var j = 0; j < order.length - offset; j++) { wedgeOrder[j] = new Uint32Array(2); let neighbour = this.graph.vertices[neighbours[order[j]]]; wedgeOrder[j][0] += neighbour.value.isStereoCenter ? 0 : 1e5; wedgeOrder[j][0] += this.areVerticesInSameRing(neighbour, vertex) ? 0 : 1e4; wedgeOrder[j][0] += neighbour.value.isHeteroAtom() ? 1e3 : 0; wedgeOrder[j][0] -= neighbour.value.subtreeDepth === 0 ? 1e3 : 0; wedgeOrder[j][0] += 1e3 - neighbour.value.subtreeDepth; wedgeOrder[j][1] = neighbours[order[j]]; } wedgeOrder.sort(function(a, b) { if (a[0] > b[0]) { return -1; } else if (a[0] < b[0]) { return 1; } return 0; }); if (!showHydrogen) { let wedgeId = wedgeOrder[0][1]; if (vertex.value.hasHydrogen) { this.graph.getEdge(vertex.id, wedgeId).wedge = wedgeB; } else { let wedge = wedgeB; for (var j = order.length - 1; j >= 0; j--) { if (wedge === wedgeA) { wedge = wedgeB; } else { wedge = wedgeA; } if (neighbours[order[j]] === wedgeId) { break; } } this.graph.getEdge(vertex.id, wedgeId).wedge = wedge; } } vertex.value.chirality = rs; } } /** * * * @param {Number} vertexId The id of a vertex. * @param {(Number|null)} previousVertexId The id of the parent vertex of the vertex. * @param {Uint8Array} visited An array containing the visited flag for all vertices in the graph. * @param {Array} priority An array of arrays storing the atomic numbers for each level. * @param {Number} maxDepth The maximum depth. * @param {Number} depth The current depth. */ visitStereochemistry(vertexId, previousVertexId, visited, priority, maxDepth, depth, parentAtomicNumber = 0) { visited[vertexId] = 1; let vertex = this.graph.vertices[vertexId]; let atomicNumber = vertex.value.getAtomicNumber(); if (priority.length <= depth) { priority.push(Array()); } for (var i = 0; i < this.graph.getEdge(vertexId, previousVertexId).weight; i++) { priority[depth].push(parentAtomicNumber * 1e3 + atomicNumber); } let neighbours = this.graph.vertices[vertexId].neighbours; for (var i = 0; i < neighbours.length; i++) { if (visited[neighbours[i]] !== 1 && depth < maxDepth - 1) { this.visitStereochemistry(neighbours[i], vertexId, visited.slice(), priority, maxDepth, depth + 1, atomicNumber); } } if (depth < maxDepth - 1) { let bonds = 0; for (var i = 0; i < neighbours.length; i++) { bonds += this.graph.getEdge(vertexId, neighbours[i]).weight; } for (var i = 0; i < vertex.value.getMaxBonds() - bonds; i++) { if (priority.length <= depth + 1) { priority.push(Array()); } priority[depth + 1].push(atomicNumber * 1e3 + 1); } } } /** * Creates pseudo-elements (such as Et, Me, Ac, Bz, ...) at the position of the carbon sets * the involved atoms not to be displayed. */ initPseudoElements() { for (var i = 0; i < this.graph.vertices.length; i++) { const vertex = this.graph.vertices[i]; const neighbourIds = vertex.neighbours; let neighbours = Array(neighbourIds.length); for (var j = 0; j < neighbourIds.length; j++) { neighbours[j] = this.graph.vertices[neighbourIds[j]]; } if (vertex.getNeighbourCount() < 3 || vertex.value.rings.length > 0) { continue; } if (vertex.value.element === "P") { continue; } if (vertex.value.element === "C" && neighbours.length === 3 && neighbours[0].value.element === "N" && neighbours[1].value.element === "N" && neighbours[2].value.element === "N") { continue; } let heteroAtomCount = 0; let ctn = 0; for (var j = 0; j < neighbours.length; j++) { let neighbour = neighbours[j]; let neighbouringElement = neighbour.value.element; let neighbourCount = neighbour.getNeighbourCount(); if (neighbouringElement !== "C" && neighbouringElement !== "H" && neighbourCount === 1) { heteroAtomCount++; } if (neighbourCount > 1) { ctn++; } } if (ctn > 1 || heteroAtomCount < 2) { continue; } let previous = null; for (var j = 0; j < neighbours.length; j++) { let neighbour = neighbours[j]; if (neighbour.getNeighbourCount() > 1) { previous = neighbour; } } for (var j = 0; j < neighbours.length; j++) { let neighbour = neighbours[j]; if (neighbour.getNeighbourCount() > 1) { continue; } neighbour.value.isDrawn = false; let hydrogens = Atom.maxBonds[neighbour.value.element] - neighbour.value.bondCount; let charge = ""; if (neighbour.value.bracket) { hydrogens = neighbour.value.bracket.hcount; charge = neighbour.value.bracket.charge || 0; } vertex.value.attachPseudoElement(neighbour.value.element, previous ? previous.value.element : null, hydrogens, charge); } } for (var i = 0; i < this.graph.vertices.length; i++) { const vertex = this.graph.vertices[i]; const atom = vertex.value; const element = atom.element; if (element === "C" || element === "H" || !atom.isDrawn) { continue; } const neighbourIds = vertex.neighbours; let neighbours = Array(neighbourIds.length); for (var j = 0; j < neighbourIds.length; j++) { neighbours[j] = this.graph.vertices[neighbourIds[j]]; } for (var j = 0; j < neighbours.length; j++) { let neighbour = neighbours[j].value; if (!neighbour.hasAttachedPseudoElements || neighbour.getAttachedPseudoElementsCount() !== 2) { continue; } const pseudoElements = neighbour.getAttachedPseudoElements(); if (pseudoElements.hasOwnProperty("0O") && pseudoElements.hasOwnProperty("3C")) { neighbour.isDrawn = false; vertex.value.attachPseudoElement("Ac", "", 0); } } } } }; module2.exports = DrawerBase; } }); // node_modules/smiles-drawer/src/SvgWrapper.js var require_SvgWrapper = __commonJS({ "node_modules/smiles-drawer/src/SvgWrapper.js"(exports, module2) { var { getChargeText } = require_UtilityFunctions(); var Line = require_Line(); var Vector2 = require_Vector2(); var MathHelper = require_MathHelper(); function makeid(length) { var result = ""; var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var charactersLength = characters.length; for (var i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } var SvgWrapper = class { constructor(themeManager, target, options, clear = true) { if (typeof target === "string" || target instanceof String) { this.svg = document.getElementById(target); } else { this.svg = target; } this.container = null; this.opts = options; this.uid = makeid(5); this.gradientId = 0; this.backgroundItems = []; this.paths = []; this.vertices = []; this.gradients = []; this.highlights = []; this.drawingWidth = 0; this.drawingHeight = 0; this.halfBondThickness = this.opts.bondThickness / 2; this.themeManager = themeManager; this.maskElements = []; this.maxX = -Number.MAX_VALUE; this.maxY = -Number.MAX_VALUE; this.minX = Number.MAX_VALUE; this.minY = Number.MAX_VALUE; if (clear) { while (this.svg.firstChild) { this.svg.removeChild(this.svg.firstChild); } } this.style = document.createElementNS("http://www.w3.org/2000/svg", "style"); this.style.appendChild(document.createTextNode(` .element { font: ${this.opts.fontSizeLarge}pt ${this.opts.fontFamily}; } .sub { font: ${this.opts.fontSizeSmall}pt ${this.opts.fontFamily}; } `)); if (this.svg) { this.svg.appendChild(this.style); } else { this.container = document.createElementNS("http://www.w3.org/2000/svg", "g"); container.appendChild(this.style); } } constructSvg() { let defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"), masks = document.createElementNS("http://www.w3.org/2000/svg", "mask"), background = document.createElementNS("http://www.w3.org/2000/svg", "g"), highlights = document.createElementNS("http://www.w3.org/2000/svg", "g"), paths = document.createElementNS("http://www.w3.org/2000/svg", "g"), vertices = document.createElementNS("http://www.w3.org/2000/svg", "g"), pathChildNodes = this.paths; let mask = document.createElementNS("http://www.w3.org/2000/svg", "rect"); mask.setAttributeNS(null, "x", this.minX); mask.setAttributeNS(null, "y", this.minY); mask.setAttributeNS(null, "width", this.maxX - this.minX); mask.setAttributeNS(null, "height", this.maxY - this.minY); mask.setAttributeNS(null, "fill", "white"); masks.appendChild(mask); masks.setAttributeNS(null, "id", this.uid + "-text-mask"); for (let path of pathChildNodes) { paths.appendChild(path); } for (let backgroundItem of this.backgroundItems) { background.appendChild(backgroundItem); } for (let highlight of this.highlights) { highlights.appendChild(highlight); } for (let vertex of this.vertices) { vertices.appendChild(vertex); } for (let mask2 of this.maskElements) { masks.appendChild(mask2); } for (let gradient of this.gradients) { defs.appendChild(gradient); } paths.setAttributeNS(null, "mask", "url(#" + this.uid + "-text-mask)"); this.updateViewbox(this.opts.scale); background.setAttributeNS(null, "style", `transform: translateX(${this.minX}px) translateY(${this.minY}px)`); if (this.svg) { this.svg.appendChild(defs); this.svg.appendChild(masks); this.svg.appendChild(background); this.svg.appendChild(highlights); this.svg.appendChild(paths); this.svg.appendChild(vertices); } else { this.container.appendChild(defs); this.container.appendChild(masks); this.container.appendChild(background); this.container.appendChild(paths); this.container.appendChild(vertices); return this.container; } } /** * Add a background to the svg. */ addLayer(svg) { this.backgroundItems.push(svg.firstChild); } /** * Create a linear gradient to apply to a line * * @param {Line} line the line to apply the gradiation to. */ createGradient(line) { let gradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient"), gradientUrl = this.uid + `-line-${this.gradientId++}`, l = line.getLeftVector(), r = line.getRightVector(), fromX = l.x, fromY = l.y, toX = r.x, toY = r.y; gradient.setAttributeNS(null, "id", gradientUrl); gradient.setAttributeNS(null, "gradientUnits", "userSpaceOnUse"); gradient.setAttributeNS(null, "x1", fromX); gradient.setAttributeNS(null, "y1", fromY); gradient.setAttributeNS(null, "x2", toX); gradient.setAttributeNS(null, "y2", toY); let firstStop = document.createElementNS("http://www.w3.org/2000/svg", "stop"); firstStop.setAttributeNS(null, "stop-color", this.themeManager.getColor(line.getLeftElement()) || this.themeManager.getColor("C")); firstStop.setAttributeNS(null, "offset", "20%"); let secondStop = document.createElementNS("http://www.w3.org/2000/svg", "stop"); secondStop.setAttributeNS(null, "stop-color", this.themeManager.getColor(line.getRightElement() || this.themeManager.getColor("C"))); secondStop.setAttributeNS(null, "offset", "100%"); gradient.appendChild(firstStop); gradient.appendChild(secondStop); this.gradients.push(gradient); return gradientUrl; } /** * Create a tspan element for sub or super scripts that styles the text * appropriately as one of those text types. * * @param {String} text the actual text * @param {String} shift the type of text, either 'sub', or 'super' */ createSubSuperScripts(text, shift) { let elem = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); elem.setAttributeNS(null, "baseline-shift", shift); elem.appendChild(document.createTextNode(text)); elem.setAttributeNS(null, "class", "sub"); return elem; } static createUnicodeCharge(n) { if (n === 1) { return "\u207A"; } if (n === -1) { return "\u207B"; } if (n > 1) { return SvgWrapper.createUnicodeSuperscript(n) + "\u207A"; } if (n < -1) { return SvgWrapper.createUnicodeSuperscript(n) + "\u207B"; } return ""; } /** * Determine drawing dimensiosn based on vertex positions. * * @param {Vertex[]} vertices An array of vertices containing the vertices associated with the current molecule. */ determineDimensions(vertices) { for (var i = 0; i < vertices.length; i++) { if (!vertices[i].value.isDrawn) { continue; } let p = vertices[i].position; if (this.maxX < p.x) this.maxX = p.x; if (this.maxY < p.y) this.maxY = p.y; if (this.minX > p.x) this.minX = p.x; if (this.minY > p.y) this.minY = p.y; } let padding = this.opts.padding; this.maxX += padding; this.maxY += padding; this.minX -= padding; this.minY -= padding; this.drawingWidth = this.maxX - this.minX; this.drawingHeight = this.maxY - this.minY; } updateViewbox(scale) { let x = this.minX; let y = this.minY; let width = this.maxX - this.minX; let height = this.maxY - this.minY; if (scale <= 0) { if (width > height) { let diff = width - height; height = width; y -= diff / 2; } else { let diff = height - width; width = height; x -= diff / 2; } } else { if (this.svg) { this.svg.style.width = scale * width + "px"; this.svg.style.height = scale * height + "px"; } } this.svg.setAttributeNS(null, "viewBox", `${x} ${y} ${width} ${height}`); } /** * Draw an svg ellipse as a ball. * * @param {Number} x The x position of the text. * @param {Number} y The y position of the text. * @param {String} elementName The name of the element (single-letter). */ drawBall(x, y, elementName) { let r = this.opts.bondLength / 4.5; if (x - r < this.minX) { this.minX = x - r; } if (x + r > this.maxX) { this.maxX = x + r; } if (y - r < this.minY) { this.minY = y - r; } if (y + r > this.maxY) { this.maxY = y + r; } let ball = document.createElementNS("http://www.w3.org/2000/svg", "circle"); ball.setAttributeNS(null, "cx", x); ball.setAttributeNS(null, "cy", y); ball.setAttributeNS(null, "r", r); ball.setAttributeNS(null, "fill", this.themeManager.getColor(elementName)); this.vertices.push(ball); } /** * @param {Line} line the line object to create the wedge from */ drawWedge(line) { let l = line.getLeftVector().clone(), r = line.getRightVector().clone(); let normals = Vector2.normals(l, r); normals[0].normalize(); normals[1].normalize(); let isRightChiralCenter = line.getRightChiral(); let start = l, end = r; if (isRightChiralCenter) { start = r; end = l; } let t2 = Vector2.add(start, Vector2.multiplyScalar(normals[0], this.halfBondThickness)), u = Vector2.add(end, Vector2.multiplyScalar(normals[0], 3 + this.opts.fontSizeLarge / 4)), v = Vector2.add(end, Vector2.multiplyScalar(normals[1], 3 + this.opts.fontSizeLarge / 4)), w = Vector2.add(start, Vector2.multiplyScalar(normals[1], this.halfBondThickness)); let polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon"), gradient = this.createGradient(line, l.x, l.y, r.x, r.y); polygon.setAttributeNS(null, "points", `${t2.x},${t2.y} ${u.x},${u.y} ${v.x},${v.y} ${w.x},${w.y}`); polygon.setAttributeNS(null, "fill", `url('#${gradient}')`); this.paths.push(polygon); } /* Draw a highlight for an atom * * @param {Number} x The x position of the highlight * @param {Number} y The y position of the highlight * @param {string} color The color of the highlight, default #03fc9d */ drawAtomHighlight(x, y, color = "#03fc9d") { let ball = document.createElementNS("http://www.w3.org/2000/svg", "circle"); ball.setAttributeNS(null, "cx", x); ball.setAttributeNS(null, "cy", y); ball.setAttributeNS(null, "r", this.opts.bondLength / 3); ball.setAttributeNS(null, "fill", color); this.highlights.push(ball); } /** * Draw a dashed wedge on the canvas. * * @param {Line} line A line. */ drawDashedWedge(line) { if (isNaN(line.from.x) || isNaN(line.from.y) || isNaN(line.to.x) || isNaN(line.to.y)) { return; } let l = line.getLeftVector().clone(), r = line.getRightVector().clone(), normals = Vector2.normals(l, r); normals[0].normalize(); normals[1].normalize(); let isRightChiralCenter = line.getRightChiral(), start, end; if (isRightChiralCenter) { start = r; end = l; } else { start = l; end = r; } let dir2 = Vector2.subtract(end, start).normalize(), length = line.getLength(), step = 1.25 / (length / (this.opts.bondLength / 10)), changed = false; let gradient = this.createGradient(line); for (let t2 = 0; t2 < 1; t2 += step) { let to = Vector2.multiplyScalar(dir2, t2 * length), startDash = Vector2.add(start, to), width = this.opts.fontSizeLarge / 2 * t2, dashOffset = Vector2.multiplyScalar(normals[0], width); startDash.subtract(dashOffset); let endDash = startDash.clone(); endDash.add(Vector2.multiplyScalar(dashOffset, 2)); this.drawLine(new Line(startDash, endDash), null, gradient); } } /** * Draws a debug dot at a given coordinate and adds text. * * @param {Number} x The x coordinate. * @param {Number} y The y coordindate. * @param {String} [debugText=''] A string. * @param {String} [color='#f00'] A color in hex form. */ drawDebugPoint(x, y, debugText = "", color = "#f00") { let point = document.createElementNS("http://www.w3.org/2000/svg", "circle"); point.setAttributeNS(null, "cx", x); point.setAttributeNS(null, "cy", y); point.setAttributeNS(null, "r", "2"); point.setAttributeNS(null, "fill", "#f00"); this.vertices.push(point); this.drawDebugText(x, y, debugText); } /** * Draws a debug text message at a given position * * @param {Number} x The x coordinate. * @param {Number} y The y coordinate. * @param {String} text The debug text. */ drawDebugText(x, y, text) { let textElem = document.createElementNS("http://www.w3.org/2000/svg", "text"); textElem.setAttributeNS(null, "x", x); textElem.setAttributeNS(null, "y", y); textElem.setAttributeNS(null, "class", "debug"); textElem.setAttributeNS(null, "fill", "#ff0000"); textElem.setAttributeNS(null, "style", ` font: 5px Droid Sans, sans-serif; `); textElem.appendChild(document.createTextNode(text)); this.vertices.push(textElem); } /** * Draws a ring. * * @param {x} x The x coordinate of the ring. * @param {y} r The y coordinate of the ring. * @param {s} s The size of the ring. */ drawRing(x, y, s) { let circleElem = document.createElementNS("http://www.w3.org/2000/svg", "circle"); let radius = MathHelper.apothemFromSideLength(this.opts.bondLength, s); circleElem.setAttributeNS(null, "cx", x); circleElem.setAttributeNS(null, "cy", y); circleElem.setAttributeNS(null, "r", radius - this.opts.bondSpacing); circleElem.setAttributeNS(null, "stroke", this.themeManager.getColor("C")); circleElem.setAttributeNS(null, "stroke-width", this.opts.bondThickness); circleElem.setAttributeNS(null, "fill", "none"); this.paths.push(circleElem); } /** * Draws a line. * * @param {Line} line A line. * @param {Boolean} dashed defaults to false. * @param {String} gradient gradient url. Defaults to null. */ drawLine(line, dashed = false, gradient = null, linecap = "round") { let opts = this.opts, stylesArr = [ ["stroke-width", this.opts.bondThickness], ["stroke-linecap", linecap], ["stroke-dasharray", dashed ? "5, 5" : "none"] ], l = line.getLeftVector(), r = line.getRightVector(), fromX = l.x, fromY = l.y, toX = r.x, toY = r.y; let styles = stylesArr.map((sub) => sub.join(":")).join(";"), lineElem = document.createElementNS("http://www.w3.org/2000/svg", "line"); lineElem.setAttributeNS(null, "x1", fromX); lineElem.setAttributeNS(null, "y1", fromY); lineElem.setAttributeNS(null, "x2", toX); lineElem.setAttributeNS(null, "y2", toY); lineElem.setAttributeNS(null, "style", styles); this.paths.push(lineElem); if (gradient == null) { gradient = this.createGradient(line, fromX, fromY, toX, toY); } lineElem.setAttributeNS(null, "stroke", `url('#${gradient}')`); } /** * Draw a point. * * @param {Number} x The x position of the point. * @param {Number} y The y position of the point. * @param {String} elementName The name of the element (single-letter). */ drawPoint(x, y, elementName) { let r = 0.75; if (x - r < this.minX) { this.minX = x - r; } if (x + r > this.maxX) { this.maxX = x + r; } if (y - r < this.minY) { this.minY = y - r; } if (y + r > this.maxY) { this.maxY = y + r; } let mask = document.createElementNS("http://www.w3.org/2000/svg", "circle"); mask.setAttributeNS(null, "cx", x); mask.setAttributeNS(null, "cy", y); mask.setAttributeNS(null, "r", "1.5"); mask.setAttributeNS(null, "fill", "black"); this.maskElements.push(mask); let point = document.createElementNS("http://www.w3.org/2000/svg", "circle"); point.setAttributeNS(null, "cx", x); point.setAttributeNS(null, "cy", y); point.setAttributeNS(null, "r", r); point.setAttributeNS(null, "fill", this.themeManager.getColor(elementName)); this.vertices.push(point); } /** * Draw a text to the canvas. * * @param {Number} x The x position of the text. * @param {Number} y The y position of the text. * @param {String} elementName The name of the element (single-letter). * @param {Number} hydrogens The number of hydrogen atoms. * @param {String} direction The direction of the text in relation to the associated vertex. * @param {Boolean} isTerminal A boolean indicating whether or not the vertex is terminal. * @param {Number} charge The charge of the atom. * @param {Number} isotope The isotope number. * @param {Number} totalVertices The total number of vertices in the graph. * @param {Object} attachedPseudoElement A map with containing information for pseudo elements or concatinated elements. The key is comprised of the element symbol and the hydrogen count. * @param {String} attachedPseudoElement.element The element symbol. * @param {Number} attachedPseudoElement.count The number of occurences that match the key. * @param {Number} attachedPseudoElement.hyrogenCount The number of hydrogens attached to each atom matching the key. */ drawText(x, y, elementName, hydrogens, direction, isTerminal, charge, isotope, totalVertices, attachedPseudoElement = {}) { let text = []; let display = elementName; if (charge !== 0 && charge !== null) { display += SvgWrapper.createUnicodeCharge(charge); } if (isotope !== 0 && isotope !== null) { display = SvgWrapper.createUnicodeSuperscript(isotope) + display; } text.push([display, elementName]); if (hydrogens === 1) { text.push(["H", "H"]); } else if (hydrogens > 1) { text.push(["H" + SvgWrapper.createUnicodeSubscript(hydrogens), "H"]); } if (charge === 1 && elementName === "N" && attachedPseudoElement.hasOwnProperty("0O") && attachedPseudoElement.hasOwnProperty("0O-1")) { attachedPseudoElement = { "0O": { element: "O", count: 2, hydrogenCount: 0, previousElement: "C", charge: "" } }; charge = 0; } for (let key in attachedPseudoElement) { if (!attachedPseudoElement.hasOwnProperty(key)) { continue; } let pe = attachedPseudoElement[key]; let display2 = pe.element; if (pe.count > 1) { display2 += SvgWrapper.createUnicodeSubscript(pe.count); } if (pe.charge !== "") { display2 += SvgWrapper.createUnicodeCharge(charge); } text.push([display2, pe.element]); if (pe.hydrogenCount === 1) { text.push(["H", "H"]); } else if (pe.hydrogenCount > 1) { text.push(["H" + SvgWrapper.createUnicodeSubscript(pe.hydrogenCount), "H"]); } } this.write(text, direction, x, y, totalVertices === 1); } write(text, direction, x, y, singleVertex) { let bbox = SvgWrapper.measureText(text[0][1], this.opts.fontSizeLarge, this.opts.fontFamily); if (direction === "left" && text[0][0] !== text[0][1]) { bbox.width *= 2; } if (singleVertex) { if (x + bbox.width * text.length > this.maxX) { this.maxX = x + bbox.width * text.length; } if (x - bbox.width / 2 < this.minX) { this.minX = x - bbox.width / 2; } if (y - bbox.height < this.minY) { this.minY = y - bbox.height; } if (y + bbox.height > this.maxY) { this.maxY = y + bbox.height; } } else { if (direction !== "right") { if (x + bbox.width * text.length > this.maxX) { this.maxX = x + bbox.width * text.length; } if (x - bbox.width * text.length < this.minX) { this.minX = x - bbox.width * text.length; } } else if (direction !== "left") { if (x + bbox.width * text.length > this.maxX) { this.maxX = x + bbox.width * text.length; } if (x - bbox.width / 2 < this.minX) { this.minX = x - bbox.width / 2; } } if (y - bbox.height < this.minY) { this.minY = y - bbox.height; } if (y + bbox.height > this.maxY) { this.maxY = y + bbox.height; } if (direction === "down") { if (y + 0.8 * bbox.height * text.length > this.maxY) { this.maxY = y + 0.8 * bbox.height * text.length; } } if (direction === "up") { if (y - 0.8 * bbox.height * text.length < this.minY) { this.minY = y - 0.8 * bbox.height * text.length; } } } let cx = x; let cy = y; let textElem = document.createElementNS("http://www.w3.org/2000/svg", "text"); textElem.setAttributeNS(null, "class", "element"); let g = document.createElementNS("http://www.w3.org/2000/svg", "g"); textElem.setAttributeNS(null, "fill", "#ffffff"); if (direction === "left") { text = text.reverse(); } if (direction === "right" || direction === "down" || direction === "up") { x -= bbox.width / 2; } if (direction === "left") { x += bbox.width / 2; } text.forEach((part, i) => { const display = part[0]; const elementName = part[1]; let tspanElem = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); tspanElem.setAttributeNS(null, "fill", this.themeManager.getColor(elementName)); tspanElem.textContent = display; if (direction === "up" || direction === "down") { tspanElem.setAttributeNS(null, "x", "0px"); if (direction === "up") { tspanElem.setAttributeNS(null, "y", `-${0.9 * i}em`); } else { tspanElem.setAttributeNS(null, "y", `${0.9 * i}em`); } } textElem.appendChild(tspanElem); }); textElem.setAttributeNS(null, "data-direction", direction); if (direction === "left" || direction === "right") { textElem.setAttributeNS(null, "dominant-baseline", "alphabetic"); textElem.setAttributeNS(null, "y", "0.36em"); } else { textElem.setAttributeNS(null, "dominant-baseline", "central"); } if (direction === "left") { textElem.setAttributeNS(null, "text-anchor", "end"); } g.appendChild(textElem); g.setAttributeNS(null, "style", `transform: translateX(${x}px) translateY(${y}px)`); let maskRadius = this.opts.fontSizeLarge * 0.75; if (text[0][1].length > 1) { maskRadius = this.opts.fontSizeLarge * 1.1; } let mask = document.createElementNS("http://www.w3.org/2000/svg", "circle"); mask.setAttributeNS(null, "cx", cx); mask.setAttributeNS(null, "cy", cy); mask.setAttributeNS(null, "r", maskRadius); mask.setAttributeNS(null, "fill", "black"); this.maskElements.push(mask); this.vertices.push(g); } /** * Draw the wrapped SVG to a canvas. * @param {HTMLCanvasElement} canvas The canvas element to draw the svg to. */ toCanvas(canvas, width, height) { if (typeof canvas === "string" || canvas instanceof String) { canvas = document.getElementById(canvas); } let image = new Image(); image.onload = function() { canvas.width = width; canvas.height = height; canvas.getContext("2d").drawImage(image, 0, 0, width, height); }; image.src = "data:image/svg+xml;charset-utf-8," + encodeURIComponent(this.svg.outerHTML); } static createUnicodeSubscript(n) { let result = ""; n.toString().split("").forEach((d) => { result += ["\u2080", "\u2081", "\u2082", "\u2083", "\u2084", "\u2085", "\u2086", "\u2087", "\u2088", "\u2089"][parseInt(d)]; }); return result; } static createUnicodeSuperscript(n) { let result = ""; n.toString().split("").forEach((d) => { let parsed = parseInt(d); if (parsed) { result += ["\u2070", "\xB9", "\xB2", "\xB3", "\u2074", "\u2075", "\u2076", "\u2077", "\u2078", "\u2079"][parseInt(d)]; } }); return result; } static replaceNumbersWithSubscript(text) { let subscriptNumbers = { "0": "\u2080", "1": "\u2081", "2": "\u2082", "3": "\u2083", "4": "\u2084", "5": "\u2085", "6": "\u2086", "7": "\u2087", "8": "\u2088", "9": "\u2089" }; for (const [key, value] of Object.entries(subscriptNumbers)) { text = text.replaceAll(key, value); } return text; } static measureText(text, fontSize, fontFamily, lineHeight = 0.9) { const element = document.createElement("canvas"); const ctx = element.getContext("2d"); ctx.font = `${fontSize}pt ${fontFamily}`; let textMetrics = ctx.measureText(text); let compWidth = Math.abs(textMetrics.actualBoundingBoxLeft) + Math.abs(textMetrics.actualBoundingBoxRight); return { "width": textMetrics.width > compWidth ? textMetrics.width : compWidth, "height": (Math.abs(textMetrics.actualBoundingBoxAscent) + Math.abs(textMetrics.actualBoundingBoxAscent)) * lineHeight }; } /** * Convert an SVG to a canvas. Warning: This happens async! * * @param {SVGElement} svg * @param {HTMLCanvasElement} canvas * @param {Number} width * @param {Number} height * @param {CallableFunction} callback * @returns {HTMLCanvasElement} The input html canvas element after drawing to. */ static svgToCanvas(svg, canvas, width, height, callback = null) { svg.setAttributeNS(null, "width", width); svg.setAttributeNS(null, "height", height); let image = new Image(); image.onload = function() { canvas.width = width; canvas.height = height; let context = canvas.getContext("2d"); context.imageSmoothingEnabled = false; context.drawImage(image, 0, 0, width, height); if (callback) { callback(canvas); } }; image.onerror = function(err) { console.log(err); }; image.src = "data:image/svg+xml;charset-utf-8," + encodeURIComponent(svg.outerHTML); return canvas; } /** * Convert an SVG to a canvas. Warning: This happens async! * * @param {SVGElement} svg * @param {HTMLImageElement} canvas * @param {Number} width * @param {Number} height */ static svgToImg(svg, img, width, height) { let canvas = document.createElement("canvas"); this.svgToCanvas(svg, canvas, width, height, (result) => { img.src = canvas.toDataURL("image/png"); }); } /** * Create an SVG element containing text. * @param {String} text * @param {*} themeManager * @param {*} options * @returns {{svg: SVGElement, width: Number, height: Number}} The SVG element containing the text and its dimensions. */ static writeText(text, themeManager, fontSize, fontFamily, maxWidth = Number.MAX_SAFE_INTEGER) { let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); let style = document.createElementNS("http://www.w3.org/2000/svg", "style"); style.appendChild(document.createTextNode(` .text { font: ${fontSize}pt ${fontFamily}; dominant-baseline: ideographic; } `)); svg.appendChild(style); let textElem = document.createElementNS("http://www.w3.org/2000/svg", "text"); textElem.setAttributeNS(null, "class", "text"); let maxLineWidth = 0; let totalHeight = 0; let lines = []; text.split("\n").forEach((line) => { let dims = SvgWrapper.measureText(line, fontSize, fontFamily, 1); if (dims.width >= maxWidth) { let totalWordsWidth = 0; let maxWordsHeight = 0; let words = line.split(" "); let offset = 0; for (let i = 0; i < words.length; i++) { let wordDims = SvgWrapper.measureText(words[i], fontSize, fontFamily, 1); if (totalWordsWidth + wordDims.width > maxWidth) { lines.push({ text: words.slice(offset, i).join(" "), width: totalWordsWidth, height: maxWordsHeight }); totalWordsWidth = 0; maxWordsHeight = 0; offset = i; } if (wordDims.height > maxWordsHeight) { maxWordsHeight = wordDims.height; } totalWordsWidth += wordDims.width; } if (offset < words.length) { lines.push({ text: words.slice(offset, words.length).join(" "), width: totalWordsWidth, height: maxWordsHeight }); } } else { lines.push({ text: line, width: dims.width, height: dims.height }); } }); lines.forEach((line, i) => { totalHeight += line.height; let tspanElem = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); tspanElem.setAttributeNS(null, "fill", themeManager.getColor("C")); tspanElem.textContent = line.text; tspanElem.setAttributeNS(null, "x", "0px"); tspanElem.setAttributeNS(null, "y", `${totalHeight}px`); textElem.appendChild(tspanElem); if (line.width > maxLineWidth) { maxLineWidth = line.width; } }); svg.appendChild(textElem); return { svg, width: maxLineWidth, height: totalHeight }; } }; module2.exports = SvgWrapper; } }); // node_modules/smiles-drawer/src/PixelsToSvg.js var require_PixelsToSvg = __commonJS({ "node_modules/smiles-drawer/src/PixelsToSvg.js"(exports, module2) { function convertImage(img) { "use strict"; function each(obj, fn) { var length = obj.length, likeArray = length === 0 || length > 0 && length - 1 in obj, i = 0; if (likeArray) { for (; i < length; i++) { if (fn.call(obj[i], i, obj[i]) === false) { break; } } } else { for (i in obj) { if (fn.call(obj[i], i, obj[i]) === false) { break; } } } } function componentToHex(c) { var hex = parseInt(c).toString(16); return hex.length == 1 ? "0" + hex : hex; } function getColor(r, g, b, a) { a = parseInt(a); if (a === void 0 || a === 255) { return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); } if (a === 0) { return false; } return "rgba(" + r + "," + g + "," + b + "," + a / 255 + ")"; } function makePathData(x, y, w) { return "M" + x + " " + y + "h" + w; } function makePath(color, data) { return '\n'; } function colorsToPaths(colors2) { var output2 = ""; each(colors2, function(color, values) { var orig = color; color = getColor.apply(null, color.split(",")); if (color === false) { return; } var paths2 = []; var curPath; var w = 1; each(values, function() { if (curPath && this[1] === curPath[1] && this[0] === curPath[0] + w) { w++; } else { if (curPath) { paths2.push(makePathData(curPath[0], curPath[1], w)); w = 1; } curPath = this; } }); paths2.push(makePathData(curPath[0], curPath[1], w)); output2 += makePath(color, paths2.join("")); }); return output2; } var getColors = function(img2) { var colors2 = {}, data = img2.data, len = data.length, w = img2.width, h = img2.height, x = 0, y = 0, i = 0, color; for (; i < len; i += 4) { if (data[i + 3] > 0) { color = data[i] + "," + data[i + 1] + "," + data[i + 2] + "," + data[i + 3]; colors2[color] = colors2[color] || []; x = i / 4 % w; y = Math.floor(i / 4 / w); colors2[color].push([x, y]); } } return colors2; }; let colors = getColors(img); let paths = colorsToPaths(colors); let output = '' + paths + ""; var dummyDiv = document.createElement("div"); dummyDiv.innerHTML = output; return dummyDiv.firstChild; } module2.exports = convertImage; } }); // node_modules/chroma-js/dist/chroma.cjs var require_chroma = __commonJS({ "node_modules/chroma-js/dist/chroma.cjs"(exports, module2) { (function(global2, factory) { typeof exports === "object" && typeof module2 !== "undefined" ? module2.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, global2.chroma = factory()); })(exports, function() { "use strict"; function limit(x, low, high) { if (low === void 0) low = 0; if (high === void 0) high = 1; return min$3(max$3(low, x), high); } function clip_rgb(rgb2) { rgb2._clipped = false; rgb2._unclipped = rgb2.slice(0); for (var i2 = 0; i2 <= 3; i2++) { if (i2 < 3) { if (rgb2[i2] < 0 || rgb2[i2] > 255) { rgb2._clipped = true; } rgb2[i2] = limit(rgb2[i2], 0, 255); } else if (i2 === 3) { rgb2[i2] = limit(rgb2[i2], 0, 1); } } return rgb2; } var classToType = {}; for (var i$1 = 0, list$1 = [ "Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Undefined", "Null" ]; i$1 < list$1.length; i$1 += 1) { var name = list$1[i$1]; classToType["[object " + name + "]"] = name.toLowerCase(); } function type(obj) { return classToType[Object.prototype.toString.call(obj)] || "object"; } function unpack(args, keyOrder) { if (keyOrder === void 0) keyOrder = null; if (args.length >= 3) { return Array.prototype.slice.call(args); } if (type(args[0]) == "object" && keyOrder) { return keyOrder.split("").filter(function(k) { return args[0][k] !== void 0; }).map(function(k) { return args[0][k]; }); } return args[0]; } function last(args) { if (args.length < 2) { return null; } var l = args.length - 1; if (type(args[l]) == "string") { return args[l].toLowerCase(); } return null; } var PI$2 = Math.PI; var min$3 = Math.min; var max$3 = Math.max; var TWOPI = PI$2 * 2; var PITHIRD = PI$2 / 3; var DEG2RAD = PI$2 / 180; var RAD2DEG = 180 / PI$2; var input = { format: {}, autodetect: [] }; var Color = function Color2() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var me = this; if (type(args[0]) === "object" && args[0].constructor && args[0].constructor === this.constructor) { return args[0]; } var mode = last(args); var autodetect = false; if (!mode) { autodetect = true; if (!input.sorted) { input.autodetect = input.autodetect.sort(function(a, b) { return b.p - a.p; }); input.sorted = true; } for (var i2 = 0, list2 = input.autodetect; i2 < list2.length; i2 += 1) { var chk = list2[i2]; mode = chk.test.apply(chk, args); if (mode) { break; } } } if (input.format[mode]) { var rgb2 = input.format[mode].apply( null, autodetect ? args : args.slice(0, -1) ); me._rgb = clip_rgb(rgb2); } else { throw new Error("unknown format: " + args); } if (me._rgb.length === 3) { me._rgb.push(1); } }; Color.prototype.toString = function toString() { if (type(this.hex) == "function") { return this.hex(); } return "[" + this._rgb.join(",") + "]"; }; var version = "2.6.0"; var chroma = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(chroma.Color, [null].concat(args)))(); }; chroma.Color = Color; chroma.version = version; var cmyk2rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "cmyk"); var c = args[0]; var m = args[1]; var y = args[2]; var k = args[3]; var alpha = args.length > 4 ? args[4] : 1; if (k === 1) { return [0, 0, 0, alpha]; } return [ c >= 1 ? 0 : 255 * (1 - c) * (1 - k), // r m >= 1 ? 0 : 255 * (1 - m) * (1 - k), // g y >= 1 ? 0 : 255 * (1 - y) * (1 - k), // b alpha ]; }; var max$2 = Math.max; var rgb2cmyk = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; r = r / 255; g = g / 255; b = b / 255; var k = 1 - max$2(r, max$2(g, b)); var f = k < 1 ? 1 / (1 - k) : 0; var c = (1 - r - k) * f; var m = (1 - g - k) * f; var y = (1 - b - k) * f; return [c, m, y, k]; }; Color.prototype.cmyk = function() { return rgb2cmyk(this._rgb); }; chroma.cmyk = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["cmyk"])))(); }; input.format.cmyk = cmyk2rgb; input.autodetect.push({ p: 2, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "cmyk"); if (type(args) === "array" && args.length === 4) { return "cmyk"; } } }); var rnd = function(a) { return Math.round(a * 100) / 100; }; var hsl2css = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var hsla = unpack(args, "hsla"); var mode = last(args) || "lsa"; hsla[0] = rnd(hsla[0] || 0); hsla[1] = rnd(hsla[1] * 100) + "%"; hsla[2] = rnd(hsla[2] * 100) + "%"; if (mode === "hsla" || hsla.length > 3 && hsla[3] < 1) { hsla[3] = hsla.length > 3 ? hsla[3] : 1; mode = "hsla"; } else { hsla.length = 3; } return mode + "(" + hsla.join(",") + ")"; }; var rgb2hsl$1 = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "rgba"); var r = args[0]; var g = args[1]; var b = args[2]; r /= 255; g /= 255; b /= 255; var minRgb = min$3(r, g, b); var maxRgb = max$3(r, g, b); var l = (maxRgb + minRgb) / 2; var s, h; if (maxRgb === minRgb) { s = 0; h = Number.NaN; } else { s = l < 0.5 ? (maxRgb - minRgb) / (maxRgb + minRgb) : (maxRgb - minRgb) / (2 - maxRgb - minRgb); } if (r == maxRgb) { h = (g - b) / (maxRgb - minRgb); } else if (g == maxRgb) { h = 2 + (b - r) / (maxRgb - minRgb); } else if (b == maxRgb) { h = 4 + (r - g) / (maxRgb - minRgb); } h *= 60; if (h < 0) { h += 360; } if (args.length > 3 && args[3] !== void 0) { return [h, s, l, args[3]]; } return [h, s, l]; }; var round$6 = Math.round; var rgb2css = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var rgba = unpack(args, "rgba"); var mode = last(args) || "rgb"; if (mode.substr(0, 3) == "hsl") { return hsl2css(rgb2hsl$1(rgba), mode); } rgba[0] = round$6(rgba[0]); rgba[1] = round$6(rgba[1]); rgba[2] = round$6(rgba[2]); if (mode === "rgba" || rgba.length > 3 && rgba[3] < 1) { rgba[3] = rgba.length > 3 ? rgba[3] : 1; mode = "rgba"; } return mode + "(" + rgba.slice(0, mode === "rgb" ? 3 : 4).join(",") + ")"; }; var round$5 = Math.round; var hsl2rgb = function() { var assign; var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hsl"); var h = args[0]; var s = args[1]; var l = args[2]; var r, g, b; if (s === 0) { r = g = b = l * 255; } else { var t3 = [0, 0, 0]; var c = [0, 0, 0]; var t2 = l < 0.5 ? l * (1 + s) : l + s - l * s; var t1 = 2 * l - t2; var h_ = h / 360; t3[0] = h_ + 1 / 3; t3[1] = h_; t3[2] = h_ - 1 / 3; for (var i2 = 0; i2 < 3; i2++) { if (t3[i2] < 0) { t3[i2] += 1; } if (t3[i2] > 1) { t3[i2] -= 1; } if (6 * t3[i2] < 1) { c[i2] = t1 + (t2 - t1) * 6 * t3[i2]; } else if (2 * t3[i2] < 1) { c[i2] = t2; } else if (3 * t3[i2] < 2) { c[i2] = t1 + (t2 - t1) * (2 / 3 - t3[i2]) * 6; } else { c[i2] = t1; } } assign = [round$5(c[0] * 255), round$5(c[1] * 255), round$5(c[2] * 255)], r = assign[0], g = assign[1], b = assign[2]; } if (args.length > 3) { return [r, g, b, args[3]]; } return [r, g, b, 1]; }; var RE_RGB = /^rgb\(\s*(-?\d+),\s*(-?\d+)\s*,\s*(-?\d+)\s*\)$/; var RE_RGBA = /^rgba\(\s*(-?\d+),\s*(-?\d+)\s*,\s*(-?\d+)\s*,\s*([01]|[01]?\.\d+)\)$/; var RE_RGB_PCT = /^rgb\(\s*(-?\d+(?:\.\d+)?)%,\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*\)$/; var RE_RGBA_PCT = /^rgba\(\s*(-?\d+(?:\.\d+)?)%,\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/; var RE_HSL = /^hsl\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*\)$/; var RE_HSLA = /^hsla\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/; var round$4 = Math.round; var css2rgb = function(css) { css = css.toLowerCase().trim(); var m; if (input.format.named) { try { return input.format.named(css); } catch (e) { } } if (m = css.match(RE_RGB)) { var rgb2 = m.slice(1, 4); for (var i2 = 0; i2 < 3; i2++) { rgb2[i2] = +rgb2[i2]; } rgb2[3] = 1; return rgb2; } if (m = css.match(RE_RGBA)) { var rgb$1 = m.slice(1, 5); for (var i$12 = 0; i$12 < 4; i$12++) { rgb$1[i$12] = +rgb$1[i$12]; } return rgb$1; } if (m = css.match(RE_RGB_PCT)) { var rgb$2 = m.slice(1, 4); for (var i$2 = 0; i$2 < 3; i$2++) { rgb$2[i$2] = round$4(rgb$2[i$2] * 2.55); } rgb$2[3] = 1; return rgb$2; } if (m = css.match(RE_RGBA_PCT)) { var rgb$3 = m.slice(1, 5); for (var i$3 = 0; i$3 < 3; i$3++) { rgb$3[i$3] = round$4(rgb$3[i$3] * 2.55); } rgb$3[3] = +rgb$3[3]; return rgb$3; } if (m = css.match(RE_HSL)) { var hsl2 = m.slice(1, 4); hsl2[1] *= 0.01; hsl2[2] *= 0.01; var rgb$4 = hsl2rgb(hsl2); rgb$4[3] = 1; return rgb$4; } if (m = css.match(RE_HSLA)) { var hsl$1 = m.slice(1, 4); hsl$1[1] *= 0.01; hsl$1[2] *= 0.01; var rgb$5 = hsl2rgb(hsl$1); rgb$5[3] = +m[4]; return rgb$5; } }; css2rgb.test = function(s) { return RE_RGB.test(s) || RE_RGBA.test(s) || RE_RGB_PCT.test(s) || RE_RGBA_PCT.test(s) || RE_HSL.test(s) || RE_HSLA.test(s); }; Color.prototype.css = function(mode) { return rgb2css(this._rgb, mode); }; chroma.css = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["css"])))(); }; input.format.css = css2rgb; input.autodetect.push({ p: 5, test: function(h) { var rest = [], len = arguments.length - 1; while (len-- > 0) rest[len] = arguments[len + 1]; if (!rest.length && type(h) === "string" && css2rgb.test(h)) { return "css"; } } }); input.format.gl = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var rgb2 = unpack(args, "rgba"); rgb2[0] *= 255; rgb2[1] *= 255; rgb2[2] *= 255; return rgb2; }; chroma.gl = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["gl"])))(); }; Color.prototype.gl = function() { var rgb2 = this._rgb; return [rgb2[0] / 255, rgb2[1] / 255, rgb2[2] / 255, rgb2[3]]; }; var floor$3 = Math.floor; var hcg2rgb = function() { var assign, assign$1, assign$2, assign$3, assign$4, assign$5; var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hcg"); var h = args[0]; var c = args[1]; var _g = args[2]; var r, g, b; _g = _g * 255; var _c = c * 255; if (c === 0) { r = g = b = _g; } else { if (h === 360) { h = 0; } if (h > 360) { h -= 360; } if (h < 0) { h += 360; } h /= 60; var i2 = floor$3(h); var f = h - i2; var p = _g * (1 - c); var q = p + _c * (1 - f); var t2 = p + _c * f; var v = p + _c; switch (i2) { case 0: assign = [v, t2, p], r = assign[0], g = assign[1], b = assign[2]; break; case 1: assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]; break; case 2: assign$2 = [p, v, t2], r = assign$2[0], g = assign$2[1], b = assign$2[2]; break; case 3: assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]; break; case 4: assign$4 = [t2, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]; break; case 5: assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]; break; } } return [r, g, b, args.length > 3 ? args[3] : 1]; }; var rgb2hcg = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; var minRgb = min$3(r, g, b); var maxRgb = max$3(r, g, b); var delta = maxRgb - minRgb; var c = delta * 100 / 255; var _g = minRgb / (255 - delta) * 100; var h; if (delta === 0) { h = Number.NaN; } else { if (r === maxRgb) { h = (g - b) / delta; } if (g === maxRgb) { h = 2 + (b - r) / delta; } if (b === maxRgb) { h = 4 + (r - g) / delta; } h *= 60; if (h < 0) { h += 360; } } return [h, c, _g]; }; Color.prototype.hcg = function() { return rgb2hcg(this._rgb); }; chroma.hcg = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["hcg"])))(); }; input.format.hcg = hcg2rgb; input.autodetect.push({ p: 1, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hcg"); if (type(args) === "array" && args.length === 3) { return "hcg"; } } }); var RE_HEX = /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; var RE_HEXA = /^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/; var hex2rgb = function(hex) { if (hex.match(RE_HEX)) { if (hex.length === 4 || hex.length === 7) { hex = hex.substr(1); } if (hex.length === 3) { hex = hex.split(""); hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } var u = parseInt(hex, 16); var r = u >> 16; var g = u >> 8 & 255; var b = u & 255; return [r, g, b, 1]; } if (hex.match(RE_HEXA)) { if (hex.length === 5 || hex.length === 9) { hex = hex.substr(1); } if (hex.length === 4) { hex = hex.split(""); hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3]; } var u$1 = parseInt(hex, 16); var r$1 = u$1 >> 24 & 255; var g$1 = u$1 >> 16 & 255; var b$1 = u$1 >> 8 & 255; var a = Math.round((u$1 & 255) / 255 * 100) / 100; return [r$1, g$1, b$1, a]; } throw new Error("unknown hex color: " + hex); }; var round$3 = Math.round; var rgb2hex = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgba"); var r = ref[0]; var g = ref[1]; var b = ref[2]; var a = ref[3]; var mode = last(args) || "auto"; if (a === void 0) { a = 1; } if (mode === "auto") { mode = a < 1 ? "rgba" : "rgb"; } r = round$3(r); g = round$3(g); b = round$3(b); var u = r << 16 | g << 8 | b; var str = "000000" + u.toString(16); str = str.substr(str.length - 6); var hxa = "0" + round$3(a * 255).toString(16); hxa = hxa.substr(hxa.length - 2); switch (mode.toLowerCase()) { case "rgba": return "#" + str + hxa; case "argb": return "#" + hxa + str; default: return "#" + str; } }; Color.prototype.hex = function(mode) { return rgb2hex(this._rgb, mode); }; chroma.hex = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["hex"])))(); }; input.format.hex = hex2rgb; input.autodetect.push({ p: 4, test: function(h) { var rest = [], len = arguments.length - 1; while (len-- > 0) rest[len] = arguments[len + 1]; if (!rest.length && type(h) === "string" && [3, 4, 5, 6, 7, 8, 9].indexOf(h.length) >= 0) { return "hex"; } } }); var cos$4 = Math.cos; var hsi2rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hsi"); var h = args[0]; var s = args[1]; var i2 = args[2]; var r, g, b; if (isNaN(h)) { h = 0; } if (isNaN(s)) { s = 0; } if (h > 360) { h -= 360; } if (h < 0) { h += 360; } h /= 360; if (h < 1 / 3) { b = (1 - s) / 3; r = (1 + s * cos$4(TWOPI * h) / cos$4(PITHIRD - TWOPI * h)) / 3; g = 1 - (b + r); } else if (h < 2 / 3) { h -= 1 / 3; r = (1 - s) / 3; g = (1 + s * cos$4(TWOPI * h) / cos$4(PITHIRD - TWOPI * h)) / 3; b = 1 - (r + g); } else { h -= 2 / 3; g = (1 - s) / 3; b = (1 + s * cos$4(TWOPI * h) / cos$4(PITHIRD - TWOPI * h)) / 3; r = 1 - (g + b); } r = limit(i2 * r * 3); g = limit(i2 * g * 3); b = limit(i2 * b * 3); return [r * 255, g * 255, b * 255, args.length > 3 ? args[3] : 1]; }; var min$2 = Math.min; var sqrt$4 = Math.sqrt; var acos = Math.acos; var rgb2hsi = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; r /= 255; g /= 255; b /= 255; var h; var min_ = min$2(r, g, b); var i2 = (r + g + b) / 3; var s = i2 > 0 ? 1 - min_ / i2 : 0; if (s === 0) { h = NaN; } else { h = (r - g + (r - b)) / 2; h /= sqrt$4((r - g) * (r - g) + (r - b) * (g - b)); h = acos(h); if (b > g) { h = TWOPI - h; } h /= TWOPI; } return [h * 360, s, i2]; }; Color.prototype.hsi = function() { return rgb2hsi(this._rgb); }; chroma.hsi = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["hsi"])))(); }; input.format.hsi = hsi2rgb; input.autodetect.push({ p: 2, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hsi"); if (type(args) === "array" && args.length === 3) { return "hsi"; } } }); Color.prototype.hsl = function() { return rgb2hsl$1(this._rgb); }; chroma.hsl = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["hsl"])))(); }; input.format.hsl = hsl2rgb; input.autodetect.push({ p: 2, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hsl"); if (type(args) === "array" && args.length === 3) { return "hsl"; } } }); var floor$2 = Math.floor; var hsv2rgb = function() { var assign, assign$1, assign$2, assign$3, assign$4, assign$5; var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hsv"); var h = args[0]; var s = args[1]; var v = args[2]; var r, g, b; v *= 255; if (s === 0) { r = g = b = v; } else { if (h === 360) { h = 0; } if (h > 360) { h -= 360; } if (h < 0) { h += 360; } h /= 60; var i2 = floor$2(h); var f = h - i2; var p = v * (1 - s); var q = v * (1 - s * f); var t2 = v * (1 - s * (1 - f)); switch (i2) { case 0: assign = [v, t2, p], r = assign[0], g = assign[1], b = assign[2]; break; case 1: assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]; break; case 2: assign$2 = [p, v, t2], r = assign$2[0], g = assign$2[1], b = assign$2[2]; break; case 3: assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]; break; case 4: assign$4 = [t2, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]; break; case 5: assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]; break; } } return [r, g, b, args.length > 3 ? args[3] : 1]; }; var min$1 = Math.min; var max$1 = Math.max; var rgb2hsl = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "rgb"); var r = args[0]; var g = args[1]; var b = args[2]; var min_ = min$1(r, g, b); var max_ = max$1(r, g, b); var delta = max_ - min_; var h, s, v; v = max_ / 255; if (max_ === 0) { h = Number.NaN; s = 0; } else { s = delta / max_; if (r === max_) { h = (g - b) / delta; } if (g === max_) { h = 2 + (b - r) / delta; } if (b === max_) { h = 4 + (r - g) / delta; } h *= 60; if (h < 0) { h += 360; } } return [h, s, v]; }; Color.prototype.hsv = function() { return rgb2hsl(this._rgb); }; chroma.hsv = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["hsv"])))(); }; input.format.hsv = hsv2rgb; input.autodetect.push({ p: 2, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "hsv"); if (type(args) === "array" && args.length === 3) { return "hsv"; } } }); var LAB_CONSTANTS = { // Corresponds roughly to RGB brighter/darker Kn: 18, // D65 standard referent Xn: 0.95047, Yn: 1, Zn: 1.08883, t0: 0.137931034, // 4 / 29 t1: 0.206896552, // 6 / 29 t2: 0.12841855, // 3 * t1 * t1 t3: 8856452e-9 // t1 * t1 * t1 }; var pow$a = Math.pow; var lab2rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "lab"); var l = args[0]; var a = args[1]; var b = args[2]; var x, y, z, r, g, b_; y = (l + 16) / 116; x = isNaN(a) ? y : y + a / 500; z = isNaN(b) ? y : y - b / 200; y = LAB_CONSTANTS.Yn * lab_xyz(y); x = LAB_CONSTANTS.Xn * lab_xyz(x); z = LAB_CONSTANTS.Zn * lab_xyz(z); r = xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z); g = xyz_rgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z); b_ = xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z); return [r, g, b_, args.length > 3 ? args[3] : 1]; }; var xyz_rgb = function(r) { return 255 * (r <= 304e-5 ? 12.92 * r : 1.055 * pow$a(r, 1 / 2.4) - 0.055); }; var lab_xyz = function(t2) { return t2 > LAB_CONSTANTS.t1 ? t2 * t2 * t2 : LAB_CONSTANTS.t2 * (t2 - LAB_CONSTANTS.t0); }; var pow$9 = Math.pow; var rgb2lab = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; var ref$1 = rgb2xyz(r, g, b); var x = ref$1[0]; var y = ref$1[1]; var z = ref$1[2]; var l = 116 * y - 16; return [l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z)]; }; var rgb_xyz = function(r) { if ((r /= 255) <= 0.04045) { return r / 12.92; } return pow$9((r + 0.055) / 1.055, 2.4); }; var xyz_lab = function(t2) { if (t2 > LAB_CONSTANTS.t3) { return pow$9(t2, 1 / 3); } return t2 / LAB_CONSTANTS.t2 + LAB_CONSTANTS.t0; }; var rgb2xyz = function(r, g, b) { r = rgb_xyz(r); g = rgb_xyz(g); b = rgb_xyz(b); var x = xyz_lab( (0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / LAB_CONSTANTS.Xn ); var y = xyz_lab( (0.2126729 * r + 0.7151522 * g + 0.072175 * b) / LAB_CONSTANTS.Yn ); var z = xyz_lab( (0.0193339 * r + 0.119192 * g + 0.9503041 * b) / LAB_CONSTANTS.Zn ); return [x, y, z]; }; Color.prototype.lab = function() { return rgb2lab(this._rgb); }; chroma.lab = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["lab"])))(); }; input.format.lab = lab2rgb; input.autodetect.push({ p: 2, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "lab"); if (type(args) === "array" && args.length === 3) { return "lab"; } } }); var sin$3 = Math.sin; var cos$3 = Math.cos; var lch2lab = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "lch"); var l = ref[0]; var c = ref[1]; var h = ref[2]; if (isNaN(h)) { h = 0; } h = h * DEG2RAD; return [l, cos$3(h) * c, sin$3(h) * c]; }; var lch2rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "lch"); var l = args[0]; var c = args[1]; var h = args[2]; var ref = lch2lab(l, c, h); var L = ref[0]; var a = ref[1]; var b_ = ref[2]; var ref$1 = lab2rgb(L, a, b_); var r = ref$1[0]; var g = ref$1[1]; var b = ref$1[2]; return [r, g, b, args.length > 3 ? args[3] : 1]; }; var hcl2rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var hcl = unpack(args, "hcl").reverse(); return lch2rgb.apply(void 0, hcl); }; var sqrt$3 = Math.sqrt; var atan2$2 = Math.atan2; var round$2 = Math.round; var lab2lch = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "lab"); var l = ref[0]; var a = ref[1]; var b = ref[2]; var c = sqrt$3(a * a + b * b); var h = (atan2$2(b, a) * RAD2DEG + 360) % 360; if (round$2(c * 1e4) === 0) { h = Number.NaN; } return [l, c, h]; }; var rgb2lch = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; var ref$1 = rgb2lab(r, g, b); var l = ref$1[0]; var a = ref$1[1]; var b_ = ref$1[2]; return lab2lch(l, a, b_); }; Color.prototype.lch = function() { return rgb2lch(this._rgb); }; Color.prototype.hcl = function() { return rgb2lch(this._rgb).reverse(); }; chroma.lch = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["lch"])))(); }; chroma.hcl = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["hcl"])))(); }; input.format.lch = lch2rgb; input.format.hcl = hcl2rgb; ["lch", "hcl"].forEach( function(m) { return input.autodetect.push({ p: 2, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, m); if (type(args) === "array" && args.length === 3) { return m; } } }); } ); var w3cx11 = { aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aqua: "#00ffff", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", black: "#000000", blanchedalmond: "#ffebcd", blue: "#0000ff", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed", cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkgrey: "#a9a9a9", darkkhaki: "#bdb76b", darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dimgrey: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222", floralwhite: "#fffaf0", forestgreen: "#228b22", fuchsia: "#ff00ff", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", gold: "#ffd700", goldenrod: "#daa520", gray: "#808080", green: "#008000", greenyellow: "#adff2f", grey: "#808080", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", laserlemon: "#ffff54", lavender: "#e6e6fa", lavenderblush: "#fff0f5", lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrod: "#fafad2", lightgoldenrodyellow: "#fafad2", lightgray: "#d3d3d3", lightgreen: "#90ee90", lightgrey: "#d3d3d3", lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightslategrey: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", lime: "#00ff00", limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", maroon: "#800000", maroon2: "#7f0000", maroon3: "#b03060", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db", mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa", mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", navy: "#000080", oldlace: "#fdf5e6", olive: "#808000", olivedrab: "#6b8e23", orange: "#ffa500", orangered: "#ff4500", orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f", pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", purple: "#800080", purple2: "#7f007f", purple3: "#a020f0", rebeccapurple: "#663399", red: "#ff0000", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072", sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", silver: "#c0c0c0", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", slategrey: "#708090", snow: "#fffafa", springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", teal: "#008080", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3", white: "#ffffff", whitesmoke: "#f5f5f5", yellow: "#ffff00", yellowgreen: "#9acd32" }; Color.prototype.name = function() { var hex = rgb2hex(this._rgb, "rgb"); for (var i2 = 0, list2 = Object.keys(w3cx11); i2 < list2.length; i2 += 1) { var n = list2[i2]; if (w3cx11[n] === hex) { return n.toLowerCase(); } } return hex; }; input.format.named = function(name2) { name2 = name2.toLowerCase(); if (w3cx11[name2]) { return hex2rgb(w3cx11[name2]); } throw new Error("unknown color name: " + name2); }; input.autodetect.push({ p: 5, test: function(h) { var rest = [], len = arguments.length - 1; while (len-- > 0) rest[len] = arguments[len + 1]; if (!rest.length && type(h) === "string" && w3cx11[h.toLowerCase()]) { return "named"; } } }); var num2rgb = function(num2) { if (type(num2) == "number" && num2 >= 0 && num2 <= 16777215) { var r = num2 >> 16; var g = num2 >> 8 & 255; var b = num2 & 255; return [r, g, b, 1]; } throw new Error("unknown num color: " + num2); }; var rgb2num = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; return (r << 16) + (g << 8) + b; }; Color.prototype.num = function() { return rgb2num(this._rgb); }; chroma.num = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["num"])))(); }; input.format.num = num2rgb; input.autodetect.push({ p: 5, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; if (args.length === 1 && type(args[0]) === "number" && args[0] >= 0 && args[0] <= 16777215) { return "num"; } } }); var round$1 = Math.round; Color.prototype.rgb = function(rnd2) { if (rnd2 === void 0) rnd2 = true; if (rnd2 === false) { return this._rgb.slice(0, 3); } return this._rgb.slice(0, 3).map(round$1); }; Color.prototype.rgba = function(rnd2) { if (rnd2 === void 0) rnd2 = true; return this._rgb.slice(0, 4).map(function(v, i2) { return i2 < 3 ? rnd2 === false ? v : round$1(v) : v; }); }; chroma.rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["rgb"])))(); }; input.format.rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var rgba = unpack(args, "rgba"); if (rgba[3] === void 0) { rgba[3] = 1; } return rgba; }; input.autodetect.push({ p: 3, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "rgba"); if (type(args) === "array" && (args.length === 3 || args.length === 4 && type(args[3]) == "number" && args[3] >= 0 && args[3] <= 1)) { return "rgb"; } } }); var log$1 = Math.log; var temperature2rgb = function(kelvin) { var temp = kelvin / 100; var r, g, b; if (temp < 66) { r = 255; g = temp < 6 ? 0 : -155.25485562709179 - 0.44596950469579133 * (g = temp - 2) + 104.49216199393888 * log$1(g); b = temp < 20 ? 0 : -254.76935184120902 + 0.8274096064007395 * (b = temp - 10) + 115.67994401066147 * log$1(b); } else { r = 351.97690566805693 + 0.114206453784165 * (r = temp - 55) - 40.25366309332127 * log$1(r); g = 325.4494125711974 + 0.07943456536662342 * (g = temp - 50) - 28.0852963507957 * log$1(g); b = 255; } return [r, g, b, 1]; }; var round = Math.round; var rgb2temperature = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var rgb2 = unpack(args, "rgb"); var r = rgb2[0], b = rgb2[2]; var minTemp = 1e3; var maxTemp = 4e4; var eps = 0.4; var temp; while (maxTemp - minTemp > eps) { temp = (maxTemp + minTemp) * 0.5; var rgb$1 = temperature2rgb(temp); if (rgb$1[2] / rgb$1[0] >= b / r) { maxTemp = temp; } else { minTemp = temp; } } return round(temp); }; Color.prototype.temp = Color.prototype.kelvin = Color.prototype.temperature = function() { return rgb2temperature(this._rgb); }; chroma.temp = chroma.kelvin = chroma.temperature = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["temp"])))(); }; input.format.temp = input.format.kelvin = input.format.temperature = temperature2rgb; var pow$8 = Math.pow; var sign$1 = Math.sign; var oklab2rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "lab"); var L = args[0]; var a = args[1]; var b = args[2]; var l = pow$8(L + 0.3963377774 * a + 0.2158037573 * b, 3); var m = pow$8(L - 0.1055613458 * a - 0.0638541728 * b, 3); var s = pow$8(L - 0.0894841775 * a - 1.291485548 * b, 3); return [ 255 * lrgb2rgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s), 255 * lrgb2rgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s), 255 * lrgb2rgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s), args.length > 3 ? args[3] : 1 ]; }; function lrgb2rgb(c) { var abs2 = Math.abs(c); if (abs2 > 31308e-7) { return (sign$1(c) || 1) * (1.055 * pow$8(abs2, 1 / 2.4) - 0.055); } return c * 12.92; } var cbrt = Math.cbrt; var pow$7 = Math.pow; var sign = Math.sign; var rgb2oklab = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; var ref$1 = [ rgb2lrgb(r / 255), rgb2lrgb(g / 255), rgb2lrgb(b / 255) ]; var lr = ref$1[0]; var lg = ref$1[1]; var lb = ref$1[2]; var l = cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb); var m = cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb); var s = cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb); return [ 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s, 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s, 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s ]; }; function rgb2lrgb(c) { var abs2 = Math.abs(c); if (abs2 < 0.04045) { return c / 12.92; } return (sign(c) || 1) * pow$7((abs2 + 0.055) / 1.055, 2.4); } Color.prototype.oklab = function() { return rgb2oklab(this._rgb); }; chroma.oklab = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["oklab"])))(); }; input.format.oklab = oklab2rgb; input.autodetect.push({ p: 3, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "oklab"); if (type(args) === "array" && args.length === 3) { return "oklab"; } } }); var oklch2rgb = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "lch"); var l = args[0]; var c = args[1]; var h = args[2]; var ref = lch2lab(l, c, h); var L = ref[0]; var a = ref[1]; var b_ = ref[2]; var ref$1 = oklab2rgb(L, a, b_); var r = ref$1[0]; var g = ref$1[1]; var b = ref$1[2]; return [r, g, b, args.length > 3 ? args[3] : 1]; }; var rgb2oklch = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; var ref = unpack(args, "rgb"); var r = ref[0]; var g = ref[1]; var b = ref[2]; var ref$1 = rgb2oklab(r, g, b); var l = ref$1[0]; var a = ref$1[1]; var b_ = ref$1[2]; return lab2lch(l, a, b_); }; Color.prototype.oklch = function() { return rgb2oklch(this._rgb); }; chroma.oklch = function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; return new (Function.prototype.bind.apply(Color, [null].concat(args, ["oklch"])))(); }; input.format.oklch = oklch2rgb; input.autodetect.push({ p: 3, test: function() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; args = unpack(args, "oklch"); if (type(args) === "array" && args.length === 3) { return "oklch"; } } }); Color.prototype.alpha = function(a, mutate) { if (mutate === void 0) mutate = false; if (a !== void 0 && type(a) === "number") { if (mutate) { this._rgb[3] = a; return this; } return new Color([this._rgb[0], this._rgb[1], this._rgb[2], a], "rgb"); } return this._rgb[3]; }; Color.prototype.clipped = function() { return this._rgb._clipped || false; }; Color.prototype.darken = function(amount) { if (amount === void 0) amount = 1; var me = this; var lab2 = me.lab(); lab2[0] -= LAB_CONSTANTS.Kn * amount; return new Color(lab2, "lab").alpha(me.alpha(), true); }; Color.prototype.brighten = function(amount) { if (amount === void 0) amount = 1; return this.darken(-amount); }; Color.prototype.darker = Color.prototype.darken; Color.prototype.brighter = Color.prototype.brighten; Color.prototype.get = function(mc) { var ref = mc.split("."); var mode = ref[0]; var channel = ref[1]; var src = this[mode](); if (channel) { var i2 = mode.indexOf(channel) - (mode.substr(0, 2) === "ok" ? 2 : 0); if (i2 > -1) { return src[i2]; } throw new Error("unknown channel " + channel + " in mode " + mode); } else { return src; } }; var pow$6 = Math.pow; var EPS = 1e-7; var MAX_ITER = 20; Color.prototype.luminance = function(lum, mode) { if (mode === void 0) mode = "rgb"; if (lum !== void 0 && type(lum) === "number") { if (lum === 0) { return new Color([0, 0, 0, this._rgb[3]], "rgb"); } if (lum === 1) { return new Color([255, 255, 255, this._rgb[3]], "rgb"); } var cur_lum = this.luminance(); var max_iter = MAX_ITER; var test = function(low, high) { var mid = low.interpolate(high, 0.5, mode); var lm = mid.luminance(); if (Math.abs(lum - lm) < EPS || !max_iter--) { return mid; } return lm > lum ? test(low, mid) : test(mid, high); }; var rgb2 = (cur_lum > lum ? test(new Color([0, 0, 0]), this) : test(this, new Color([255, 255, 255]))).rgb(); return new Color(rgb2.concat([this._rgb[3]])); } return rgb2luminance.apply(void 0, this._rgb.slice(0, 3)); }; var rgb2luminance = function(r, g, b) { r = luminance_x(r); g = luminance_x(g); b = luminance_x(b); return 0.2126 * r + 0.7152 * g + 0.0722 * b; }; var luminance_x = function(x) { x /= 255; return x <= 0.03928 ? x / 12.92 : pow$6((x + 0.055) / 1.055, 2.4); }; var index = {}; function mix(col1, col2, f) { if (f === void 0) f = 0.5; var rest = [], len = arguments.length - 3; while (len-- > 0) rest[len] = arguments[len + 3]; var mode = rest[0] || "lrgb"; if (!index[mode] && !rest.length) { mode = Object.keys(index)[0]; } if (!index[mode]) { throw new Error("interpolation mode " + mode + " is not defined"); } if (type(col1) !== "object") { col1 = new Color(col1); } if (type(col2) !== "object") { col2 = new Color(col2); } return index[mode](col1, col2, f).alpha( col1.alpha() + f * (col2.alpha() - col1.alpha()) ); } Color.prototype.mix = Color.prototype.interpolate = function(col2, f) { if (f === void 0) f = 0.5; var rest = [], len = arguments.length - 2; while (len-- > 0) rest[len] = arguments[len + 2]; return mix.apply(void 0, [this, col2, f].concat(rest)); }; Color.prototype.premultiply = function(mutate) { if (mutate === void 0) mutate = false; var rgb2 = this._rgb; var a = rgb2[3]; if (mutate) { this._rgb = [rgb2[0] * a, rgb2[1] * a, rgb2[2] * a, a]; return this; } else { return new Color([rgb2[0] * a, rgb2[1] * a, rgb2[2] * a, a], "rgb"); } }; Color.prototype.saturate = function(amount) { if (amount === void 0) amount = 1; var me = this; var lch2 = me.lch(); lch2[1] += LAB_CONSTANTS.Kn * amount; if (lch2[1] < 0) { lch2[1] = 0; } return new Color(lch2, "lch").alpha(me.alpha(), true); }; Color.prototype.desaturate = function(amount) { if (amount === void 0) amount = 1; return this.saturate(-amount); }; Color.prototype.set = function(mc, value, mutate) { if (mutate === void 0) mutate = false; var ref = mc.split("."); var mode = ref[0]; var channel = ref[1]; var src = this[mode](); if (channel) { var i2 = mode.indexOf(channel) - (mode.substr(0, 2) === "ok" ? 2 : 0); if (i2 > -1) { if (type(value) == "string") { switch (value.charAt(0)) { case "+": src[i2] += +value; break; case "-": src[i2] += +value; break; case "*": src[i2] *= +value.substr(1); break; case "/": src[i2] /= +value.substr(1); break; default: src[i2] = +value; } } else if (type(value) === "number") { src[i2] = value; } else { throw new Error("unsupported value for Color.set"); } var out = new Color(src, mode); if (mutate) { this._rgb = out._rgb; return this; } return out; } throw new Error("unknown channel " + channel + " in mode " + mode); } else { return src; } }; Color.prototype.tint = function(f) { if (f === void 0) f = 0.5; var rest = [], len = arguments.length - 1; while (len-- > 0) rest[len] = arguments[len + 1]; return mix.apply(void 0, [this, "white", f].concat(rest)); }; Color.prototype.shade = function(f) { if (f === void 0) f = 0.5; var rest = [], len = arguments.length - 1; while (len-- > 0) rest[len] = arguments[len + 1]; return mix.apply(void 0, [this, "black", f].concat(rest)); }; var rgb = function(col1, col2, f) { var xyz0 = col1._rgb; var xyz1 = col2._rgb; return new Color( xyz0[0] + f * (xyz1[0] - xyz0[0]), xyz0[1] + f * (xyz1[1] - xyz0[1]), xyz0[2] + f * (xyz1[2] - xyz0[2]), "rgb" ); }; index.rgb = rgb; var sqrt$2 = Math.sqrt; var pow$5 = Math.pow; var lrgb = function(col1, col2, f) { var ref = col1._rgb; var x1 = ref[0]; var y1 = ref[1]; var z1 = ref[2]; var ref$1 = col2._rgb; var x2 = ref$1[0]; var y2 = ref$1[1]; var z2 = ref$1[2]; return new Color( sqrt$2(pow$5(x1, 2) * (1 - f) + pow$5(x2, 2) * f), sqrt$2(pow$5(y1, 2) * (1 - f) + pow$5(y2, 2) * f), sqrt$2(pow$5(z1, 2) * (1 - f) + pow$5(z2, 2) * f), "rgb" ); }; index.lrgb = lrgb; var lab = function(col1, col2, f) { var xyz0 = col1.lab(); var xyz1 = col2.lab(); return new Color( xyz0[0] + f * (xyz1[0] - xyz0[0]), xyz0[1] + f * (xyz1[1] - xyz0[1]), xyz0[2] + f * (xyz1[2] - xyz0[2]), "lab" ); }; index.lab = lab; function interpolate_hsx(col1, col2, f, m) { var assign, assign$1; var xyz0, xyz1; if (m === "hsl") { xyz0 = col1.hsl(); xyz1 = col2.hsl(); } else if (m === "hsv") { xyz0 = col1.hsv(); xyz1 = col2.hsv(); } else if (m === "hcg") { xyz0 = col1.hcg(); xyz1 = col2.hcg(); } else if (m === "hsi") { xyz0 = col1.hsi(); xyz1 = col2.hsi(); } else if (m === "lch" || m === "hcl") { m = "hcl"; xyz0 = col1.hcl(); xyz1 = col2.hcl(); } else if (m === "oklch") { xyz0 = col1.oklch().reverse(); xyz1 = col2.oklch().reverse(); } var hue0, hue1, sat0, sat1, lbv0, lbv1; if (m.substr(0, 1) === "h" || m === "oklch") { assign = xyz0, hue0 = assign[0], sat0 = assign[1], lbv0 = assign[2]; assign$1 = xyz1, hue1 = assign$1[0], sat1 = assign$1[1], lbv1 = assign$1[2]; } var sat, hue, lbv, dh; if (!isNaN(hue0) && !isNaN(hue1)) { if (hue1 > hue0 && hue1 - hue0 > 180) { dh = hue1 - (hue0 + 360); } else if (hue1 < hue0 && hue0 - hue1 > 180) { dh = hue1 + 360 - hue0; } else { dh = hue1 - hue0; } hue = hue0 + f * dh; } else if (!isNaN(hue0)) { hue = hue0; if ((lbv1 == 1 || lbv1 == 0) && m != "hsv") { sat = sat0; } } else if (!isNaN(hue1)) { hue = hue1; if ((lbv0 == 1 || lbv0 == 0) && m != "hsv") { sat = sat1; } } else { hue = Number.NaN; } if (sat === void 0) { sat = sat0 + f * (sat1 - sat0); } lbv = lbv0 + f * (lbv1 - lbv0); return m === "oklch" ? new Color([lbv, sat, hue], m) : new Color([hue, sat, lbv], m); } var lch = function(col1, col2, f) { return interpolate_hsx(col1, col2, f, "lch"); }; index.lch = lch; index.hcl = lch; var num = function(col1, col2, f) { var c1 = col1.num(); var c2 = col2.num(); return new Color(c1 + f * (c2 - c1), "num"); }; index.num = num; var hcg = function(col1, col2, f) { return interpolate_hsx(col1, col2, f, "hcg"); }; index.hcg = hcg; var hsi = function(col1, col2, f) { return interpolate_hsx(col1, col2, f, "hsi"); }; index.hsi = hsi; var hsl = function(col1, col2, f) { return interpolate_hsx(col1, col2, f, "hsl"); }; index.hsl = hsl; var hsv = function(col1, col2, f) { return interpolate_hsx(col1, col2, f, "hsv"); }; index.hsv = hsv; var oklab = function(col1, col2, f) { var xyz0 = col1.oklab(); var xyz1 = col2.oklab(); return new Color( xyz0[0] + f * (xyz1[0] - xyz0[0]), xyz0[1] + f * (xyz1[1] - xyz0[1]), xyz0[2] + f * (xyz1[2] - xyz0[2]), "oklab" ); }; index.oklab = oklab; var oklch = function(col1, col2, f) { return interpolate_hsx(col1, col2, f, "oklch"); }; index.oklch = oklch; var pow$4 = Math.pow; var sqrt$1 = Math.sqrt; var PI$1 = Math.PI; var cos$2 = Math.cos; var sin$2 = Math.sin; var atan2$1 = Math.atan2; function average(colors, mode, weights) { if (mode === void 0) mode = "lrgb"; if (weights === void 0) weights = null; var l = colors.length; if (!weights) { weights = Array.from(new Array(l)).map(function() { return 1; }); } var k = l / weights.reduce(function(a, b) { return a + b; }); weights.forEach(function(w, i3) { weights[i3] *= k; }); colors = colors.map(function(c) { return new Color(c); }); if (mode === "lrgb") { return _average_lrgb(colors, weights); } var first = colors.shift(); var xyz = first.get(mode); var cnt = []; var dx = 0; var dy = 0; for (var i2 = 0; i2 < xyz.length; i2++) { xyz[i2] = (xyz[i2] || 0) * weights[0]; cnt.push(isNaN(xyz[i2]) ? 0 : weights[0]); if (mode.charAt(i2) === "h" && !isNaN(xyz[i2])) { var A = xyz[i2] / 180 * PI$1; dx += cos$2(A) * weights[0]; dy += sin$2(A) * weights[0]; } } var alpha = first.alpha() * weights[0]; colors.forEach(function(c, ci) { var xyz2 = c.get(mode); alpha += c.alpha() * weights[ci + 1]; for (var i3 = 0; i3 < xyz.length; i3++) { if (!isNaN(xyz2[i3])) { cnt[i3] += weights[ci + 1]; if (mode.charAt(i3) === "h") { var A2 = xyz2[i3] / 180 * PI$1; dx += cos$2(A2) * weights[ci + 1]; dy += sin$2(A2) * weights[ci + 1]; } else { xyz[i3] += xyz2[i3] * weights[ci + 1]; } } } }); for (var i$12 = 0; i$12 < xyz.length; i$12++) { if (mode.charAt(i$12) === "h") { var A$1 = atan2$1(dy / cnt[i$12], dx / cnt[i$12]) / PI$1 * 180; while (A$1 < 0) { A$1 += 360; } while (A$1 >= 360) { A$1 -= 360; } xyz[i$12] = A$1; } else { xyz[i$12] = xyz[i$12] / cnt[i$12]; } } alpha /= l; return new Color(xyz, mode).alpha(alpha > 0.99999 ? 1 : alpha, true); } var _average_lrgb = function(colors, weights) { var l = colors.length; var xyz = [0, 0, 0, 0]; for (var i2 = 0; i2 < colors.length; i2++) { var col = colors[i2]; var f = weights[i2] / l; var rgb2 = col._rgb; xyz[0] += pow$4(rgb2[0], 2) * f; xyz[1] += pow$4(rgb2[1], 2) * f; xyz[2] += pow$4(rgb2[2], 2) * f; xyz[3] += rgb2[3] * f; } xyz[0] = sqrt$1(xyz[0]); xyz[1] = sqrt$1(xyz[1]); xyz[2] = sqrt$1(xyz[2]); if (xyz[3] > 0.9999999) { xyz[3] = 1; } return new Color(clip_rgb(xyz)); }; var pow$3 = Math.pow; function scale(colors) { var _mode = "rgb"; var _nacol = chroma("#ccc"); var _spread = 0; var _domain = [0, 1]; var _pos = []; var _padding = [0, 0]; var _classes = false; var _colors = []; var _out = false; var _min = 0; var _max = 1; var _correctLightness = false; var _colorCache = {}; var _useCache = true; var _gamma = 1; var setColors = function(colors2) { colors2 = colors2 || ["#fff", "#000"]; if (colors2 && type(colors2) === "string" && chroma.brewer && chroma.brewer[colors2.toLowerCase()]) { colors2 = chroma.brewer[colors2.toLowerCase()]; } if (type(colors2) === "array") { if (colors2.length === 1) { colors2 = [colors2[0], colors2[0]]; } colors2 = colors2.slice(0); for (var c = 0; c < colors2.length; c++) { colors2[c] = chroma(colors2[c]); } _pos.length = 0; for (var c$1 = 0; c$1 < colors2.length; c$1++) { _pos.push(c$1 / (colors2.length - 1)); } } resetCache(); return _colors = colors2; }; var getClass = function(value) { if (_classes != null) { var n = _classes.length - 1; var i2 = 0; while (i2 < n && value >= _classes[i2]) { i2++; } return i2 - 1; } return 0; }; var tMapLightness = function(t2) { return t2; }; var tMapDomain = function(t2) { return t2; }; var getColor = function(val, bypassMap) { var col, t2; if (bypassMap == null) { bypassMap = false; } if (isNaN(val) || val === null) { return _nacol; } if (!bypassMap) { if (_classes && _classes.length > 2) { var c = getClass(val); t2 = c / (_classes.length - 2); } else if (_max !== _min) { t2 = (val - _min) / (_max - _min); } else { t2 = 1; } } else { t2 = val; } t2 = tMapDomain(t2); if (!bypassMap) { t2 = tMapLightness(t2); } if (_gamma !== 1) { t2 = pow$3(t2, _gamma); } t2 = _padding[0] + t2 * (1 - _padding[0] - _padding[1]); t2 = limit(t2, 0, 1); var k = Math.floor(t2 * 1e4); if (_useCache && _colorCache[k]) { col = _colorCache[k]; } else { if (type(_colors) === "array") { for (var i2 = 0; i2 < _pos.length; i2++) { var p = _pos[i2]; if (t2 <= p) { col = _colors[i2]; break; } if (t2 >= p && i2 === _pos.length - 1) { col = _colors[i2]; break; } if (t2 > p && t2 < _pos[i2 + 1]) { t2 = (t2 - p) / (_pos[i2 + 1] - p); col = chroma.interpolate( _colors[i2], _colors[i2 + 1], t2, _mode ); break; } } } else if (type(_colors) === "function") { col = _colors(t2); } if (_useCache) { _colorCache[k] = col; } } return col; }; var resetCache = function() { return _colorCache = {}; }; setColors(colors); var f = function(v) { var c = chroma(getColor(v)); if (_out && c[_out]) { return c[_out](); } else { return c; } }; f.classes = function(classes) { if (classes != null) { if (type(classes) === "array") { _classes = classes; _domain = [classes[0], classes[classes.length - 1]]; } else { var d = chroma.analyze(_domain); if (classes === 0) { _classes = [d.min, d.max]; } else { _classes = chroma.limits(d, "e", classes); } } return f; } return _classes; }; f.domain = function(domain) { if (!arguments.length) { return _domain; } _min = domain[0]; _max = domain[domain.length - 1]; _pos = []; var k = _colors.length; if (domain.length === k && _min !== _max) { for (var i2 = 0, list2 = Array.from(domain); i2 < list2.length; i2 += 1) { var d = list2[i2]; _pos.push((d - _min) / (_max - _min)); } } else { for (var c = 0; c < k; c++) { _pos.push(c / (k - 1)); } if (domain.length > 2) { var tOut = domain.map(function(d2, i3) { return i3 / (domain.length - 1); }); var tBreaks = domain.map(function(d2) { return (d2 - _min) / (_max - _min); }); if (!tBreaks.every(function(val, i3) { return tOut[i3] === val; })) { tMapDomain = function(t2) { if (t2 <= 0 || t2 >= 1) { return t2; } var i3 = 0; while (t2 >= tBreaks[i3 + 1]) { i3++; } var f2 = (t2 - tBreaks[i3]) / (tBreaks[i3 + 1] - tBreaks[i3]); var out = tOut[i3] + f2 * (tOut[i3 + 1] - tOut[i3]); return out; }; } } } _domain = [_min, _max]; return f; }; f.mode = function(_m) { if (!arguments.length) { return _mode; } _mode = _m; resetCache(); return f; }; f.range = function(colors2, _pos2) { setColors(colors2); return f; }; f.out = function(_o) { _out = _o; return f; }; f.spread = function(val) { if (!arguments.length) { return _spread; } _spread = val; return f; }; f.correctLightness = function(v) { if (v == null) { v = true; } _correctLightness = v; resetCache(); if (_correctLightness) { tMapLightness = function(t2) { var L0 = getColor(0, true).lab()[0]; var L1 = getColor(1, true).lab()[0]; var pol = L0 > L1; var L_actual = getColor(t2, true).lab()[0]; var L_ideal = L0 + (L1 - L0) * t2; var L_diff = L_actual - L_ideal; var t0 = 0; var t1 = 1; var max_iter = 20; while (Math.abs(L_diff) > 0.01 && max_iter-- > 0) { (function() { if (pol) { L_diff *= -1; } if (L_diff < 0) { t0 = t2; t2 += (t1 - t2) * 0.5; } else { t1 = t2; t2 += (t0 - t2) * 0.5; } L_actual = getColor(t2, true).lab()[0]; return L_diff = L_actual - L_ideal; })(); } return t2; }; } else { tMapLightness = function(t2) { return t2; }; } return f; }; f.padding = function(p) { if (p != null) { if (type(p) === "number") { p = [p, p]; } _padding = p; return f; } else { return _padding; } }; f.colors = function(numColors, out) { if (arguments.length < 2) { out = "hex"; } var result = []; if (arguments.length === 0) { result = _colors.slice(0); } else if (numColors === 1) { result = [f(0.5)]; } else if (numColors > 1) { var dm = _domain[0]; var dd = _domain[1] - dm; result = __range__(0, numColors).map( function(i3) { return f(dm + i3 / (numColors - 1) * dd); } ); } else { colors = []; var samples = []; if (_classes && _classes.length > 2) { for (var i2 = 1, end = _classes.length, asc = 1 <= end; asc ? i2 < end : i2 > end; asc ? i2++ : i2--) { samples.push((_classes[i2 - 1] + _classes[i2]) * 0.5); } } else { samples = _domain; } result = samples.map(function(v) { return f(v); }); } if (chroma[out]) { result = result.map(function(c) { return c[out](); }); } return result; }; f.cache = function(c) { if (c != null) { _useCache = c; return f; } else { return _useCache; } }; f.gamma = function(g) { if (g != null) { _gamma = g; return f; } else { return _gamma; } }; f.nodata = function(d) { if (d != null) { _nacol = chroma(d); return f; } else { return _nacol; } }; return f; } function __range__(left, right, inclusive) { var range = []; var ascending = left < right; var end = right; for (var i2 = left; ascending ? i2 < end : i2 > end; ascending ? i2++ : i2--) { range.push(i2); } return range; } var binom_row = function(n) { var row = [1, 1]; for (var i2 = 1; i2 < n; i2++) { var newrow = [1]; for (var j = 1; j <= row.length; j++) { newrow[j] = (row[j] || 0) + row[j - 1]; } row = newrow; } return row; }; var bezier = function(colors) { var assign, assign$1, assign$2; var I, lab0, lab1, lab2; colors = colors.map(function(c) { return new Color(c); }); if (colors.length === 2) { assign = colors.map(function(c) { return c.lab(); }), lab0 = assign[0], lab1 = assign[1]; I = function(t2) { var lab4 = [0, 1, 2].map(function(i2) { return lab0[i2] + t2 * (lab1[i2] - lab0[i2]); }); return new Color(lab4, "lab"); }; } else if (colors.length === 3) { assign$1 = colors.map(function(c) { return c.lab(); }), lab0 = assign$1[0], lab1 = assign$1[1], lab2 = assign$1[2]; I = function(t2) { var lab4 = [0, 1, 2].map( function(i2) { return (1 - t2) * (1 - t2) * lab0[i2] + 2 * (1 - t2) * t2 * lab1[i2] + t2 * t2 * lab2[i2]; } ); return new Color(lab4, "lab"); }; } else if (colors.length === 4) { var lab3; assign$2 = colors.map(function(c) { return c.lab(); }), lab0 = assign$2[0], lab1 = assign$2[1], lab2 = assign$2[2], lab3 = assign$2[3]; I = function(t2) { var lab4 = [0, 1, 2].map( function(i2) { return (1 - t2) * (1 - t2) * (1 - t2) * lab0[i2] + 3 * (1 - t2) * (1 - t2) * t2 * lab1[i2] + 3 * (1 - t2) * t2 * t2 * lab2[i2] + t2 * t2 * t2 * lab3[i2]; } ); return new Color(lab4, "lab"); }; } else if (colors.length >= 5) { var labs, row, n; labs = colors.map(function(c) { return c.lab(); }); n = colors.length - 1; row = binom_row(n); I = function(t2) { var u = 1 - t2; var lab4 = [0, 1, 2].map( function(i2) { return labs.reduce( function(sum, el, j) { return sum + row[j] * Math.pow(u, n - j) * Math.pow(t2, j) * el[i2]; }, 0 ); } ); return new Color(lab4, "lab"); }; } else { throw new RangeError("No point in running bezier with only one color."); } return I; }; function bezier$1(colors) { var f = bezier(colors); f.scale = function() { return scale(f); }; return f; } var blend = function(bottom, top, mode) { if (!blend[mode]) { throw new Error("unknown blend mode " + mode); } return blend[mode](bottom, top); }; var blend_f = function(f) { return function(bottom, top) { var c0 = chroma(top).rgb(); var c1 = chroma(bottom).rgb(); return chroma.rgb(f(c0, c1)); }; }; var each = function(f) { return function(c0, c1) { var out = []; out[0] = f(c0[0], c1[0]); out[1] = f(c0[1], c1[1]); out[2] = f(c0[2], c1[2]); return out; }; }; var normal = function(a) { return a; }; var multiply = function(a, b) { return a * b / 255; }; var darken = function(a, b) { return a > b ? b : a; }; var lighten = function(a, b) { return a > b ? a : b; }; var screen = function(a, b) { return 255 * (1 - (1 - a / 255) * (1 - b / 255)); }; var overlay = function(a, b) { return b < 128 ? 2 * a * b / 255 : 255 * (1 - 2 * (1 - a / 255) * (1 - b / 255)); }; var burn = function(a, b) { return 255 * (1 - (1 - b / 255) / (a / 255)); }; var dodge = function(a, b) { if (a === 255) { return 255; } a = 255 * (b / 255) / (1 - a / 255); return a > 255 ? 255 : a; }; blend.normal = blend_f(each(normal)); blend.multiply = blend_f(each(multiply)); blend.screen = blend_f(each(screen)); blend.overlay = blend_f(each(overlay)); blend.darken = blend_f(each(darken)); blend.lighten = blend_f(each(lighten)); blend.dodge = blend_f(each(dodge)); blend.burn = blend_f(each(burn)); var pow$2 = Math.pow; var sin$1 = Math.sin; var cos$1 = Math.cos; function cubehelix(start, rotations, hue, gamma, lightness) { if (start === void 0) start = 300; if (rotations === void 0) rotations = -1.5; if (hue === void 0) hue = 1; if (gamma === void 0) gamma = 1; if (lightness === void 0) lightness = [0, 1]; var dh = 0, dl; if (type(lightness) === "array") { dl = lightness[1] - lightness[0]; } else { dl = 0; lightness = [lightness, lightness]; } var f = function(fract) { var a = TWOPI * ((start + 120) / 360 + rotations * fract); var l = pow$2(lightness[0] + dl * fract, gamma); var h = dh !== 0 ? hue[0] + fract * dh : hue; var amp = h * l * (1 - l) / 2; var cos_a = cos$1(a); var sin_a = sin$1(a); var r = l + amp * (-0.14861 * cos_a + 1.78277 * sin_a); var g = l + amp * (-0.29227 * cos_a - 0.90649 * sin_a); var b = l + amp * (1.97294 * cos_a); return chroma(clip_rgb([r * 255, g * 255, b * 255, 1])); }; f.start = function(s) { if (s == null) { return start; } start = s; return f; }; f.rotations = function(r) { if (r == null) { return rotations; } rotations = r; return f; }; f.gamma = function(g) { if (g == null) { return gamma; } gamma = g; return f; }; f.hue = function(h) { if (h == null) { return hue; } hue = h; if (type(hue) === "array") { dh = hue[1] - hue[0]; if (dh === 0) { hue = hue[1]; } } else { dh = 0; } return f; }; f.lightness = function(h) { if (h == null) { return lightness; } if (type(h) === "array") { lightness = h; dl = h[1] - h[0]; } else { lightness = [h, h]; dl = 0; } return f; }; f.scale = function() { return chroma.scale(f); }; f.hue(hue); return f; } var digits = "0123456789abcdef"; var floor$1 = Math.floor; var random = Math.random; function random$1() { var code = "#"; for (var i2 = 0; i2 < 6; i2++) { code += digits.charAt(floor$1(random() * 16)); } return new Color(code, "hex"); } var log = Math.log; var pow$1 = Math.pow; var floor = Math.floor; var abs$1 = Math.abs; function analyze(data, key2) { if (key2 === void 0) key2 = null; var r = { min: Number.MAX_VALUE, max: Number.MAX_VALUE * -1, sum: 0, values: [], count: 0 }; if (type(data) === "object") { data = Object.values(data); } data.forEach(function(val) { if (key2 && type(val) === "object") { val = val[key2]; } if (val !== void 0 && val !== null && !isNaN(val)) { r.values.push(val); r.sum += val; if (val < r.min) { r.min = val; } if (val > r.max) { r.max = val; } r.count += 1; } }); r.domain = [r.min, r.max]; r.limits = function(mode, num2) { return limits(r, mode, num2); }; return r; } function limits(data, mode, num2) { if (mode === void 0) mode = "equal"; if (num2 === void 0) num2 = 7; if (type(data) == "array") { data = analyze(data); } var min2 = data.min; var max2 = data.max; var values = data.values.sort(function(a, b) { return a - b; }); if (num2 === 1) { return [min2, max2]; } var limits2 = []; if (mode.substr(0, 1) === "c") { limits2.push(min2); limits2.push(max2); } if (mode.substr(0, 1) === "e") { limits2.push(min2); for (var i2 = 1; i2 < num2; i2++) { limits2.push(min2 + i2 / num2 * (max2 - min2)); } limits2.push(max2); } else if (mode.substr(0, 1) === "l") { if (min2 <= 0) { throw new Error( "Logarithmic scales are only possible for values > 0" ); } var min_log = Math.LOG10E * log(min2); var max_log = Math.LOG10E * log(max2); limits2.push(min2); for (var i$12 = 1; i$12 < num2; i$12++) { limits2.push(pow$1(10, min_log + i$12 / num2 * (max_log - min_log))); } limits2.push(max2); } else if (mode.substr(0, 1) === "q") { limits2.push(min2); for (var i$2 = 1; i$2 < num2; i$2++) { var p = (values.length - 1) * i$2 / num2; var pb = floor(p); if (pb === p) { limits2.push(values[pb]); } else { var pr = p - pb; limits2.push(values[pb] * (1 - pr) + values[pb + 1] * pr); } } limits2.push(max2); } else if (mode.substr(0, 1) === "k") { var cluster; var n = values.length; var assignments = new Array(n); var clusterSizes = new Array(num2); var repeat = true; var nb_iters = 0; var centroids = null; centroids = []; centroids.push(min2); for (var i$3 = 1; i$3 < num2; i$3++) { centroids.push(min2 + i$3 / num2 * (max2 - min2)); } centroids.push(max2); while (repeat) { for (var j = 0; j < num2; j++) { clusterSizes[j] = 0; } for (var i$4 = 0; i$4 < n; i$4++) { var value = values[i$4]; var mindist = Number.MAX_VALUE; var best = void 0; for (var j$1 = 0; j$1 < num2; j$1++) { var dist = abs$1(centroids[j$1] - value); if (dist < mindist) { mindist = dist; best = j$1; } clusterSizes[best]++; assignments[i$4] = best; } } var newCentroids = new Array(num2); for (var j$2 = 0; j$2 < num2; j$2++) { newCentroids[j$2] = null; } for (var i$5 = 0; i$5 < n; i$5++) { cluster = assignments[i$5]; if (newCentroids[cluster] === null) { newCentroids[cluster] = values[i$5]; } else { newCentroids[cluster] += values[i$5]; } } for (var j$3 = 0; j$3 < num2; j$3++) { newCentroids[j$3] *= 1 / clusterSizes[j$3]; } repeat = false; for (var j$4 = 0; j$4 < num2; j$4++) { if (newCentroids[j$4] !== centroids[j$4]) { repeat = true; break; } } centroids = newCentroids; nb_iters++; if (nb_iters > 200) { repeat = false; } } var kClusters = {}; for (var j$5 = 0; j$5 < num2; j$5++) { kClusters[j$5] = []; } for (var i$6 = 0; i$6 < n; i$6++) { cluster = assignments[i$6]; kClusters[cluster].push(values[i$6]); } var tmpKMeansBreaks = []; for (var j$6 = 0; j$6 < num2; j$6++) { tmpKMeansBreaks.push(kClusters[j$6][0]); tmpKMeansBreaks.push(kClusters[j$6][kClusters[j$6].length - 1]); } tmpKMeansBreaks = tmpKMeansBreaks.sort(function(a, b) { return a - b; }); limits2.push(tmpKMeansBreaks[0]); for (var i$7 = 1; i$7 < tmpKMeansBreaks.length; i$7 += 2) { var v = tmpKMeansBreaks[i$7]; if (!isNaN(v) && limits2.indexOf(v) === -1) { limits2.push(v); } } } return limits2; } function contrast(a, b) { a = new Color(a); b = new Color(b); var l1 = a.luminance(); var l2 = b.luminance(); return l1 > l2 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05); } var sqrt = Math.sqrt; var pow = Math.pow; var min = Math.min; var max = Math.max; var atan2 = Math.atan2; var abs = Math.abs; var cos = Math.cos; var sin = Math.sin; var exp = Math.exp; var PI = Math.PI; function deltaE(a, b, Kl, Kc, Kh) { if (Kl === void 0) Kl = 1; if (Kc === void 0) Kc = 1; if (Kh === void 0) Kh = 1; var rad2deg = function(rad) { return 360 * rad / (2 * PI); }; var deg2rad = function(deg) { return 2 * PI * deg / 360; }; a = new Color(a); b = new Color(b); var ref = Array.from(a.lab()); var L1 = ref[0]; var a1 = ref[1]; var b1 = ref[2]; var ref$1 = Array.from(b.lab()); var L2 = ref$1[0]; var a2 = ref$1[1]; var b2 = ref$1[2]; var avgL = (L1 + L2) / 2; var C1 = sqrt(pow(a1, 2) + pow(b1, 2)); var C2 = sqrt(pow(a2, 2) + pow(b2, 2)); var avgC = (C1 + C2) / 2; var G = 0.5 * (1 - sqrt(pow(avgC, 7) / (pow(avgC, 7) + pow(25, 7)))); var a1p = a1 * (1 + G); var a2p = a2 * (1 + G); var C1p = sqrt(pow(a1p, 2) + pow(b1, 2)); var C2p = sqrt(pow(a2p, 2) + pow(b2, 2)); var avgCp = (C1p + C2p) / 2; var arctan1 = rad2deg(atan2(b1, a1p)); var arctan2 = rad2deg(atan2(b2, a2p)); var h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360; var h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360; var avgHp = abs(h1p - h2p) > 180 ? (h1p + h2p + 360) / 2 : (h1p + h2p) / 2; var T = 1 - 0.17 * cos(deg2rad(avgHp - 30)) + 0.24 * cos(deg2rad(2 * avgHp)) + 0.32 * cos(deg2rad(3 * avgHp + 6)) - 0.2 * cos(deg2rad(4 * avgHp - 63)); var deltaHp = h2p - h1p; deltaHp = abs(deltaHp) <= 180 ? deltaHp : h2p <= h1p ? deltaHp + 360 : deltaHp - 360; deltaHp = 2 * sqrt(C1p * C2p) * sin(deg2rad(deltaHp) / 2); var deltaL = L2 - L1; var deltaCp = C2p - C1p; var sl = 1 + 0.015 * pow(avgL - 50, 2) / sqrt(20 + pow(avgL - 50, 2)); var sc = 1 + 0.045 * avgCp; var sh = 1 + 0.015 * avgCp * T; var deltaTheta = 30 * exp(-pow((avgHp - 275) / 25, 2)); var Rc = 2 * sqrt(pow(avgCp, 7) / (pow(avgCp, 7) + pow(25, 7))); var Rt = -Rc * sin(2 * deg2rad(deltaTheta)); var result = sqrt( pow(deltaL / (Kl * sl), 2) + pow(deltaCp / (Kc * sc), 2) + pow(deltaHp / (Kh * sh), 2) + Rt * (deltaCp / (Kc * sc)) * (deltaHp / (Kh * sh)) ); return max(0, min(100, result)); } function distance(a, b, mode) { if (mode === void 0) mode = "lab"; a = new Color(a); b = new Color(b); var l1 = a.get(mode); var l2 = b.get(mode); var sum_sq = 0; for (var i2 in l1) { var d = (l1[i2] || 0) - (l2[i2] || 0); sum_sq += d * d; } return Math.sqrt(sum_sq); } function valid() { var args = [], len = arguments.length; while (len--) args[len] = arguments[len]; try { new (Function.prototype.bind.apply(Color, [null].concat(args)))(); return true; } catch (e) { return false; } } var scales = { cool: function cool() { return scale([chroma.hsl(180, 1, 0.9), chroma.hsl(250, 0.7, 0.4)]); }, hot: function hot() { return scale(["#000", "#f00", "#ff0", "#fff"]).mode( "rgb" ); } }; var colorbrewer = { // sequential OrRd: ["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"], PuBu: ["#fff7fb", "#ece7f2", "#d0d1e6", "#a6bddb", "#74a9cf", "#3690c0", "#0570b0", "#045a8d", "#023858"], BuPu: ["#f7fcfd", "#e0ecf4", "#bfd3e6", "#9ebcda", "#8c96c6", "#8c6bb1", "#88419d", "#810f7c", "#4d004b"], Oranges: ["#fff5eb", "#fee6ce", "#fdd0a2", "#fdae6b", "#fd8d3c", "#f16913", "#d94801", "#a63603", "#7f2704"], BuGn: ["#f7fcfd", "#e5f5f9", "#ccece6", "#99d8c9", "#66c2a4", "#41ae76", "#238b45", "#006d2c", "#00441b"], YlOrBr: ["#ffffe5", "#fff7bc", "#fee391", "#fec44f", "#fe9929", "#ec7014", "#cc4c02", "#993404", "#662506"], YlGn: ["#ffffe5", "#f7fcb9", "#d9f0a3", "#addd8e", "#78c679", "#41ab5d", "#238443", "#006837", "#004529"], Reds: ["#fff5f0", "#fee0d2", "#fcbba1", "#fc9272", "#fb6a4a", "#ef3b2c", "#cb181d", "#a50f15", "#67000d"], RdPu: ["#fff7f3", "#fde0dd", "#fcc5c0", "#fa9fb5", "#f768a1", "#dd3497", "#ae017e", "#7a0177", "#49006a"], Greens: ["#f7fcf5", "#e5f5e0", "#c7e9c0", "#a1d99b", "#74c476", "#41ab5d", "#238b45", "#006d2c", "#00441b"], YlGnBu: ["#ffffd9", "#edf8b1", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#253494", "#081d58"], Purples: ["#fcfbfd", "#efedf5", "#dadaeb", "#bcbddc", "#9e9ac8", "#807dba", "#6a51a3", "#54278f", "#3f007d"], GnBu: ["#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5", "#7bccc4", "#4eb3d3", "#2b8cbe", "#0868ac", "#084081"], Greys: ["#ffffff", "#f0f0f0", "#d9d9d9", "#bdbdbd", "#969696", "#737373", "#525252", "#252525", "#000000"], YlOrRd: ["#ffffcc", "#ffeda0", "#fed976", "#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#bd0026", "#800026"], PuRd: ["#f7f4f9", "#e7e1ef", "#d4b9da", "#c994c7", "#df65b0", "#e7298a", "#ce1256", "#980043", "#67001f"], Blues: ["#f7fbff", "#deebf7", "#c6dbef", "#9ecae1", "#6baed6", "#4292c6", "#2171b5", "#08519c", "#08306b"], PuBuGn: ["#fff7fb", "#ece2f0", "#d0d1e6", "#a6bddb", "#67a9cf", "#3690c0", "#02818a", "#016c59", "#014636"], Viridis: ["#440154", "#482777", "#3f4a8a", "#31678e", "#26838f", "#1f9d8a", "#6cce5a", "#b6de2b", "#fee825"], // diverging Spectral: ["#9e0142", "#d53e4f", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#e6f598", "#abdda4", "#66c2a5", "#3288bd", "#5e4fa2"], RdYlGn: ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee08b", "#ffffbf", "#d9ef8b", "#a6d96a", "#66bd63", "#1a9850", "#006837"], RdBu: ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#f7f7f7", "#d1e5f0", "#92c5de", "#4393c3", "#2166ac", "#053061"], PiYG: ["#8e0152", "#c51b7d", "#de77ae", "#f1b6da", "#fde0ef", "#f7f7f7", "#e6f5d0", "#b8e186", "#7fbc41", "#4d9221", "#276419"], PRGn: ["#40004b", "#762a83", "#9970ab", "#c2a5cf", "#e7d4e8", "#f7f7f7", "#d9f0d3", "#a6dba0", "#5aae61", "#1b7837", "#00441b"], RdYlBu: ["#a50026", "#d73027", "#f46d43", "#fdae61", "#fee090", "#ffffbf", "#e0f3f8", "#abd9e9", "#74add1", "#4575b4", "#313695"], BrBG: ["#543005", "#8c510a", "#bf812d", "#dfc27d", "#f6e8c3", "#f5f5f5", "#c7eae5", "#80cdc1", "#35978f", "#01665e", "#003c30"], RdGy: ["#67001f", "#b2182b", "#d6604d", "#f4a582", "#fddbc7", "#ffffff", "#e0e0e0", "#bababa", "#878787", "#4d4d4d", "#1a1a1a"], PuOr: ["#7f3b08", "#b35806", "#e08214", "#fdb863", "#fee0b6", "#f7f7f7", "#d8daeb", "#b2abd2", "#8073ac", "#542788", "#2d004b"], // qualitative Set2: ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3"], Accent: ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17", "#666666"], Set1: ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf", "#999999"], Set3: ["#8dd3c7", "#ffffb3", "#bebada", "#fb8072", "#80b1d3", "#fdb462", "#b3de69", "#fccde5", "#d9d9d9", "#bc80bd", "#ccebc5", "#ffed6f"], Dark2: ["#1b9e77", "#d95f02", "#7570b3", "#e7298a", "#66a61e", "#e6ab02", "#a6761d", "#666666"], Paired: ["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99", "#e31a1c", "#fdbf6f", "#ff7f00", "#cab2d6", "#6a3d9a", "#ffff99", "#b15928"], Pastel2: ["#b3e2cd", "#fdcdac", "#cbd5e8", "#f4cae4", "#e6f5c9", "#fff2ae", "#f1e2cc", "#cccccc"], Pastel1: ["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6", "#ffffcc", "#e5d8bd", "#fddaec", "#f2f2f2"] }; for (var i = 0, list = Object.keys(colorbrewer); i < list.length; i += 1) { var key = list[i]; colorbrewer[key.toLowerCase()] = colorbrewer[key]; } Object.assign(chroma, { average, bezier: bezier$1, blend, cubehelix, mix, interpolate: mix, random: random$1, scale, analyze, contrast, deltaE, distance, limits, valid, scales, input, colors: w3cx11, brewer: colorbrewer }); return chroma; }); } }); // node_modules/smiles-drawer/src/GaussDrawer.js var require_GaussDrawer = __commonJS({ "node_modules/smiles-drawer/src/GaussDrawer.js"(exports, module2) { var Vector2 = require_Vector2(); var convertImage = require_PixelsToSvg(); var chroma = require_chroma(); var GaussDrawer = class { /** * The constructor of the class Graph. * * @param {Vector2[]} points The centres of the gaussians. * @param {Number[]} weights The weights / amplitudes for each gaussian. */ constructor(points, weights, width, height, sigma = 0.3, interval = 0, colormap = null, opacity = 1, normalized = false) { this.points = points; this.weights = weights; this.width = width; this.height = height; this.sigma = sigma; this.interval = interval; this.opacity = opacity; this.normalized = normalized; if (colormap === null) { let piyg11 = [ "#c51b7d", "#de77ae", "#f1b6da", "#fde0ef", "#ffffff", "#e6f5d0", "#b8e186", "#7fbc41", "#4d9221" ]; colormap = piyg11; } this.colormap = colormap; this.canvas = document.createElement("canvas"); this.context = this.canvas.getContext("2d"); this.canvas.width = this.width; this.canvas.height = this.height; } setFromArray(arr_points, arr_weights) { this.points = []; arr_points.forEach((a) => { this.points.push(new Vector2(a[0], a[1])); }); this.weights = []; arr_weights.forEach((w) => { this.weights.push(w); }); } /** * Compute and draw the gaussians. */ draw() { let m = []; for (let x = 0; x < this.width; x++) { let row = []; for (let y = 0; y < this.height; y++) { row.push(0); } m.push(row); } let divisor = 1 / (2 * this.sigma ** 2); for (let i = 0; i < this.points.length; i++) { let v = this.points[i]; let a = this.weights[i]; for (let x = 0; x < this.width; x++) { for (let y = 0; y < this.height; y++) { let v_xy = ((x - v.x) ** 2 + (y - v.y) ** 2) * divisor; let val = a * Math.exp(-v_xy); m[x][y] += val; } } } let abs_max = 1; if (!this.normalized) { let max = -Number.MAX_SAFE_INTEGER; let min = Number.MAX_SAFE_INTEGER; for (let x = 0; x < this.width; x++) { for (let y = 0; y < this.height; y++) { if (m[x][y] < min) { min = m[x][y]; } if (m[x][y] > max) { max = m[x][y]; } } } abs_max = Math.max(Math.abs(min), Math.abs(max)); } const scale = chroma.scale(this.colormap).domain([-1, 1]); for (let x = 0; x < this.width; x++) { for (let y = 0; y < this.height; y++) { if (!this.normalized) { m[x][y] = m[x][y] / abs_max; } if (this.interval !== 0) { m[x][y] = Math.round(m[x][y] / this.interval) * this.interval; } let [r, g, b] = scale(m[x][y]).rgb(); this.setPixel(new Vector2(x, y), r, g, b); } } } /** * Get the canvas as an HTML image. * * @param {CallableFunction} callback */ getImage(callback) { let image = new Image(); image.onload = () => { this.context.imageSmoothingEnabled = false; this.context.drawImage(image, 0, 0, this.width, this.height); if (callback) { callback(image); } }; image.onerror = function(err) { console.log(err); }; image.src = this.canvas.toDataURL(); } /** * Get the canvas as an SVG element. * * @param {CallableFunction} callback */ getSVG() { return convertImage(this.context.getImageData(0, 0, this.width, this.height)); } /** * Set the colour at a specific point on the canvas. * * @param {Vector2} vec The pixel position on the canvas. * @param {Number} r The red colour-component. * @param {Number} g The green colour-component. * @param {Number} b The blue colour-component. * @param {Number} a The alpha colour-component. */ setPixel(vec, r, g, b) { this.context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + this.opacity + ")"; this.context.fillRect(vec.x, vec.y, 1, 1); } }; module2.exports = GaussDrawer; } }); // node_modules/smiles-drawer/src/SvgDrawer.js var require_SvgDrawer = __commonJS({ "node_modules/smiles-drawer/src/SvgDrawer.js"(exports, module2) { var ArrayHelper = require_ArrayHelper(); var Atom = require_Atom(); var DrawerBase = require_DrawerBase(); var Graph = require_Graph(); var Line = require_Line(); var SvgWrapper = require_SvgWrapper(); var ThemeManager = require_ThemeManager(); var Vector2 = require_Vector2(); var GaussDrawer = require_GaussDrawer(); var SvgDrawer = class { constructor(options, clear = true) { this.preprocessor = new DrawerBase(options); this.opts = this.preprocessor.opts; this.clear = clear; this.svgWrapper = null; } /** * Draws the parsed smiles data to an svg element. * * @param {Object} data The tree returned by the smiles parser. * @param {?(String|SVGElement)} target The id of the HTML svg element the structure is drawn to - or the element itself. * @param {String} themeName='dark' The name of the theme to use. Built-in themes are 'light' and 'dark'. * @param {Boolean} infoOnly=false Only output info on the molecule without drawing anything to the canvas. * * @returns {SVGElement} The svg element */ draw(data, target, themeName = "light", weights = null, infoOnly = false, highlight_atoms = [], weightsNormalized = false) { if (target === null || target === "svg") { target = document.createElementNS("http://www.w3.org/2000/svg", "svg"); target.setAttribute("xmlns", "http://www.w3.org/2000/svg"); target.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); target.setAttributeNS(null, "width", this.opts.width); target.setAttributeNS(null, "height", this.opts.height); } else if (target instanceof String) { target = document.getElementById(target); } let optionBackup = { padding: this.opts.padding, compactDrawing: this.opts.compactDrawing }; if (weights !== null) { this.opts.padding += this.opts.weights.additionalPadding; this.opts.compactDrawing = false; } let preprocessor = this.preprocessor; preprocessor.initDraw(data, themeName, infoOnly, highlight_atoms); if (!infoOnly) { this.themeManager = new ThemeManager(this.opts.themes, themeName); if (this.svgWrapper === null || this.clear) { this.svgWrapper = new SvgWrapper(this.themeManager, target, this.opts, this.clear); } } preprocessor.processGraph(); this.svgWrapper.determineDimensions(preprocessor.graph.vertices); this.drawAtomHighlights(preprocessor.opts.debug); this.drawEdges(preprocessor.opts.debug); this.drawVertices(preprocessor.opts.debug); if (weights !== null) { this.drawWeights(weights, weightsNormalized); } if (preprocessor.opts.debug) { console.log(preprocessor.graph); console.log(preprocessor.rings); console.log(preprocessor.ringConnections); } this.svgWrapper.constructSvg(); if (weights !== null) { this.opts.padding = optionBackup.padding; this.opts.compactDrawing = optionBackup.padding; } return target; } /** * Draws the parsed smiles data to a canvas element. * * @param {Object} data The tree returned by the smiles parser. * @param {(String|HTMLCanvasElement)} target The id of the HTML canvas element the structure is drawn to - or the element itself. * @param {String} themeName='dark' The name of the theme to use. Built-in themes are 'light' and 'dark'. * @param {Boolean} infoOnly=false Only output info on the molecule without drawing anything to the canvas. */ drawCanvas(data, target, themeName = "light", infoOnly = false) { let canvas = null; if (typeof target === "string" || target instanceof String) { canvas = document.getElementById(target); } else { canvas = target; } let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); svg.setAttributeNS(null, "viewBox", "0 0 " + 500 + " " + 500); svg.setAttributeNS(null, "width", 500 + ""); svg.setAttributeNS(null, "height", 500 + ""); svg.setAttributeNS(null, "style", "visibility: hidden: position: absolute; left: -1000px"); document.body.appendChild(svg); this.svgDrawer.draw(data, svg, themeName, infoOnly); this.svgDrawer.svgWrapper.toCanvas(canvas, this.svgDrawer.opts.width, this.svgDrawer.opts.height); document.body.removeChild(svg); return target; } /** * Draws a ring inside a provided ring, indicating aromaticity. * * @param {Ring} ring A ring. */ drawAromaticityRing(ring) { let svgWrapper = this.svgWrapper; svgWrapper.drawRing(ring.center.x, ring.center.y, ring.getSize()); } /** * Draw the actual edges as bonds. * * @param {Boolean} debug A boolean indicating whether or not to draw debug helpers. */ drawEdges(debug) { let preprocessor = this.preprocessor, graph = preprocessor.graph, rings = preprocessor.rings, drawn = Array(this.preprocessor.graph.edges.length); drawn.fill(false); graph.traverseBF(0, (vertex) => { let edges = graph.getEdges(vertex.id); for (var i2 = 0; i2 < edges.length; i2++) { let edgeId = edges[i2]; if (!drawn[edgeId]) { drawn[edgeId] = true; this.drawEdge(edgeId, debug); } } }); if (!this.bridgedRing) { for (var i = 0; i < rings.length; i++) { let ring = rings[i]; if (preprocessor.isRingAromatic(ring)) { this.drawAromaticityRing(ring); } } } } /** * Draw the an edge as a bond. * * @param {Number} edgeId An edge id. * @param {Boolean} debug A boolean indicating whether or not to draw debug helpers. */ drawEdge(edgeId, debug) { let preprocessor = this.preprocessor, opts = preprocessor.opts, svgWrapper = this.svgWrapper, edge = preprocessor.graph.edges[edgeId], vertexA = preprocessor.graph.vertices[edge.sourceId], vertexB = preprocessor.graph.vertices[edge.targetId], elementA = vertexA.value.element, elementB = vertexB.value.element; if ((!vertexA.value.isDrawn || !vertexB.value.isDrawn) && preprocessor.opts.atomVisualization === "default") { return; } let a = vertexA.position, b = vertexB.position, normals = preprocessor.getEdgeNormals(edge), sides = ArrayHelper.clone(normals); sides[0].multiplyScalar(10).add(a); sides[1].multiplyScalar(10).add(a); if (edge.bondType === "=" || preprocessor.getRingbondType(vertexA, vertexB) === "=" || edge.isPartOfAromaticRing && preprocessor.bridgedRing) { let inRing = preprocessor.areVerticesInSameRing(vertexA, vertexB); let s = preprocessor.chooseSide(vertexA, vertexB, sides); if (inRing) { let lcr = preprocessor.getLargestOrAromaticCommonRing(vertexA, vertexB); let center = lcr.center; normals[0].multiplyScalar(opts.bondSpacing); normals[1].multiplyScalar(opts.bondSpacing); let line = null; if (center.sameSideAs(vertexA.position, vertexB.position, Vector2.add(a, normals[0]))) { line = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); } else { line = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); } line.shorten(opts.bondLength - opts.shortBondLength * opts.bondLength); if (edge.isPartOfAromaticRing) { svgWrapper.drawLine(line, true); } else { svgWrapper.drawLine(line); } svgWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (edge.center || vertexA.isTerminal() && vertexB.isTerminal() || (s.anCount == 0 && s.bnCount > 1 || s.bnCount == 0 && s.anCount > 1)) { this.multiplyNormals(normals, opts.halfBondSpacing); let lineA = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB), lineB = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); svgWrapper.drawLine(lineA); svgWrapper.drawLine(lineB); } else if (s.sideCount[0] > s.sideCount[1] || s.totalSideCount[0] > s.totalSideCount[1]) { this.multiplyNormals(normals, opts.bondSpacing); let line = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); line.shorten(opts.bondLength - opts.shortBondLength * opts.bondLength); svgWrapper.drawLine(line); svgWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (s.sideCount[0] < s.sideCount[1] || s.totalSideCount[0] <= s.totalSideCount[1]) { this.multiplyNormals(normals, opts.bondSpacing); let line = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); line.shorten(opts.bondLength - opts.shortBondLength * opts.bondLength); svgWrapper.drawLine(line); svgWrapper.drawLine(new Line(a, b, elementA, elementB)); } } else if (edge.bondType === "#") { normals[0].multiplyScalar(opts.bondSpacing / 1.5); normals[1].multiplyScalar(opts.bondSpacing / 1.5); let lineA = new Line(Vector2.add(a, normals[0]), Vector2.add(b, normals[0]), elementA, elementB); let lineB = new Line(Vector2.add(a, normals[1]), Vector2.add(b, normals[1]), elementA, elementB); svgWrapper.drawLine(lineA); svgWrapper.drawLine(lineB); svgWrapper.drawLine(new Line(a, b, elementA, elementB)); } else if (edge.bondType === ".") { } else { let isChiralCenterA = vertexA.value.isStereoCenter; let isChiralCenterB = vertexB.value.isStereoCenter; if (edge.wedge === "up") { svgWrapper.drawWedge(new Line(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB)); } else if (edge.wedge === "down") { svgWrapper.drawDashedWedge(new Line(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB)); } else { svgWrapper.drawLine(new Line(a, b, elementA, elementB, isChiralCenterA, isChiralCenterB)); } } if (debug) { let midpoint = Vector2.midpoint(a, b); svgWrapper.drawDebugText(midpoint.x, midpoint.y, "e: " + edgeId); } } /** * Draw the highlights for atoms to the canvas. * * @param {Boolean} debug */ drawAtomHighlights(debug) { let preprocessor = this.preprocessor; let opts = preprocessor.opts; let graph = preprocessor.graph; let rings = preprocessor.rings; let svgWrapper = this.svgWrapper; for (var i = 0; i < graph.vertices.length; i++) { let vertex = graph.vertices[i]; let atom = vertex.value; for (var j = 0; j < preprocessor.highlight_atoms.length; j++) { let highlight = preprocessor.highlight_atoms[j]; if (atom.class === highlight[0]) { svgWrapper.drawAtomHighlight(vertex.position.x, vertex.position.y, highlight[1]); } } } } /** * Draws the vertices representing atoms to the canvas. * * @param {Boolean} debug A boolean indicating whether or not to draw debug messages to the canvas. */ drawVertices(debug) { let preprocessor = this.preprocessor, opts = preprocessor.opts, graph = preprocessor.graph, rings = preprocessor.rings, svgWrapper = this.svgWrapper; var i = graph.vertices.length; for (var i = 0; i < graph.vertices.length; i++) { let vertex = graph.vertices[i]; let atom = vertex.value; let charge = 0; let isotope = 0; let bondCount = vertex.value.bondCount; let element = atom.element; let hydrogens = Atom.maxBonds[element] - bondCount; let dir2 = vertex.getTextDirection(graph.vertices, atom.hasAttachedPseudoElements); let isTerminal = opts.terminalCarbons || element !== "C" || atom.hasAttachedPseudoElements ? vertex.isTerminal() : false; let isCarbon = atom.element === "C"; if (graph.vertices.length < 3) { isCarbon = false; } if (atom.element === "N" && atom.isPartOfAromaticRing) { hydrogens = 0; } if (atom.bracket) { hydrogens = atom.bracket.hcount; charge = atom.bracket.charge; isotope = atom.bracket.isotope; } if (opts.atomVisualization === "allballs") { svgWrapper.drawBall(vertex.position.x, vertex.position.y, element); } else if (atom.isDrawn && (!isCarbon || atom.drawExplicit || isTerminal || atom.hasAttachedPseudoElements) || graph.vertices.length === 1) { if (opts.atomVisualization === "default") { let attachedPseudoElements = atom.getAttachedPseudoElements(); if (atom.hasAttachedPseudoElements && graph.vertices.length === Object.keys(attachedPseudoElements).length + 1) { dir2 = "right"; } svgWrapper.drawText( vertex.position.x, vertex.position.y, element, hydrogens, dir2, isTerminal, charge, isotope, graph.vertices.length, attachedPseudoElements ); } else if (opts.atomVisualization === "balls") { svgWrapper.drawBall(vertex.position.x, vertex.position.y, element); } } else if (vertex.getNeighbourCount() === 2 && vertex.forcePositioned == true) { let a = graph.vertices[vertex.neighbours[0]].position; let b = graph.vertices[vertex.neighbours[1]].position; let angle = Vector2.threePointangle(vertex.position, a, b); if (Math.abs(Math.PI - angle) < 0.1) { svgWrapper.drawPoint(vertex.position.x, vertex.position.y, element); } } if (debug) { let value = "v: " + vertex.id + " " + ArrayHelper.print(atom.ringbonds); svgWrapper.drawDebugText(vertex.position.x, vertex.position.y, value); } } if (opts.debug) { for (var i = 0; i < rings.length; i++) { let center = rings[i].center; svgWrapper.drawDebugPoint(center.x, center.y, "r: " + rings[i].id); } } } /** * Draw the weights on a background image. * @param {Number[]} weights The weights assigned to each atom. */ drawWeights(weights, weightsNormalized) { if (weights.every((w) => w === 0)) { return; } if (weights.length !== this.preprocessor.graph.atomIdxToVertexId.length) { throw new Error("The number of weights supplied must be equal to the number of (heavy) atoms in the molecule."); } let points = []; for (const atomIdx of this.preprocessor.graph.atomIdxToVertexId) { let vertex = this.preprocessor.graph.vertices[atomIdx]; points.push( new Vector2( vertex.position.x - this.svgWrapper.minX, vertex.position.y - this.svgWrapper.minY ) ); } let gd = new GaussDrawer( points, weights, this.svgWrapper.drawingWidth, this.svgWrapper.drawingHeight, this.opts.weights.sigma, this.opts.weights.interval, this.opts.weights.colormap, this.opts.weights.opacity, weightsNormalized ); gd.draw(); this.svgWrapper.addLayer(gd.getSVG()); } /** * Returns the total overlap score of the current molecule. * * @returns {Number} The overlap score. */ getTotalOverlapScore() { return this.preprocessor.getTotalOverlapScore(); } /** * Returns the molecular formula of the loaded molecule as a string. * * @returns {String} The molecular formula. */ getMolecularFormula(graph = null) { return this.preprocessor.getMolecularFormula(graph); } /** * @param {Array} normals list of normals to multiply * @param {Number} spacing value to multiply normals by */ multiplyNormals(normals, spacing) { normals[0].multiplyScalar(spacing); normals[1].multiplyScalar(spacing); } }; module2.exports = SvgDrawer; } }); // node_modules/smiles-drawer/src/Drawer.js var require_Drawer = __commonJS({ "node_modules/smiles-drawer/src/Drawer.js"(exports, module2) { var SvgDrawer = require_SvgDrawer(); var Drawer = class { /** * The constructor for the class SmilesDrawer. * * @param {Object} options An object containing custom values for different options. It is merged with the default options. */ constructor(options) { this.svgDrawer = new SvgDrawer(options); } /** * Draws the parsed smiles data to a canvas element. * * @param {Object} data The tree returned by the smiles parser. * @param {(String|HTMLCanvasElement)} target The id of the HTML canvas element the structure is drawn to - or the element itself. * @param {String} themeName='dark' The name of the theme to use. Built-in themes are 'light' and 'dark'. * @param {Boolean} infoOnly=false Only output info on the molecule without drawing anything to the canvas. */ draw(data, target, themeName = "light", infoOnly = false, highlight_atoms = []) { let canvas = null; if (typeof target === "string" || target instanceof String) { canvas = document.getElementById(target); } else { canvas = target; } let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); svg.setAttributeNS(null, "viewBox", "0 0 " + this.svgDrawer.opts.width + " " + this.svgDrawer.opts.height); svg.setAttributeNS(null, "width", this.svgDrawer.opts.width + ""); svg.setAttributeNS(null, "height", this.svgDrawer.opts.height + ""); this.svgDrawer.draw(data, svg, themeName, infoOnly, highlight_atoms); this.svgDrawer.svgWrapper.toCanvas(canvas, this.svgDrawer.opts.width, this.svgDrawer.opts.height); } /** * Returns the total overlap score of the current molecule. * * @returns {Number} The overlap score. */ getTotalOverlapScore() { return this.svgDrawer.getTotalOverlapScore(); } /** * Returns the molecular formula of the loaded molecule as a string. * * @returns {String} The molecular formula. */ getMolecularFormula() { this.svgDrawer.getMolecularFormula(); } }; module2.exports = Drawer; } }); // node_modules/smiles-drawer/src/Parser.js var require_Parser = __commonJS({ "node_modules/smiles-drawer/src/Parser.js"(exports, module2) { module2.exports = function() { "use strict"; function peg$subclass(child, parent) { function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); } function peg$SyntaxError(message, expected, found, location) { this.message = message; this.expected = expected; this.found = found; this.location = location; this.name = "SyntaxError"; if (typeof Error.captureStackTrace === "function") { Error.captureStackTrace(this, peg$SyntaxError); } } peg$subclass(peg$SyntaxError, Error); peg$SyntaxError.buildMessage = function(expected, found) { var DESCRIBE_EXPECTATION_FNS = { literal: function(expectation) { return '"' + literalEscape(expectation.text) + '"'; }, "class": function(expectation) { var escapedParts = "", i; for (i = 0; i < expectation.parts.length; i++) { escapedParts += expectation.parts[i] instanceof Array ? classEscape(expectation.parts[i][0]) + "-" + classEscape(expectation.parts[i][1]) : classEscape(expectation.parts[i]); } return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]"; }, any: function(expectation) { return "any character"; }, end: function(expectation) { return "end of input"; }, other: function(expectation) { return expectation.description; } }; function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } function literalEscape(s) { return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }).replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); } function classEscape(s) { return s.replace(/\\/g, "\\\\").replace(/\]/g, "\\]").replace(/\^/g, "\\^").replace(/-/g, "\\-").replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }).replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); } function describeExpectation(expectation) { return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); } function describeExpected(expected2) { var descriptions = new Array(expected2.length), i, j; for (i = 0; i < expected2.length; i++) { descriptions[i] = describeExpectation(expected2[i]); } descriptions.sort(); if (descriptions.length > 0) { for (i = 1, j = 1; i < descriptions.length; i++) { if (descriptions[i - 1] !== descriptions[i]) { descriptions[j] = descriptions[i]; j++; } } descriptions.length = j; } switch (descriptions.length) { case 1: return descriptions[0]; case 2: return descriptions[0] + " or " + descriptions[1]; default: return descriptions.slice(0, -1).join(", ") + ", or " + descriptions[descriptions.length - 1]; } } function describeFound(found2) { return found2 ? '"' + literalEscape(found2) + '"' : "end of input"; } return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; }; function peg$parse(input, options) { options = options !== void 0 ? options : {}; var nOpenParentheses = input.split("(").length - 1; var nCloseParentheses = input.split(")").length - 1; if (nOpenParentheses !== nCloseParentheses) { throw peg$buildSimpleError("The number of opening parentheses does not match the number of closing parentheses.", 0); } var peg$FAILED = {}, peg$startRuleFunctions = { chain: peg$parsechain }, peg$startRuleFunction = peg$parsechain, peg$c0 = function(s) { var branches = []; var rings = []; for (var i = 0; i < s[1].length; i++) { branches.push(s[1][i]); } for (var i = 0; i < s[2].length; i++) { var bond = s[2][i][0] ? s[2][i][0] : "-"; rings.push({ "bond": bond, "id": s[2][i][1] }); } for (var i = 0; i < s[3].length; i++) { branches.push(s[3][i]); } for (var i = 0; i < s[6].length; i++) { branches.push(s[6][i]); } return { "atom": s[0], "isBracket": s[0].element ? true : false, "branches": branches, "branchCount": branches.length, "ringbonds": rings, "ringbondCount": rings.length, "bond": s[4] ? s[4] : "-", "next": s[5], "hasNext": s[5] ? true : false }; }, peg$c1 = "(", peg$c2 = peg$literalExpectation("(", false), peg$c3 = ")", peg$c4 = peg$literalExpectation(")", false), peg$c5 = function(b) { var bond = b[1] ? b[1] : "-"; b[2].branchBond = bond; return b[2]; }, peg$c6 = function(a) { return a; }, peg$c7 = /^[\-=#$:\/\\.]/, peg$c8 = peg$classExpectation(["-", "=", "#", "$", ":", "/", "\\", "."], false, false), peg$c9 = function(b) { return b; }, peg$c10 = "[", peg$c11 = peg$literalExpectation("[", false), peg$c12 = "se", peg$c13 = peg$literalExpectation("se", false), peg$c14 = "as", peg$c15 = peg$literalExpectation("as", false), peg$c16 = "]", peg$c17 = peg$literalExpectation("]", false), peg$c18 = function(b) { return { "isotope": b[1], "element": b[2], "chirality": b[3], "hcount": b[4], "charge": b[5], "class": b[6] }; }, peg$c19 = "B", peg$c20 = peg$literalExpectation("B", false), peg$c21 = "r", peg$c22 = peg$literalExpectation("r", false), peg$c23 = "C", peg$c24 = peg$literalExpectation("C", false), peg$c25 = "l", peg$c26 = peg$literalExpectation("l", false), peg$c27 = /^[NOPSFI]/, peg$c28 = peg$classExpectation(["N", "O", "P", "S", "F", "I"], false, false), peg$c29 = function(o) { if (o.length > 1) return o.join(""); return o; }, peg$c30 = /^[bcnops]/, peg$c31 = peg$classExpectation(["b", "c", "n", "o", "p", "s"], false, false), peg$c32 = "*", peg$c33 = peg$literalExpectation("*", false), peg$c34 = function(w) { return w; }, peg$c35 = /^[A-Z]/, peg$c36 = peg$classExpectation([ ["A", "Z"] ], false, false), peg$c37 = /^[a-z]/, peg$c38 = peg$classExpectation([ ["a", "z"] ], false, false), peg$c39 = function(e) { return e.join(""); }, peg$c40 = "%", peg$c41 = peg$literalExpectation("%", false), peg$c42 = /^[1-9]/, peg$c43 = peg$classExpectation([ ["1", "9"] ], false, false), peg$c44 = /^[0-9]/, peg$c45 = peg$classExpectation([ ["0", "9"] ], false, false), peg$c46 = function(r) { if (r.length == 1) return Number(r); return Number(r.join("").replace("%", "")); }, peg$c47 = "@", peg$c48 = peg$literalExpectation("@", false), peg$c49 = "TH", peg$c50 = peg$literalExpectation("TH", false), peg$c51 = /^[12]/, peg$c52 = peg$classExpectation(["1", "2"], false, false), peg$c53 = "AL", peg$c54 = peg$literalExpectation("AL", false), peg$c55 = "SP", peg$c56 = peg$literalExpectation("SP", false), peg$c57 = /^[1-3]/, peg$c58 = peg$classExpectation([ ["1", "3"] ], false, false), peg$c59 = "TB", peg$c60 = peg$literalExpectation("TB", false), peg$c61 = "OH", peg$c62 = peg$literalExpectation("OH", false), peg$c63 = function(c) { if (!c[1]) return "@"; if (c[1] == "@") return "@@"; return c[1].join("").replace(",", ""); }, peg$c64 = function(c) { return c; }, peg$c65 = "+", peg$c66 = peg$literalExpectation("+", false), peg$c67 = function(c) { if (!c[1]) return 1; if (c[1] != "+") return Number(c[1].join("")); return 2; }, peg$c68 = "-", peg$c69 = peg$literalExpectation("-", false), peg$c70 = function(c) { if (!c[1]) return -1; if (c[1] != "-") return -Number(c[1].join("")); return -2; }, peg$c71 = "H", peg$c72 = peg$literalExpectation("H", false), peg$c73 = function(h) { if (h[1]) return Number(h[1]); return 1; }, peg$c74 = ":", peg$c75 = peg$literalExpectation(":", false), peg$c76 = /^[0]/, peg$c77 = peg$classExpectation(["0"], false, false), peg$c78 = function(c) { return Number(c[1][0] + c[1][1].join("")); }, peg$c79 = function(i) { return Number(i.join("")); }, peg$currPos = 0, peg$savedPos = 0, peg$posDetailsCache = [{ line: 1, column: 1 }], peg$maxFailPos = 0, peg$maxFailExpected = [], peg$silentFails = 0, peg$result; if ("startRule" in options) { if (!(options.startRule in peg$startRuleFunctions)) { throw new Error(`Can't start parsing from rule "` + options.startRule + '".'); } peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; } function text() { return input.substring(peg$savedPos, peg$currPos); } function location() { return peg$computeLocation(peg$savedPos, peg$currPos); } function expected(description, location2) { location2 = location2 !== void 0 ? location2 : peg$computeLocation(peg$savedPos, peg$currPos); throw peg$buildStructuredError( [peg$otherExpectation(description)], input.substring(peg$savedPos, peg$currPos), location2 ); } function error(message, location2) { location2 = location2 !== void 0 ? location2 : peg$computeLocation(peg$savedPos, peg$currPos); throw peg$buildSimpleError(message, location2); } function peg$literalExpectation(text2, ignoreCase) { return { type: "literal", text: text2, ignoreCase }; } function peg$classExpectation(parts, inverted, ignoreCase) { return { type: "class", parts, inverted, ignoreCase }; } function peg$anyExpectation() { return { type: "any" }; } function peg$endExpectation() { return { type: "end" }; } function peg$otherExpectation(description) { return { type: "other", description }; } function peg$computePosDetails(pos) { var details = peg$posDetailsCache[pos], p; if (details) { return details; } else { p = pos - 1; while (!peg$posDetailsCache[p]) { p--; } details = peg$posDetailsCache[p]; details = { line: details.line, column: details.column }; while (p < pos) { if (input.charCodeAt(p) === 10) { details.line++; details.column = 1; } else { details.column++; } p++; } peg$posDetailsCache[pos] = details; return details; } } function peg$computeLocation(startPos, endPos) { var startPosDetails = peg$computePosDetails(startPos), endPosDetails = peg$computePosDetails(endPos); return { start: { offset: startPos, line: startPosDetails.line, column: startPosDetails.column }, end: { offset: endPos, line: endPosDetails.line, column: endPosDetails.column } }; } function peg$fail(expected2) { if (peg$currPos < peg$maxFailPos) { return; } if (peg$currPos > peg$maxFailPos) { peg$maxFailPos = peg$currPos; peg$maxFailExpected = []; } peg$maxFailExpected.push(expected2); } function peg$buildSimpleError(message, location2) { return new peg$SyntaxError(message, null, null, location2); } function peg$buildStructuredError(expected2, found, location2) { return new peg$SyntaxError( peg$SyntaxError.buildMessage(expected2, found), expected2, found, location2 ); } function peg$parsechain() { var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; s0 = peg$currPos; s1 = peg$currPos; s2 = peg$parseatom(); if (s2 !== peg$FAILED) { s3 = []; s4 = peg$parsebranch(); while (s4 !== peg$FAILED) { s3.push(s4); s4 = peg$parsebranch(); } if (s3 !== peg$FAILED) { s4 = []; s5 = peg$currPos; s6 = peg$parsebond(); if (s6 === peg$FAILED) { s6 = null; } if (s6 !== peg$FAILED) { s7 = peg$parsering(); if (s7 !== peg$FAILED) { s6 = [s6, s7]; s5 = s6; } else { peg$currPos = s5; s5 = peg$FAILED; } } else { peg$currPos = s5; s5 = peg$FAILED; } while (s5 !== peg$FAILED) { s4.push(s5); s5 = peg$currPos; s6 = peg$parsebond(); if (s6 === peg$FAILED) { s6 = null; } if (s6 !== peg$FAILED) { s7 = peg$parsering(); if (s7 !== peg$FAILED) { s6 = [s6, s7]; s5 = s6; } else { peg$currPos = s5; s5 = peg$FAILED; } } else { peg$currPos = s5; s5 = peg$FAILED; } } if (s4 !== peg$FAILED) { s5 = []; s6 = peg$parsebranch(); while (s6 !== peg$FAILED) { s5.push(s6); s6 = peg$parsebranch(); } if (s5 !== peg$FAILED) { s6 = peg$parsebond(); if (s6 === peg$FAILED) { s6 = null; } if (s6 !== peg$FAILED) { s7 = peg$parsechain(); if (s7 === peg$FAILED) { s7 = null; } if (s7 !== peg$FAILED) { s8 = []; s9 = peg$parsebranch(); while (s9 !== peg$FAILED) { s8.push(s9); s9 = peg$parsebranch(); } if (s8 !== peg$FAILED) { s2 = [s2, s3, s4, s5, s6, s7, s8]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c0(s1); } s0 = s1; return s0; } function peg$parsebranch() { var s0, s1, s2, s3, s4, s5; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 40) { s2 = peg$c1; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c2); } } if (s2 !== peg$FAILED) { s3 = peg$parsebond(); if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s4 = peg$parsechain(); if (s4 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 41) { s5 = peg$c3; peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c4); } } if (s5 !== peg$FAILED) { s2 = [s2, s3, s4, s5]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c5(s1); } s0 = s1; return s0; } function peg$parseatom() { var s0, s1; s0 = peg$currPos; s1 = peg$parseorganicsymbol(); if (s1 === peg$FAILED) { s1 = peg$parsearomaticsymbol(); if (s1 === peg$FAILED) { s1 = peg$parsebracketatom(); if (s1 === peg$FAILED) { s1 = peg$parsewildcard(); } } } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c6(s1); } s0 = s1; return s0; } function peg$parsebond() { var s0, s1; s0 = peg$currPos; if (peg$c7.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); if (s1 === input.charAt(peg$currPos + 1)) { s1 = peg$FAILED; if (peg$silentFails === 0) { throw peg$buildSimpleError("The parser encountered a bond repetition.", peg$currPos + 1); } } peg$currPos++; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c8); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c9(s1); } s0 = s1; return s0; } function peg$parsebracketatom() { var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 91) { s2 = peg$c10; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c11); } } if (s2 !== peg$FAILED) { s3 = peg$parseisotope(); if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { if (input.substr(peg$currPos, 2) === peg$c12) { s4 = peg$c12; peg$currPos += 2; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c13); } } if (s4 === peg$FAILED) { if (input.substr(peg$currPos, 2) === peg$c14) { s4 = peg$c14; peg$currPos += 2; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c15); } } if (s4 === peg$FAILED) { s4 = peg$parsearomaticsymbol(); if (s4 === peg$FAILED) { s4 = peg$parseelementsymbol(); if (s4 === peg$FAILED) { s4 = peg$parsewildcard(); } } } } if (s4 !== peg$FAILED) { s5 = peg$parsechiral(); if (s5 === peg$FAILED) { s5 = null; } if (s5 !== peg$FAILED) { s6 = peg$parsehcount(); if (s6 === peg$FAILED) { s6 = null; } if (s6 !== peg$FAILED) { s7 = peg$parsecharge(); if (s7 === peg$FAILED) { s7 = null; } if (s7 !== peg$FAILED) { s8 = peg$parseclass(); if (s8 === peg$FAILED) { s8 = null; } if (s8 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 93) { s9 = peg$c16; peg$currPos++; } else { s9 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c17); } } if (s9 !== peg$FAILED) { s2 = [s2, s3, s4, s5, s6, s7, s8, s9]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c18(s1); } s0 = s1; return s0; } function peg$parseorganicsymbol() { var s0, s1, s2, s3; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 66) { s2 = peg$c19; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c20); } } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 114) { s3 = peg$c21; peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c22); } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 === peg$FAILED) { s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 67) { s2 = peg$c23; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c24); } } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 108) { s3 = peg$c25; peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c26); } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 === peg$FAILED) { if (peg$c27.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); peg$currPos++; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c28); } } } } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c29(s1); } s0 = s1; return s0; } function peg$parsearomaticsymbol() { var s0, s1; s0 = peg$currPos; if (peg$c30.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); peg$currPos++; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c31); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c6(s1); } s0 = s1; return s0; } function peg$parsewildcard() { var s0, s1; s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 42) { s1 = peg$c32; peg$currPos++; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c33); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c34(s1); } s0 = s1; return s0; } function peg$parseelementsymbol() { var s0, s1, s2, s3; s0 = peg$currPos; s1 = peg$currPos; if (peg$c35.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c36); } } if (s2 !== peg$FAILED) { if (peg$c37.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c38); } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c39(s1); } s0 = s1; return s0; } function peg$parsering() { var s0, s1, s2, s3, s4; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 37) { s2 = peg$c40; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c41); } } if (s2 !== peg$FAILED) { if (peg$c42.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s3 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s4 = input.charAt(peg$currPos); peg$currPos++; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s4 !== peg$FAILED) { s2 = [s2, s3, s4]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 === peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); peg$currPos++; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c46(s1); } s0 = s1; return s0; } function peg$parsechiral() { var s0, s1, s2, s3, s4, s5, s6; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 64) { s2 = peg$c47; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c48); } } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 64) { s3 = peg$c47; peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c48); } } if (s3 === peg$FAILED) { s3 = peg$currPos; if (input.substr(peg$currPos, 2) === peg$c49) { s4 = peg$c49; peg$currPos += 2; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c50); } } if (s4 !== peg$FAILED) { if (peg$c51.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c52); } } if (s5 !== peg$FAILED) { s4 = [s4, s5]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } if (s3 === peg$FAILED) { s3 = peg$currPos; if (input.substr(peg$currPos, 2) === peg$c53) { s4 = peg$c53; peg$currPos += 2; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c54); } } if (s4 !== peg$FAILED) { if (peg$c51.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c52); } } if (s5 !== peg$FAILED) { s4 = [s4, s5]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } if (s3 === peg$FAILED) { s3 = peg$currPos; if (input.substr(peg$currPos, 2) === peg$c55) { s4 = peg$c55; peg$currPos += 2; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c56); } } if (s4 !== peg$FAILED) { if (peg$c57.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c58); } } if (s5 !== peg$FAILED) { s4 = [s4, s5]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } if (s3 === peg$FAILED) { s3 = peg$currPos; if (input.substr(peg$currPos, 2) === peg$c59) { s4 = peg$c59; peg$currPos += 2; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c60); } } if (s4 !== peg$FAILED) { if (peg$c42.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s5 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s6 = input.charAt(peg$currPos); peg$currPos++; } else { s6 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s6 === peg$FAILED) { s6 = null; } if (s6 !== peg$FAILED) { s4 = [s4, s5, s6]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } if (s3 === peg$FAILED) { s3 = peg$currPos; if (input.substr(peg$currPos, 2) === peg$c61) { s4 = peg$c61; peg$currPos += 2; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c62); } } if (s4 !== peg$FAILED) { if (peg$c42.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s5 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s6 = input.charAt(peg$currPos); peg$currPos++; } else { s6 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s6 === peg$FAILED) { s6 = null; } if (s6 !== peg$FAILED) { s4 = [s4, s5, s6]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } } } } } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c63(s1); } s0 = s1; return s0; } function peg$parsecharge() { var s0, s1; s0 = peg$currPos; s1 = peg$parseposcharge(); if (s1 === peg$FAILED) { s1 = peg$parsenegcharge(); } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c64(s1); } s0 = s1; return s0; } function peg$parseposcharge() { var s0, s1, s2, s3, s4, s5; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 43) { s2 = peg$c65; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c66); } } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 43) { s3 = peg$c65; peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c66); } } if (s3 === peg$FAILED) { s3 = peg$currPos; if (peg$c42.test(input.charAt(peg$currPos))) { s4 = input.charAt(peg$currPos); peg$currPos++; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s4 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s5 === peg$FAILED) { s5 = null; } if (s5 !== peg$FAILED) { s4 = [s4, s5]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c67(s1); } s0 = s1; return s0; } function peg$parsenegcharge() { var s0, s1, s2, s3, s4, s5; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 45) { s2 = peg$c68; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c69); } } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 45) { s3 = peg$c68; peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c69); } } if (s3 === peg$FAILED) { s3 = peg$currPos; if (peg$c42.test(input.charAt(peg$currPos))) { s4 = input.charAt(peg$currPos); peg$currPos++; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s4 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s5 = input.charAt(peg$currPos); peg$currPos++; } else { s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s5 === peg$FAILED) { s5 = null; } if (s5 !== peg$FAILED) { s4 = [s4, s5]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c70(s1); } s0 = s1; return s0; } function peg$parsehcount() { var s0, s1, s2, s3; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 72) { s2 = peg$c71; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c72); } } if (s2 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c73(s1); } s0 = s1; return s0; } function peg$parseclass() { var s0, s1, s2, s3, s4, s5, s6; s0 = peg$currPos; s1 = peg$currPos; if (input.charCodeAt(peg$currPos) === 58) { s2 = peg$c74; peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c75); } } if (s2 !== peg$FAILED) { s3 = peg$currPos; if (peg$c42.test(input.charAt(peg$currPos))) { s4 = input.charAt(peg$currPos); peg$currPos++; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s4 !== peg$FAILED) { s5 = []; if (peg$c44.test(input.charAt(peg$currPos))) { s6 = input.charAt(peg$currPos); peg$currPos++; } else { s6 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } while (s6 !== peg$FAILED) { s5.push(s6); if (peg$c44.test(input.charAt(peg$currPos))) { s6 = input.charAt(peg$currPos); peg$currPos++; } else { s6 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } } if (s5 !== peg$FAILED) { s4 = [s4, s5]; s3 = s4; } else { peg$currPos = s3; s3 = peg$FAILED; } } else { peg$currPos = s3; s3 = peg$FAILED; } if (s3 === peg$FAILED) { if (peg$c76.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c77); } } } if (s3 !== peg$FAILED) { s2 = [s2, s3]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c78(s1); } s0 = s1; return s0; } function peg$parseisotope() { var s0, s1, s2, s3, s4; s0 = peg$currPos; s1 = peg$currPos; if (peg$c42.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c43); } } if (s2 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { if (peg$c44.test(input.charAt(peg$currPos))) { s4 = input.charAt(peg$currPos); peg$currPos++; } else { s4 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c45); } } if (s4 === peg$FAILED) { s4 = null; } if (s4 !== peg$FAILED) { s2 = [s2, s3, s4]; s1 = s2; } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } } else { peg$currPos = s1; s1 = peg$FAILED; } if (s1 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c79(s1); } s0 = s1; return s0; } peg$result = peg$startRuleFunction(); if (peg$result !== peg$FAILED && peg$currPos === input.length) { return peg$result; } else { if (peg$result !== peg$FAILED && peg$currPos < input.length) { peg$fail(peg$endExpectation()); } throw peg$buildStructuredError( peg$maxFailExpected, peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, peg$maxFailPos < input.length ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) ); } } return { SyntaxError: peg$SyntaxError, parse: peg$parse }; }(); } }); // node_modules/smiles-drawer/src/Reaction.js var require_Reaction = __commonJS({ "node_modules/smiles-drawer/src/Reaction.js"(exports, module2) { var Parser = require_Parser(); var Reaction = class { /** * The constructor for the class Reaction. * * @param {string} reactionSmiles A reaction SMILES. */ constructor(reactionSmiles) { this.reactantsSmiles = []; this.reagentsSmiles = []; this.productsSmiles = []; this.reactantsWeights = []; this.reagentsWeights = []; this.productsWeights = []; this.reactants = []; this.reagents = []; this.products = []; let parts = reactionSmiles.split(">"); if (parts.length !== 3) { throw new Error("Invalid reaction SMILES. Did you add fewer than or more than two '>'?"); } if (parts[0] !== "") { this.reactantsSmiles = parts[0].split("."); } if (parts[1] !== "") { this.reagentsSmiles = parts[1].split("."); } if (parts[2] !== "") { this.productsSmiles = parts[2].split("."); } for (var i = 0; i < this.reactantsSmiles.length; i++) { this.reactants.push(Parser.parse(this.reactantsSmiles[i])); } for (var i = 0; i < this.reagentsSmiles.length; i++) { this.reagents.push(Parser.parse(this.reagentsSmiles[i])); } for (var i = 0; i < this.productsSmiles.length; i++) { this.products.push(Parser.parse(this.productsSmiles[i])); } } }; module2.exports = Reaction; } }); // node_modules/smiles-drawer/src/ReactionParser.js var require_ReactionParser = __commonJS({ "node_modules/smiles-drawer/src/ReactionParser.js"(exports, module2) { var Reaction = require_Reaction(); var ReactionParser = class { /** * Returns the hex code of a color associated with a key from the current theme. * * @param {String} reactionSmiles A reaction SMILES. * @returns {Reaction} A reaction object. */ static parse(reactionSmiles) { let reaction = new Reaction(reactionSmiles); return reaction; } }; module2.exports = ReactionParser; } }); // node_modules/smiles-drawer/src/FormulaToCommonName.js var require_FormulaToCommonName = __commonJS({ "node_modules/smiles-drawer/src/FormulaToCommonName.js"(exports, module2) { var formulaToCommonName = { "C2H4O2": "acetic acid", "C3H6O": "acetone", "C2H3N": "acetonitrile", "C6H6": "benzene", "CCl4": "carbon tetrachloride", "C6H5Cl": "chlorobenzene", "CHCl3": "chloroform", "C6H12": "cyclohexane", "C2H4Cl2": "1,2-dichloroethane", "C4H10O3": "diethylene glycol", "C6H14O3": "diglyme", "C4H10O2": "DME", "C3H7NO": "DMF", "C2H6OS": "DMSO", "C2H6O": "ethanol", "C2H6O2": "ethylene glycol", "C3H8O3": "glycerin", "C7H16": "heptane", "C6H18N3OP": "HMPA", "C6H18N3P": "HMPT", "C6H14": "hexane", "CH4O": "methanol", "C5H12O": "MTBE", "CH2Cl2": "methylene chloride", "CH5H9NO": "NMP", "CH3NO2": "nitromethane", "C5H12": "pentane", "C5H5N": "pyridine", "C7H8": "toluene", "C6H15N": "triethyl amine", "H2O": "water" }; module2.exports = formulaToCommonName; } }); // node_modules/smiles-drawer/src/ReactionDrawer.js var require_ReactionDrawer = __commonJS({ "node_modules/smiles-drawer/src/ReactionDrawer.js"(exports, module2) { var SvgDrawer = require_SvgDrawer(); var SvgWrapper = require_SvgWrapper(); var Options = require_Options(); var ThemeManager = require_ThemeManager(); var formulaToCommonName = require_FormulaToCommonName(); var ReactionDrawer = class { /** * The constructor for the class ReactionDrawer. * * @param {Object} options An object containing reaction drawing specitic options. * @param {Object} moleculeOptions An object containing molecule drawing specific options. */ constructor(options, moleculeOptions) { this.defaultOptions = { scale: moleculeOptions.scale > 0 ? moleculeOptions.scale : 1, fontSize: moleculeOptions.fontSizeLarge * 0.8, fontFamily: "Arial, Helvetica, sans-serif", spacing: 10, plus: { size: 9, thickness: 1 }, arrow: { length: moleculeOptions.bondLength * 4, headSize: 6, thickness: 1, margin: 3 }, weights: { normalize: false } }; this.opts = Options.extend(true, this.defaultOptions, options); this.drawer = new SvgDrawer(moleculeOptions); this.molOpts = this.drawer.opts; } /** * Draws the parsed reaction smiles data to a canvas element. * * @param {Object} reaction The reaction object returned by the reaction smiles parser. * @param {(String|SVGElement)} target The id of the HTML canvas element the structure is drawn to - or the element itself. * @param {String} themeName='dark' The name of the theme to use. Built-in themes are 'light' and 'dark'. * @param {?Object} weights=null The weights for reactants, agents, and products. * @param {String} textAbove='{reagents}' The text above the arrow. * @param {String} textBelow='' The text below the arrow. * @param {?Object} weights=null The weights for reactants, agents, and products. * @param {Boolean} infoOnly=false Only output info on the molecule without drawing anything to the canvas. * * @returns {SVGElement} The svg element */ draw(reaction, target, themeName = "light", weights = null, textAbove = "{reagents}", textBelow = "", infoOnly = false) { this.themeManager = new ThemeManager(this.molOpts.themes, themeName); if (this.opts.weights.normalize) { let max = -Number.MAX_SAFE_INTEGER; let min = Number.MAX_SAFE_INTEGER; if (weights.hasOwnProperty("reactants")) { for (let i2 = 0; i2 < weights.reactants.length; i2++) { for (let j = 0; j < weights.reactants[i2].length; j++) { if (weights.reactants[i2][j] < min) { min = weights.reactants[i2][j]; } if (weights.reactants[i2][j] > max) { max = weights.reactants[i2][j]; } } } } if (weights.hasOwnProperty("reagents")) { for (let i2 = 0; i2 < weights.reagents.length; i2++) { for (let j = 0; j < weights.reagents[i2].length; j++) { if (weights.reagents[i2][j] < min) { min = weights.reagents[i2][j]; } if (weights.reagents[i2][j] > max) { max = weights.reagents[i2][j]; } } } } if (weights.hasOwnProperty("products")) { for (let i2 = 0; i2 < weights.products.length; i2++) { for (let j = 0; j < weights.products[i2].length; j++) { if (weights.products[i2][j] < min) { min = weights.products[i2][j]; } if (weights.products[i2][j] > max) { max = weights.products[i2][j]; } } } } let abs_max = Math.max(Math.abs(min), Math.abs(max)); if (abs_max === 0) { abs_max = 1; } if (weights.hasOwnProperty("reactants")) { for (let i2 = 0; i2 < weights.reactants.length; i2++) { for (let j = 0; j < weights.reactants[i2].length; j++) { weights.reactants[i2][j] /= abs_max; } } } if (weights.hasOwnProperty("reagents")) { for (let i2 = 0; i2 < weights.reagents.length; i2++) { for (let j = 0; j < weights.reagents[i2].length; j++) { weights.reagents[i2][j] /= abs_max; } } } if (weights.hasOwnProperty("products")) { for (let i2 = 0; i2 < weights.products.length; i2++) { for (let j = 0; j < weights.products[i2].length; j++) { weights.products[i2][j] /= abs_max; } } } } let svg = null; if (target === null || target === "svg") { svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); svg.setAttributeNS(null, "width", 500 + ""); svg.setAttributeNS(null, "height", 500 + ""); } else if (typeof target === "string" || target instanceof String) { svg = document.getElementById(target); } else { svg = target; } while (svg.firstChild) { svg.removeChild(svg.firstChild); } let elements = []; let maxHeight = 0; for (var i = 0; i < reaction.reactants.length; i++) { if (i > 0) { elements.push({ width: this.opts.plus.size * this.opts.scale, height: this.opts.plus.size * this.opts.scale, svg: this.getPlus() }); } let reactantWeights = null; if (weights && weights.hasOwnProperty("reactants") && weights.reactants.length > i) { reactantWeights = weights.reactants[i]; } let reactantSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); this.drawer.draw(reaction.reactants[i], reactantSvg, themeName, reactantWeights, infoOnly, [], this.opts.weights.normalize); let element = { width: reactantSvg.viewBox.baseVal.width * this.opts.scale, height: reactantSvg.viewBox.baseVal.height * this.opts.scale, svg: reactantSvg }; elements.push(element); if (element.height > maxHeight) { maxHeight = element.height; } } elements.push({ width: this.opts.arrow.length * this.opts.scale, height: this.opts.arrow.headSize * 2 * this.opts.scale, svg: this.getArrow() }); let reagentsText = ""; for (var i = 0; i < reaction.reagents.length; i++) { if (i > 0) { reagentsText += ", "; } let text = this.drawer.getMolecularFormula(reaction.reagents[i]); if (text in formulaToCommonName) { text = formulaToCommonName[text]; } reagentsText += SvgWrapper.replaceNumbersWithSubscript(text); } textAbove = textAbove.replace("{reagents}", reagentsText); const topText = SvgWrapper.writeText( textAbove, this.themeManager, this.opts.fontSize * this.opts.scale, this.opts.fontFamily, this.opts.arrow.length * this.opts.scale ); let centerOffsetX = (this.opts.arrow.length * this.opts.scale - topText.width) / 2; elements.push({ svg: topText.svg, height: topText.height, width: this.opts.arrow.length * this.opts.scale, offsetX: -(this.opts.arrow.length * this.opts.scale + this.opts.spacing) + centerOffsetX, offsetY: -(topText.height / 2) - this.opts.arrow.margin, position: "relative" }); const bottomText = SvgWrapper.writeText( textBelow, this.themeManager, this.opts.fontSize * this.opts.scale, this.opts.fontFamily, this.opts.arrow.length * this.opts.scale ); centerOffsetX = (this.opts.arrow.length * this.opts.scale - bottomText.width) / 2; elements.push({ svg: bottomText.svg, height: bottomText.height, width: this.opts.arrow.length * this.opts.scale, offsetX: -(this.opts.arrow.length * this.opts.scale + this.opts.spacing) + centerOffsetX, offsetY: bottomText.height / 2 + this.opts.arrow.margin, position: "relative" }); for (var i = 0; i < reaction.products.length; i++) { if (i > 0) { elements.push({ width: this.opts.plus.size * this.opts.scale, height: this.opts.plus.size * this.opts.scale, svg: this.getPlus() }); } let productWeights = null; if (weights && weights.hasOwnProperty("products") && weights.products.length > i) { productWeights = weights.products[i]; } let productSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); this.drawer.draw(reaction.products[i], productSvg, themeName, productWeights, infoOnly, [], this.opts.weights.normalize); let element = { width: productSvg.viewBox.baseVal.width * this.opts.scale, height: productSvg.viewBox.baseVal.height * this.opts.scale, svg: productSvg }; elements.push(element); if (element.height > maxHeight) { maxHeight = element.height; } } let totalWidth = 0; elements.forEach((element) => { var _a, _b; let offsetX = (_a = element.offsetX) != null ? _a : 0; let offsetY = (_b = element.offsetY) != null ? _b : 0; element.svg.setAttributeNS(null, "x", Math.round(totalWidth + offsetX)); element.svg.setAttributeNS(null, "y", Math.round((maxHeight - element.height) / 2 + offsetY)); element.svg.setAttributeNS(null, "width", Math.round(element.width)); element.svg.setAttributeNS(null, "height", Math.round(element.height)); svg.appendChild(element.svg); if (element.position !== "relative") { totalWidth += Math.round(element.width + this.opts.spacing + offsetX); } }); svg.setAttributeNS(null, "viewBox", `0 0 ${totalWidth} ${maxHeight}`); svg.style.width = totalWidth + "px"; svg.style.height = maxHeight + "px"; return svg; } getPlus() { let s = this.opts.plus.size; let w = this.opts.plus.thickness; let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); let rect_h = document.createElementNS("http://www.w3.org/2000/svg", "rect"); let rect_v = document.createElementNS("http://www.w3.org/2000/svg", "rect"); svg.setAttributeNS(null, "id", "plus"); rect_h.setAttributeNS(null, "x", 0); rect_h.setAttributeNS(null, "y", s / 2 - w / 2); rect_h.setAttributeNS(null, "width", s); rect_h.setAttributeNS(null, "height", w); rect_h.setAttributeNS(null, "fill", this.themeManager.getColor("C")); rect_v.setAttributeNS(null, "x", s / 2 - w / 2); rect_v.setAttributeNS(null, "y", 0); rect_v.setAttributeNS(null, "width", w); rect_v.setAttributeNS(null, "height", s); rect_v.setAttributeNS(null, "fill", this.themeManager.getColor("C")); svg.appendChild(rect_h); svg.appendChild(rect_v); svg.setAttributeNS(null, "viewBox", `0 0 ${s} ${s}`); return svg; } getArrowhead() { let s = this.opts.arrow.headSize; let marker = document.createElementNS("http://www.w3.org/2000/svg", "marker"); let polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); marker.setAttributeNS(null, "id", "arrowhead"); marker.setAttributeNS(null, "viewBox", `0 0 ${s} ${s}`); marker.setAttributeNS(null, "markerUnits", "userSpaceOnUse"); marker.setAttributeNS(null, "markerWidth", s); marker.setAttributeNS(null, "markerHeight", s); marker.setAttributeNS(null, "refX", 0); marker.setAttributeNS(null, "refY", s / 2); marker.setAttributeNS(null, "orient", "auto"); marker.setAttributeNS(null, "fill", this.themeManager.getColor("C")); polygon.setAttributeNS(null, "points", `0 0, ${s} ${s / 2}, 0 ${s}`); marker.appendChild(polygon); return marker; } getCDArrowhead() { let s = this.opts.arrow.headSize; let sw = s * (7 / 4.5); let marker = document.createElementNS("http://www.w3.org/2000/svg", "marker"); let path = document.createElementNS("http://www.w3.org/2000/svg", "path"); marker.setAttributeNS(null, "id", "arrowhead"); marker.setAttributeNS(null, "viewBox", `0 0 ${sw} ${s}`); marker.setAttributeNS(null, "markerUnits", "userSpaceOnUse"); marker.setAttributeNS(null, "markerWidth", sw * 2); marker.setAttributeNS(null, "markerHeight", s * 2); marker.setAttributeNS(null, "refX", 2.2); marker.setAttributeNS(null, "refY", 2.2); marker.setAttributeNS(null, "orient", "auto"); marker.setAttributeNS(null, "fill", this.themeManager.getColor("C")); path.setAttributeNS(null, "style", "fill-rule:nonzero;"); path.setAttributeNS(null, "d", "m 0 0 l 7 2.25 l -7 2.25 c 0 0 0.735 -1.084 0.735 -2.28 c 0 -1.196 -0.735 -2.22 -0.735 -2.22 z"); marker.appendChild(path); return marker; } getArrow() { let s = this.opts.arrow.headSize; let l = this.opts.arrow.length; let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); let defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); let line = document.createElementNS("http://www.w3.org/2000/svg", "line"); defs.appendChild(this.getCDArrowhead()); svg.appendChild(defs); svg.setAttributeNS(null, "id", "arrow"); line.setAttributeNS(null, "x1", 0); line.setAttributeNS(null, "y1", -this.opts.arrow.thickness / 2); line.setAttributeNS(null, "x2", l); line.setAttributeNS(null, "y2", -this.opts.arrow.thickness / 2); line.setAttributeNS(null, "stroke-width", this.opts.arrow.thickness); line.setAttributeNS(null, "stroke", this.themeManager.getColor("C")); line.setAttributeNS(null, "marker-end", "url(#arrowhead)"); svg.appendChild(line); svg.setAttributeNS(null, "viewBox", `0 ${-s / 2} ${l + s * (7 / 4.5)} ${s}`); return svg; } }; module2.exports = ReactionDrawer; } }); // node_modules/smiles-drawer/src/SmilesDrawer.js var require_SmilesDrawer = __commonJS({ "node_modules/smiles-drawer/src/SmilesDrawer.js"(exports, module2) { var Drawer = require_Drawer(); var Parser = require_Parser(); var ReactionParser = require_ReactionParser(); var SvgDrawer = require_SvgDrawer(); var ReactionDrawer = require_ReactionDrawer(); var SvgWrapper = require_SvgWrapper(); var Options = require_Options(); var SmilesDrawer2 = class { constructor(moleculeOptions = {}, reactionOptions = {}) { this.drawer = new SvgDrawer(moleculeOptions); this.reactionDrawer = new ReactionDrawer(reactionOptions, JSON.parse(JSON.stringify(this.drawer.opts))); } static apply(moleculeOptions = {}, reactionOptions = {}, attribute = "data-smiles", theme = "light", successCallback = null, errorCallback = null) { const drawer = new SmilesDrawer2(moleculeOptions, reactionOptions); drawer.apply(attribute, theme, successCallback, errorCallback); } apply(attribute = "data-smiles", theme = "light", successCallback = null, errorCallback = null) { let elements = document.querySelectorAll(`[${attribute}]`); elements.forEach((element) => { let smiles = element.getAttribute(attribute); if (smiles === null) { throw Error("No SMILES provided."); } let currentTheme = theme; let weights = null; if (element.hasAttribute("data-smiles-theme")) { currentTheme = element.getAttribute("data-smiles-theme"); } if (element.hasAttribute("data-smiles-weights")) { weights = element.getAttribute("data-smiles-weights").split(",").map(parseFloat); } if (element.hasAttribute("data-smiles-reactant-weights") || element.hasAttribute("data-smiles-reagent-weights") || element.hasAttribute("data-smiles-product-weights")) { weights = { reactants: [], reagents: [], products: [] }; if (element.hasAttribute("data-smiles-reactant-weights")) { weights.reactants = element.getAttribute("data-smiles-reactant-weights").split(";").map((v) => { return v.split(",").map(parseFloat); }); } if (element.hasAttribute("data-smiles-reagent-weights")) { weights.reagents = element.getAttribute("data-smiles-reagent-weights").split(";").map((v) => { return v.split(",").map(parseFloat); }); } if (element.hasAttribute("data-smiles-product-weights")) { weights.products = element.getAttribute("data-smiles-product-weights").split(";").map((v) => { return v.split(",").map(parseFloat); }); } } if (element.hasAttribute("data-smiles-options") || element.hasAttribute("data-smiles-reaction-options")) { let moleculeOptions = {}; if (element.hasAttribute("data-smiles-options")) { moleculeOptions = JSON.parse(element.getAttribute("data-smiles-options").replaceAll("'", '"')); } let reactionOptions = {}; if (element.hasAttribute("data-smiles-reaction-options")) { reactionOptions = JSON.parse(element.getAttribute("data-smiles-reaction-options").replaceAll("'", '"')); } let smilesDrawer = new SmilesDrawer2(moleculeOptions, reactionOptions); smilesDrawer.draw(smiles, element, currentTheme, successCallback, errorCallback, weights); } else { this.draw(smiles, element, currentTheme, successCallback, errorCallback, weights); } }); } /** * Draw the smiles to the target. * @param {String} smiles The SMILES to be depicted. * @param {*} target The target element. * @param {String} theme The theme. * @param {?CallableFunction} successCallback The function called on success. * @param {?CallableFunction} errorCallback The function called on error. * @param {?Number[]|Object} weights The weights for the gaussians. */ draw(smiles, target, theme = "light", successCallback = null, errorCallback = null, weights = null) { let rest = []; [smiles, ...rest] = smiles.split(" "); let info = rest.join(" "); let settings = {}; if (info.includes("__")) { let settingsString = info.substring( info.indexOf("__") + 2, info.lastIndexOf("__") ); settings = JSON.parse(settingsString.replaceAll("'", '"')); } let defaultSettings = { textAboveArrow: "{reagents}", textBelowArrow: "" }; settings = Options.extend(true, defaultSettings, settings); if (smiles.includes(">")) { try { this.drawReaction(smiles, target, theme, settings, weights, successCallback); } catch (err) { if (errorCallback) { errorCallback(err); } else { console.error(err); } } } else { try { this.drawMolecule(smiles, target, theme, weights, successCallback); } catch (err) { if (errorCallback) { errorCallback(err); } else { console.error(err); } } } } drawMolecule(smiles, target, theme, weights, callback) { let parseTree = Parser.parse(smiles); if (target === null || target === "svg") { let svg = this.drawer.draw(parseTree, null, theme, weights); let dims = this.getDimensions(svg); svg.setAttributeNS(null, "width", "" + dims.w); svg.setAttributeNS(null, "height", "" + dims.h); if (callback) { callback(svg); } } else if (target === "canvas") { let canvas = this.svgToCanvas(this.drawer.draw(parseTree, null, theme, weights)); if (callback) { callback(canvas); } } else if (target === "img") { let img = this.svgToImg(this.drawer.draw(parseTree, null, theme, weights)); if (callback) { callback(img); } } else if (target instanceof HTMLImageElement) { this.svgToImg(this.drawer.draw(parseTree, null, theme, weights), target); if (callback) { callback(target); } } else if (target instanceof SVGElement) { this.drawer.draw(parseTree, target, theme, weights); if (callback) { callback(target); } } else { let elements = document.querySelectorAll(target); elements.forEach((element) => { let tag = element.nodeName.toLowerCase(); if (tag === "svg") { this.drawer.draw(parseTree, element, theme, weights); if (callback) { callback(element); } } else if (tag === "canvas") { this.svgToCanvas(this.drawer.draw(parseTree, null, theme, weights), element); if (callback) { callback(element); } } else if (tag === "img") { this.svgToImg(this.drawer.draw(parseTree, null, theme, weights), element); if (callback) { callback(element); } } }); } } drawReaction(smiles, target, theme, settings, weights, callback) { let reaction = ReactionParser.parse(smiles); if (target === null || target === "svg") { let svg = this.reactionDrawer.draw(reaction, null, theme); let dims = this.getDimensions(svg); svg.setAttributeNS(null, "width", "" + dims.w); svg.setAttributeNS(null, "height", "" + dims.h); if (callback) { callback(svg); } } else if (target === "canvas") { let canvas = this.svgToCanvas(this.reactionDrawer.draw(reaction, null, theme, weights, settings.textAboveArrow, settings.textBelowArrow)); if (callback) { callback(canvas); } } else if (target === "img") { let img = this.svgToImg(this.reactionDrawer.draw(reaction, null, theme, weights, settings.textAboveArrow, settings.textBelowArrow)); if (callback) { callback(img); } } else if (target instanceof HTMLImageElement) { this.svgToImg(this.reactionDrawer.draw(reaction, null, theme, weights, settings.textAboveArrow, settings.textBelowArrow), target); if (callback) { callback(target); } } else if (target instanceof SVGElement) { this.reactionDrawer.draw(reaction, target, theme, weights, settings.textAboveArrow, settings.textBelowArrow); if (callback) { callback(target); } } else { let elements = document.querySelectorAll(target); elements.forEach((element) => { let tag = element.nodeName.toLowerCase(); if (tag === "svg") { this.reactionDrawer.draw(reaction, element, theme, weights, settings.textAboveArrow, settings.textBelowArrow); if (this.reactionDrawer.opts.scale <= 0) { element.style.width = null; element.style.height = null; } if (callback) { callback(element); } } else if (tag === "canvas") { this.svgToCanvas(this.reactionDrawer.draw(reaction, null, theme, weights, settings.textAboveArrow, settings.textBelowArrow), element); if (callback) { callback(element); } } else if (tag === "img") { this.svgToImg(this.reactionDrawer.draw(reaction, null, theme, weights, settings.textAboveArrow, settings.textBelowArrow), element); if (callback) { callback(element); } } }); } } svgToCanvas(svg, canvas = null) { if (canvas === null) { canvas = document.createElement("canvas"); } let dims = this.getDimensions(canvas, svg); SvgWrapper.svgToCanvas(svg, canvas, dims.w, dims.h); return canvas; } svgToImg(svg, img = null) { if (img === null) { img = document.createElement("img"); } let dims = this.getDimensions(img, svg); SvgWrapper.svgToImg(svg, img, dims.w, dims.h); return img; } /** * * @param {HTMLImageElement|HTMLCanvasElement|SVGElement} element * @param {SVGElement} svg * @returns {{w: Number, h: Number}} The width and height. */ getDimensions(element, svg = null) { let w = this.drawer.opts.width; let h = this.drawer.opts.height; if (this.drawer.opts.scale <= 0) { if (w === null) { w = element.width; } if (h === null) { h = element.height; } if (element.style.width !== "") { w = parseInt(element.style.width); } if (element.style.height !== "") { h = parseInt(element.style.height); } } else if (svg) { w = parseFloat(svg.style.width); h = parseFloat(svg.style.height); } return { w, h }; } }; module2.exports = SmilesDrawer2; } }); // node_modules/smiles-drawer/app.js var require_app = __commonJS({ "node_modules/smiles-drawer/app.js"(exports, module2) { var Drawer = require_Drawer(); var Parser = require_Parser(); var ReactionParser = require_ReactionParser(); var SvgDrawer = require_SvgDrawer(); var ReactionDrawer = require_ReactionDrawer(); var SmiDrawer = require_SmilesDrawer(); var GaussDrawer = require_GaussDrawer(); var canUseDOM = !!(typeof window !== "undefined" && window.document && window.document.createElement); var SmilesDrawer2 = { Version: "1.0.0" }; SmilesDrawer2.Drawer = Drawer; SmilesDrawer2.Parser = Parser; SmilesDrawer2.SvgDrawer = SvgDrawer; SmilesDrawer2.ReactionDrawer = ReactionDrawer; SmilesDrawer2.ReactionParser = ReactionParser; SmilesDrawer2.GaussDrawer = GaussDrawer; SmilesDrawer2.clean = function(smiles) { return smiles.replace(/[^A-Za-z0-9@\.\+\-\?!\(\)\[\]\{\}/\\=#\$:\*]/g, ""); }; SmilesDrawer2.apply = function(options, selector = "canvas[data-smiles]", themeName = "light", onError = null) { let smilesDrawer = new Drawer(options); let elements = document.querySelectorAll(selector); for (var i = 0; i < elements.length; i++) { let element = elements[i]; SmilesDrawer2.parse(element.getAttribute("data-smiles"), function(tree) { smilesDrawer.draw(tree, element, themeName, false); }, function(err) { if (onError) { onError(err); } }); } }; SmilesDrawer2.parse = function(smiles, successCallback, errorCallback) { try { if (successCallback) { successCallback(Parser.parse(smiles)); } } catch (err) { if (errorCallback) { errorCallback(err); } } }; SmilesDrawer2.parseReaction = function(reactionSmiles, successCallback, errorCallback) { try { if (successCallback) { successCallback(ReactionParser.parse(reactionSmiles)); } } catch (err) { if (errorCallback) { errorCallback(err); } } }; if (canUseDOM) { window.SmilesDrawer = SmilesDrawer2; window.SmiDrawer = SmiDrawer; } SmilesDrawer2.SmiDrawer = SmiDrawer; if (!Array.prototype.fill) { Object.defineProperty(Array.prototype, "fill", { value: function(value) { if (this == null) { throw new TypeError("this is null or not defined"); } var O = Object(this); var len = O.length >>> 0; var start = arguments[1]; var relativeStart = start >> 0; var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); var end = arguments[2]; var relativeEnd = end === void 0 ? len : end >> 0; var final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); while (k < final) { O[k] = value; k++; } return O; } }); } module2.exports = SmilesDrawer2; } }); // node_modules/obsidian-dataview/lib/index.js var require_lib = __commonJS({ "node_modules/obsidian-dataview/lib/index.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); require("obsidian"); var LuxonError = class extends Error { }; var InvalidDateTimeError = class extends LuxonError { constructor(reason) { super(`Invalid DateTime: ${reason.toMessage()}`); } }; var InvalidIntervalError = class extends LuxonError { constructor(reason) { super(`Invalid Interval: ${reason.toMessage()}`); } }; var InvalidDurationError = class extends LuxonError { constructor(reason) { super(`Invalid Duration: ${reason.toMessage()}`); } }; var ConflictingSpecificationError = class extends LuxonError { }; var InvalidUnitError = class extends LuxonError { constructor(unit) { super(`Invalid unit ${unit}`); } }; var InvalidArgumentError = class extends LuxonError { }; var ZoneIsAbstractError = class extends LuxonError { constructor() { super("Zone is an abstract class"); } }; var n = "numeric"; var s = "short"; var l = "long"; var DATE_SHORT = { year: n, month: n, day: n }; var DATE_MED = { year: n, month: s, day: n }; var DATE_MED_WITH_WEEKDAY = { year: n, month: s, day: n, weekday: s }; var DATE_FULL = { year: n, month: l, day: n }; var DATE_HUGE = { year: n, month: l, day: n, weekday: l }; var TIME_SIMPLE = { hour: n, minute: n }; var TIME_WITH_SECONDS = { hour: n, minute: n, second: n }; var TIME_WITH_SHORT_OFFSET = { hour: n, minute: n, second: n, timeZoneName: s }; var TIME_WITH_LONG_OFFSET = { hour: n, minute: n, second: n, timeZoneName: l }; var TIME_24_SIMPLE = { hour: n, minute: n, hourCycle: "h23" }; var TIME_24_WITH_SECONDS = { hour: n, minute: n, second: n, hourCycle: "h23" }; var TIME_24_WITH_SHORT_OFFSET = { hour: n, minute: n, second: n, hourCycle: "h23", timeZoneName: s }; var TIME_24_WITH_LONG_OFFSET = { hour: n, minute: n, second: n, hourCycle: "h23", timeZoneName: l }; var DATETIME_SHORT = { year: n, month: n, day: n, hour: n, minute: n }; var DATETIME_SHORT_WITH_SECONDS = { year: n, month: n, day: n, hour: n, minute: n, second: n }; var DATETIME_MED = { year: n, month: s, day: n, hour: n, minute: n }; var DATETIME_MED_WITH_SECONDS = { year: n, month: s, day: n, hour: n, minute: n, second: n }; var DATETIME_MED_WITH_WEEKDAY = { year: n, month: s, day: n, weekday: s, hour: n, minute: n }; var DATETIME_FULL = { year: n, month: l, day: n, hour: n, minute: n, timeZoneName: s }; var DATETIME_FULL_WITH_SECONDS = { year: n, month: l, day: n, hour: n, minute: n, second: n, timeZoneName: s }; var DATETIME_HUGE = { year: n, month: l, day: n, weekday: l, hour: n, minute: n, timeZoneName: l }; var DATETIME_HUGE_WITH_SECONDS = { year: n, month: l, day: n, weekday: l, hour: n, minute: n, second: n, timeZoneName: l }; var Zone = class { /** * The type of zone * @abstract * @type {string} */ get type() { throw new ZoneIsAbstractError(); } /** * The name of this zone. * @abstract * @type {string} */ get name() { throw new ZoneIsAbstractError(); } get ianaName() { return this.name; } /** * Returns whether the offset is known to be fixed for the whole year. * @abstract * @type {boolean} */ get isUniversal() { throw new ZoneIsAbstractError(); } /** * Returns the offset's common name (such as EST) at the specified timestamp * @abstract * @param {number} ts - Epoch milliseconds for which to get the name * @param {Object} opts - Options to affect the format * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'. * @param {string} opts.locale - What locale to return the offset name in. * @return {string} */ offsetName(ts, opts) { throw new ZoneIsAbstractError(); } /** * Returns the offset's value as a string * @abstract * @param {number} ts - Epoch milliseconds for which to get the offset * @param {string} format - What style of offset to return. * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively * @return {string} */ formatOffset(ts, format) { throw new ZoneIsAbstractError(); } /** * Return the offset in minutes for this zone at the specified timestamp. * @abstract * @param {number} ts - Epoch milliseconds for which to compute the offset * @return {number} */ offset(ts) { throw new ZoneIsAbstractError(); } /** * Return whether this Zone is equal to another zone * @abstract * @param {Zone} otherZone - the zone to compare * @return {boolean} */ equals(otherZone) { throw new ZoneIsAbstractError(); } /** * Return whether this Zone is valid. * @abstract * @type {boolean} */ get isValid() { throw new ZoneIsAbstractError(); } }; var singleton$1 = null; var SystemZone = class extends Zone { /** * Get a singleton instance of the local zone * @return {SystemZone} */ static get instance() { if (singleton$1 === null) { singleton$1 = new SystemZone(); } return singleton$1; } /** @override **/ get type() { return "system"; } /** @override **/ get name() { return new Intl.DateTimeFormat().resolvedOptions().timeZone; } /** @override **/ get isUniversal() { return false; } /** @override **/ offsetName(ts, { format, locale }) { return parseZoneInfo(ts, format, locale); } /** @override **/ formatOffset(ts, format) { return formatOffset(this.offset(ts), format); } /** @override **/ offset(ts) { return -new Date(ts).getTimezoneOffset(); } /** @override **/ equals(otherZone) { return otherZone.type === "system"; } /** @override **/ get isValid() { return true; } }; var dtfCache = {}; function makeDTF(zone) { if (!dtfCache[zone]) { dtfCache[zone] = new Intl.DateTimeFormat("en-US", { hour12: false, timeZone: zone, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", era: "short" }); } return dtfCache[zone]; } var typeToPos = { year: 0, month: 1, day: 2, era: 3, hour: 4, minute: 5, second: 6 }; function hackyOffset(dtf, date) { const formatted = dtf.format(date).replace(/\u200E/g, ""), parsed = /(\d+)\/(\d+)\/(\d+) (AD|BC),? (\d+):(\d+):(\d+)/.exec(formatted), [, fMonth, fDay, fYear, fadOrBc, fHour, fMinute, fSecond] = parsed; return [fYear, fMonth, fDay, fadOrBc, fHour, fMinute, fSecond]; } function partsOffset(dtf, date) { const formatted = dtf.formatToParts(date); const filled = []; for (let i = 0; i < formatted.length; i++) { const { type, value } = formatted[i]; const pos = typeToPos[type]; if (type === "era") { filled[pos] = value; } else if (!isUndefined(pos)) { filled[pos] = parseInt(value, 10); } } return filled; } var ianaZoneCache = {}; var IANAZone = class extends Zone { /** * @param {string} name - Zone name * @return {IANAZone} */ static create(name) { if (!ianaZoneCache[name]) { ianaZoneCache[name] = new IANAZone(name); } return ianaZoneCache[name]; } /** * Reset local caches. Should only be necessary in testing scenarios. * @return {void} */ static resetCache() { ianaZoneCache = {}; dtfCache = {}; } /** * Returns whether the provided string is a valid specifier. This only checks the string's format, not that the specifier identifies a known zone; see isValidZone for that. * @param {string} s - The string to check validity on * @example IANAZone.isValidSpecifier("America/New_York") //=> true * @example IANAZone.isValidSpecifier("Sport~~blorp") //=> false * @deprecated This method returns false for some valid IANA names. Use isValidZone instead. * @return {boolean} */ static isValidSpecifier(s2) { return this.isValidZone(s2); } /** * Returns whether the provided string identifies a real zone * @param {string} zone - The string to check * @example IANAZone.isValidZone("America/New_York") //=> true * @example IANAZone.isValidZone("Fantasia/Castle") //=> false * @example IANAZone.isValidZone("Sport~~blorp") //=> false * @return {boolean} */ static isValidZone(zone) { if (!zone) { return false; } try { new Intl.DateTimeFormat("en-US", { timeZone: zone }).format(); return true; } catch (e) { return false; } } constructor(name) { super(); this.zoneName = name; this.valid = IANAZone.isValidZone(name); } /** @override **/ get type() { return "iana"; } /** @override **/ get name() { return this.zoneName; } /** @override **/ get isUniversal() { return false; } /** @override **/ offsetName(ts, { format, locale }) { return parseZoneInfo(ts, format, locale, this.name); } /** @override **/ formatOffset(ts, format) { return formatOffset(this.offset(ts), format); } /** @override **/ offset(ts) { const date = new Date(ts); if (isNaN(date)) return NaN; const dtf = makeDTF(this.name); let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts ? partsOffset(dtf, date) : hackyOffset(dtf, date); if (adOrBc === "BC") { year = -Math.abs(year) + 1; } const adjustedHour = hour === 24 ? 0 : hour; const asUTC = objToLocalTS({ year, month, day, hour: adjustedHour, minute, second, millisecond: 0 }); let asTS = +date; const over = asTS % 1e3; asTS -= over >= 0 ? over : 1e3 + over; return (asUTC - asTS) / (60 * 1e3); } /** @override **/ equals(otherZone) { return otherZone.type === "iana" && otherZone.name === this.name; } /** @override **/ get isValid() { return this.valid; } }; var intlLFCache = {}; function getCachedLF(locString, opts = {}) { const key = JSON.stringify([locString, opts]); let dtf = intlLFCache[key]; if (!dtf) { dtf = new Intl.ListFormat(locString, opts); intlLFCache[key] = dtf; } return dtf; } var intlDTCache = {}; function getCachedDTF(locString, opts = {}) { const key = JSON.stringify([locString, opts]); let dtf = intlDTCache[key]; if (!dtf) { dtf = new Intl.DateTimeFormat(locString, opts); intlDTCache[key] = dtf; } return dtf; } var intlNumCache = {}; function getCachedINF(locString, opts = {}) { const key = JSON.stringify([locString, opts]); let inf = intlNumCache[key]; if (!inf) { inf = new Intl.NumberFormat(locString, opts); intlNumCache[key] = inf; } return inf; } var intlRelCache = {}; function getCachedRTF(locString, opts = {}) { const { base, ...cacheKeyOpts } = opts; const key = JSON.stringify([locString, cacheKeyOpts]); let inf = intlRelCache[key]; if (!inf) { inf = new Intl.RelativeTimeFormat(locString, opts); intlRelCache[key] = inf; } return inf; } var sysLocaleCache = null; function systemLocale() { if (sysLocaleCache) { return sysLocaleCache; } else { sysLocaleCache = new Intl.DateTimeFormat().resolvedOptions().locale; return sysLocaleCache; } } function parseLocaleString(localeStr) { const xIndex = localeStr.indexOf("-x-"); if (xIndex !== -1) { localeStr = localeStr.substring(0, xIndex); } const uIndex = localeStr.indexOf("-u-"); if (uIndex === -1) { return [localeStr]; } else { let options; let selectedStr; try { options = getCachedDTF(localeStr).resolvedOptions(); selectedStr = localeStr; } catch (e) { const smaller = localeStr.substring(0, uIndex); options = getCachedDTF(smaller).resolvedOptions(); selectedStr = smaller; } const { numberingSystem, calendar } = options; return [selectedStr, numberingSystem, calendar]; } } function intlConfigString(localeStr, numberingSystem, outputCalendar) { if (outputCalendar || numberingSystem) { if (!localeStr.includes("-u-")) { localeStr += "-u"; } if (outputCalendar) { localeStr += `-ca-${outputCalendar}`; } if (numberingSystem) { localeStr += `-nu-${numberingSystem}`; } return localeStr; } else { return localeStr; } } function mapMonths(f) { const ms = []; for (let i = 1; i <= 12; i++) { const dt = DateTime.utc(2009, i, 1); ms.push(f(dt)); } return ms; } function mapWeekdays(f) { const ms = []; for (let i = 1; i <= 7; i++) { const dt = DateTime.utc(2016, 11, 13 + i); ms.push(f(dt)); } return ms; } function listStuff(loc, length, englishFn, intlFn) { const mode = loc.listingMode(); if (mode === "error") { return null; } else if (mode === "en") { return englishFn(length); } else { return intlFn(length); } } function supportsFastNumbers(loc) { if (loc.numberingSystem && loc.numberingSystem !== "latn") { return false; } else { return loc.numberingSystem === "latn" || !loc.locale || loc.locale.startsWith("en") || new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn"; } } var PolyNumberFormatter = class { constructor(intl, forceSimple, opts) { this.padTo = opts.padTo || 0; this.floor = opts.floor || false; const { padTo, floor, ...otherOpts } = opts; if (!forceSimple || Object.keys(otherOpts).length > 0) { const intlOpts = { useGrouping: false, ...opts }; if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo; this.inf = getCachedINF(intl, intlOpts); } } format(i) { if (this.inf) { const fixed = this.floor ? Math.floor(i) : i; return this.inf.format(fixed); } else { const fixed = this.floor ? Math.floor(i) : roundTo(i, 3); return padStart(fixed, this.padTo); } } }; var PolyDateFormatter = class { constructor(dt, intl, opts) { this.opts = opts; this.originalZone = void 0; let z = void 0; if (this.opts.timeZone) { this.dt = dt; } else if (dt.zone.type === "fixed") { const gmtOffset = -1 * (dt.offset / 60); const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`; if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) { z = offsetZ; this.dt = dt; } else { z = "UTC"; this.dt = dt.offset === 0 ? dt : dt.setZone("UTC").plus({ minutes: dt.offset }); this.originalZone = dt.zone; } } else if (dt.zone.type === "system") { this.dt = dt; } else if (dt.zone.type === "iana") { this.dt = dt; z = dt.zone.name; } else { z = "UTC"; this.dt = dt.setZone("UTC").plus({ minutes: dt.offset }); this.originalZone = dt.zone; } const intlOpts = { ...this.opts }; intlOpts.timeZone = intlOpts.timeZone || z; this.dtf = getCachedDTF(intl, intlOpts); } format() { if (this.originalZone) { return this.formatToParts().map(({ value }) => value).join(""); } return this.dtf.format(this.dt.toJSDate()); } formatToParts() { const parts = this.dtf.formatToParts(this.dt.toJSDate()); if (this.originalZone) { return parts.map((part) => { if (part.type === "timeZoneName") { const offsetName = this.originalZone.offsetName(this.dt.ts, { locale: this.dt.locale, format: this.opts.timeZoneName }); return { ...part, value: offsetName }; } else { return part; } }); } return parts; } resolvedOptions() { return this.dtf.resolvedOptions(); } }; var PolyRelFormatter = class { constructor(intl, isEnglish, opts) { this.opts = { style: "long", ...opts }; if (!isEnglish && hasRelative()) { this.rtf = getCachedRTF(intl, opts); } } format(count, unit) { if (this.rtf) { return this.rtf.format(count, unit); } else { return formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== "long"); } } formatToParts(count, unit) { if (this.rtf) { return this.rtf.formatToParts(count, unit); } else { return []; } } }; var Locale = class { static fromOpts(opts) { return Locale.create(opts.locale, opts.numberingSystem, opts.outputCalendar, opts.defaultToEN); } static create(locale, numberingSystem, outputCalendar, defaultToEN = false) { const specifiedLocale = locale || Settings.defaultLocale; const localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale()); const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem; const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar; return new Locale(localeR, numberingSystemR, outputCalendarR, specifiedLocale); } static resetCache() { sysLocaleCache = null; intlDTCache = {}; intlNumCache = {}; intlRelCache = {}; } static fromObject({ locale, numberingSystem, outputCalendar } = {}) { return Locale.create(locale, numberingSystem, outputCalendar); } constructor(locale, numbering, outputCalendar, specifiedLocale) { const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale); this.locale = parsedLocale; this.numberingSystem = numbering || parsedNumberingSystem || null; this.outputCalendar = outputCalendar || parsedOutputCalendar || null; this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar); this.weekdaysCache = { format: {}, standalone: {} }; this.monthsCache = { format: {}, standalone: {} }; this.meridiemCache = null; this.eraCache = {}; this.specifiedLocale = specifiedLocale; this.fastNumbersCached = null; } get fastNumbers() { if (this.fastNumbersCached == null) { this.fastNumbersCached = supportsFastNumbers(this); } return this.fastNumbersCached; } listingMode() { const isActuallyEn = this.isEnglish(); const hasNoWeirdness = (this.numberingSystem === null || this.numberingSystem === "latn") && (this.outputCalendar === null || this.outputCalendar === "gregory"); return isActuallyEn && hasNoWeirdness ? "en" : "intl"; } clone(alts) { if (!alts || Object.getOwnPropertyNames(alts).length === 0) { return this; } else { return Locale.create( alts.locale || this.specifiedLocale, alts.numberingSystem || this.numberingSystem, alts.outputCalendar || this.outputCalendar, alts.defaultToEN || false ); } } redefaultToEN(alts = {}) { return this.clone({ ...alts, defaultToEN: true }); } redefaultToSystem(alts = {}) { return this.clone({ ...alts, defaultToEN: false }); } months(length, format = false) { return listStuff(this, length, months, () => { const intl = format ? { month: length, day: "numeric" } : { month: length }, formatStr = format ? "format" : "standalone"; if (!this.monthsCache[formatStr][length]) { this.monthsCache[formatStr][length] = mapMonths((dt) => this.extract(dt, intl, "month")); } return this.monthsCache[formatStr][length]; }); } weekdays(length, format = false) { return listStuff(this, length, weekdays, () => { const intl = format ? { weekday: length, year: "numeric", month: "long", day: "numeric" } : { weekday: length }, formatStr = format ? "format" : "standalone"; if (!this.weekdaysCache[formatStr][length]) { this.weekdaysCache[formatStr][length] = mapWeekdays( (dt) => this.extract(dt, intl, "weekday") ); } return this.weekdaysCache[formatStr][length]; }); } meridiems() { return listStuff( this, void 0, () => meridiems, () => { if (!this.meridiemCache) { const intl = { hour: "numeric", hourCycle: "h12" }; this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map( (dt) => this.extract(dt, intl, "dayperiod") ); } return this.meridiemCache; } ); } eras(length) { return listStuff(this, length, eras, () => { const intl = { era: length }; if (!this.eraCache[length]) { this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map( (dt) => this.extract(dt, intl, "era") ); } return this.eraCache[length]; }); } extract(dt, intlOpts, field) { const df = this.dtFormatter(dt, intlOpts), results = df.formatToParts(), matching = results.find((m) => m.type.toLowerCase() === field); return matching ? matching.value : null; } numberFormatter(opts = {}) { return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts); } dtFormatter(dt, intlOpts = {}) { return new PolyDateFormatter(dt, this.intl, intlOpts); } relFormatter(opts = {}) { return new PolyRelFormatter(this.intl, this.isEnglish(), opts); } listFormatter(opts = {}) { return getCachedLF(this.intl, opts); } isEnglish() { return this.locale === "en" || this.locale.toLowerCase() === "en-us" || new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us"); } equals(other) { return this.locale === other.locale && this.numberingSystem === other.numberingSystem && this.outputCalendar === other.outputCalendar; } }; var singleton = null; var FixedOffsetZone = class extends Zone { /** * Get a singleton instance of UTC * @return {FixedOffsetZone} */ static get utcInstance() { if (singleton === null) { singleton = new FixedOffsetZone(0); } return singleton; } /** * Get an instance with a specified offset * @param {number} offset - The offset in minutes * @return {FixedOffsetZone} */ static instance(offset2) { return offset2 === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset2); } /** * Get an instance of FixedOffsetZone from a UTC offset string, like "UTC+6" * @param {string} s - The offset string to parse * @example FixedOffsetZone.parseSpecifier("UTC+6") * @example FixedOffsetZone.parseSpecifier("UTC+06") * @example FixedOffsetZone.parseSpecifier("UTC-6:00") * @return {FixedOffsetZone} */ static parseSpecifier(s2) { if (s2) { const r = s2.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i); if (r) { return new FixedOffsetZone(signedOffset(r[1], r[2])); } } return null; } constructor(offset2) { super(); this.fixed = offset2; } /** @override **/ get type() { return "fixed"; } /** @override **/ get name() { return this.fixed === 0 ? "UTC" : `UTC${formatOffset(this.fixed, "narrow")}`; } get ianaName() { if (this.fixed === 0) { return "Etc/UTC"; } else { return `Etc/GMT${formatOffset(-this.fixed, "narrow")}`; } } /** @override **/ offsetName() { return this.name; } /** @override **/ formatOffset(ts, format) { return formatOffset(this.fixed, format); } /** @override **/ get isUniversal() { return true; } /** @override **/ offset() { return this.fixed; } /** @override **/ equals(otherZone) { return otherZone.type === "fixed" && otherZone.fixed === this.fixed; } /** @override **/ get isValid() { return true; } }; var InvalidZone = class extends Zone { constructor(zoneName) { super(); this.zoneName = zoneName; } /** @override **/ get type() { return "invalid"; } /** @override **/ get name() { return this.zoneName; } /** @override **/ get isUniversal() { return false; } /** @override **/ offsetName() { return null; } /** @override **/ formatOffset() { return ""; } /** @override **/ offset() { return NaN; } /** @override **/ equals() { return false; } /** @override **/ get isValid() { return false; } }; function normalizeZone(input, defaultZone2) { if (isUndefined(input) || input === null) { return defaultZone2; } else if (input instanceof Zone) { return input; } else if (isString2(input)) { const lowered = input.toLowerCase(); if (lowered === "default") return defaultZone2; else if (lowered === "local" || lowered === "system") return SystemZone.instance; else if (lowered === "utc" || lowered === "gmt") return FixedOffsetZone.utcInstance; else return FixedOffsetZone.parseSpecifier(lowered) || IANAZone.create(input); } else if (isNumber(input)) { return FixedOffsetZone.instance(input); } else if (typeof input === "object" && "offset" in input && typeof input.offset === "function") { return input; } else { return new InvalidZone(input); } } var now = () => Date.now(); var defaultZone = "system"; var defaultLocale = null; var defaultNumberingSystem = null; var defaultOutputCalendar = null; var twoDigitCutoffYear = 60; var throwOnInvalid; var Settings = class { /** * Get the callback for returning the current timestamp. * @type {function} */ static get now() { return now; } /** * Set the callback for returning the current timestamp. * The function should return a number, which will be interpreted as an Epoch millisecond count * @type {function} * @example Settings.now = () => Date.now() + 3000 // pretend it is 3 seconds in the future * @example Settings.now = () => 0 // always pretend it's Jan 1, 1970 at midnight in UTC time */ static set now(n2) { now = n2; } /** * Set the default time zone to create DateTimes in. Does not affect existing instances. * Use the value "system" to reset this value to the system's time zone. * @type {string} */ static set defaultZone(zone) { defaultZone = zone; } /** * Get the default time zone object currently used to create DateTimes. Does not affect existing instances. * The default value is the system's time zone (the one set on the machine that runs this code). * @type {Zone} */ static get defaultZone() { return normalizeZone(defaultZone, SystemZone.instance); } /** * Get the default locale to create DateTimes with. Does not affect existing instances. * @type {string} */ static get defaultLocale() { return defaultLocale; } /** * Set the default locale to create DateTimes with. Does not affect existing instances. * @type {string} */ static set defaultLocale(locale) { defaultLocale = locale; } /** * Get the default numbering system to create DateTimes with. Does not affect existing instances. * @type {string} */ static get defaultNumberingSystem() { return defaultNumberingSystem; } /** * Set the default numbering system to create DateTimes with. Does not affect existing instances. * @type {string} */ static set defaultNumberingSystem(numberingSystem) { defaultNumberingSystem = numberingSystem; } /** * Get the default output calendar to create DateTimes with. Does not affect existing instances. * @type {string} */ static get defaultOutputCalendar() { return defaultOutputCalendar; } /** * Set the default output calendar to create DateTimes with. Does not affect existing instances. * @type {string} */ static set defaultOutputCalendar(outputCalendar) { defaultOutputCalendar = outputCalendar; } /** * Get the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century. * @type {number} */ static get twoDigitCutoffYear() { return twoDigitCutoffYear; } /** * Set the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century. * @type {number} * @example Settings.twoDigitCutoffYear = 0 // cut-off year is 0, so all 'yy' are interpreted as current century * @example Settings.twoDigitCutoffYear = 50 // '49' -> 1949; '50' -> 2050 * @example Settings.twoDigitCutoffYear = 1950 // interpreted as 50 * @example Settings.twoDigitCutoffYear = 2050 // ALSO interpreted as 50 */ static set twoDigitCutoffYear(cutoffYear) { twoDigitCutoffYear = cutoffYear % 100; } /** * Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals * @type {boolean} */ static get throwOnInvalid() { return throwOnInvalid; } /** * Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals * @type {boolean} */ static set throwOnInvalid(t2) { throwOnInvalid = t2; } /** * Reset Luxon's global caches. Should only be necessary in testing scenarios. * @return {void} */ static resetCaches() { Locale.resetCache(); IANAZone.resetCache(); } }; function isUndefined(o) { return typeof o === "undefined"; } function isNumber(o) { return typeof o === "number"; } function isInteger(o) { return typeof o === "number" && o % 1 === 0; } function isString2(o) { return typeof o === "string"; } function isDate(o) { return Object.prototype.toString.call(o) === "[object Date]"; } function hasRelative() { try { return typeof Intl !== "undefined" && !!Intl.RelativeTimeFormat; } catch (e) { return false; } } function maybeArray(thing) { return Array.isArray(thing) ? thing : [thing]; } function bestBy(arr, by, compare) { if (arr.length === 0) { return void 0; } return arr.reduce((best, next) => { const pair = [by(next), next]; if (!best) { return pair; } else if (compare(best[0], pair[0]) === best[0]) { return best; } else { return pair; } }, null)[1]; } function pick(obj, keys) { return keys.reduce((a, k) => { a[k] = obj[k]; return a; }, {}); } function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } function integerBetween(thing, bottom, top) { return isInteger(thing) && thing >= bottom && thing <= top; } function floorMod(x, n2) { return x - n2 * Math.floor(x / n2); } function padStart(input, n2 = 2) { const isNeg = input < 0; let padded; if (isNeg) { padded = "-" + ("" + -input).padStart(n2, "0"); } else { padded = ("" + input).padStart(n2, "0"); } return padded; } function parseInteger(string) { if (isUndefined(string) || string === null || string === "") { return void 0; } else { return parseInt(string, 10); } } function parseFloating(string) { if (isUndefined(string) || string === null || string === "") { return void 0; } else { return parseFloat(string); } } function parseMillis(fraction) { if (isUndefined(fraction) || fraction === null || fraction === "") { return void 0; } else { const f = parseFloat("0." + fraction) * 1e3; return Math.floor(f); } } function roundTo(number, digits, towardZero = false) { const factor = 10 ** digits, rounder = towardZero ? Math.trunc : Math.round; return rounder(number * factor) / factor; } function isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); } function daysInYear(year) { return isLeapYear(year) ? 366 : 365; } function daysInMonth(year, month) { const modMonth = floorMod(month - 1, 12) + 1, modYear = year + (month - modMonth) / 12; if (modMonth === 2) { return isLeapYear(modYear) ? 29 : 28; } else { return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1]; } } function objToLocalTS(obj) { let d = Date.UTC( obj.year, obj.month - 1, obj.day, obj.hour, obj.minute, obj.second, obj.millisecond ); if (obj.year < 100 && obj.year >= 0) { d = new Date(d); d.setUTCFullYear(obj.year, obj.month - 1, obj.day); } return +d; } function weeksInWeekYear(weekYear) { const p1 = (weekYear + Math.floor(weekYear / 4) - Math.floor(weekYear / 100) + Math.floor(weekYear / 400)) % 7, last = weekYear - 1, p2 = (last + Math.floor(last / 4) - Math.floor(last / 100) + Math.floor(last / 400)) % 7; return p1 === 4 || p2 === 3 ? 53 : 52; } function untruncateYear(year) { if (year > 99) { return year; } else return year > Settings.twoDigitCutoffYear ? 1900 + year : 2e3 + year; } function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) { const date = new Date(ts), intlOpts = { hourCycle: "h23", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }; if (timeZone) { intlOpts.timeZone = timeZone; } const modified = { timeZoneName: offsetFormat, ...intlOpts }; const parsed = new Intl.DateTimeFormat(locale, modified).formatToParts(date).find((m) => m.type.toLowerCase() === "timezonename"); return parsed ? parsed.value : null; } function signedOffset(offHourStr, offMinuteStr) { let offHour = parseInt(offHourStr, 10); if (Number.isNaN(offHour)) { offHour = 0; } const offMin = parseInt(offMinuteStr, 10) || 0, offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin; return offHour * 60 + offMinSigned; } function asNumber(value) { const numericValue = Number(value); if (typeof value === "boolean" || value === "" || Number.isNaN(numericValue)) throw new InvalidArgumentError(`Invalid unit value ${value}`); return numericValue; } function normalizeObject(obj, normalizer) { const normalized = {}; for (const u in obj) { if (hasOwnProperty(obj, u)) { const v = obj[u]; if (v === void 0 || v === null) continue; normalized[normalizer(u)] = asNumber(v); } } return normalized; } function formatOffset(offset2, format) { const hours = Math.trunc(Math.abs(offset2 / 60)), minutes = Math.trunc(Math.abs(offset2 % 60)), sign = offset2 >= 0 ? "+" : "-"; switch (format) { case "short": return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`; case "narrow": return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ""}`; case "techie": return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`; default: throw new RangeError(`Value format ${format} is out of range for property format`); } } function timeObject(obj) { return pick(obj, ["hour", "minute", "second", "millisecond"]); } var monthsLong = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; var monthsShort = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; var monthsNarrow = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"]; function months(length) { switch (length) { case "narrow": return [...monthsNarrow]; case "short": return [...monthsShort]; case "long": return [...monthsLong]; case "numeric": return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]; case "2-digit": return ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]; default: return null; } } var weekdaysLong = [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ]; var weekdaysShort = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; var weekdaysNarrow = ["M", "T", "W", "T", "F", "S", "S"]; function weekdays(length) { switch (length) { case "narrow": return [...weekdaysNarrow]; case "short": return [...weekdaysShort]; case "long": return [...weekdaysLong]; case "numeric": return ["1", "2", "3", "4", "5", "6", "7"]; default: return null; } } var meridiems = ["AM", "PM"]; var erasLong = ["Before Christ", "Anno Domini"]; var erasShort = ["BC", "AD"]; var erasNarrow = ["B", "A"]; function eras(length) { switch (length) { case "narrow": return [...erasNarrow]; case "short": return [...erasShort]; case "long": return [...erasLong]; default: return null; } } function meridiemForDateTime(dt) { return meridiems[dt.hour < 12 ? 0 : 1]; } function weekdayForDateTime(dt, length) { return weekdays(length)[dt.weekday - 1]; } function monthForDateTime(dt, length) { return months(length)[dt.month - 1]; } function eraForDateTime(dt, length) { return eras(length)[dt.year < 0 ? 0 : 1]; } function formatRelativeTime(unit, count, numeric = "always", narrow = false) { const units = { years: ["year", "yr."], quarters: ["quarter", "qtr."], months: ["month", "mo."], weeks: ["week", "wk."], days: ["day", "day", "days"], hours: ["hour", "hr."], minutes: ["minute", "min."], seconds: ["second", "sec."] }; const lastable = ["hours", "minutes", "seconds"].indexOf(unit) === -1; if (numeric === "auto" && lastable) { const isDay = unit === "days"; switch (count) { case 1: return isDay ? "tomorrow" : `next ${units[unit][0]}`; case -1: return isDay ? "yesterday" : `last ${units[unit][0]}`; case 0: return isDay ? "today" : `this ${units[unit][0]}`; } } const isInPast = Object.is(count, -0) || count < 0, fmtValue = Math.abs(count), singular = fmtValue === 1, lilUnits = units[unit], fmtUnit = narrow ? singular ? lilUnits[1] : lilUnits[2] || lilUnits[1] : singular ? units[unit][0] : unit; return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`; } function stringifyTokens(splits, tokenToString) { let s2 = ""; for (const token of splits) { if (token.literal) { s2 += token.val; } else { s2 += tokenToString(token.val); } } return s2; } var macroTokenToFormatOpts = { D: DATE_SHORT, DD: DATE_MED, DDD: DATE_FULL, DDDD: DATE_HUGE, t: TIME_SIMPLE, tt: TIME_WITH_SECONDS, ttt: TIME_WITH_SHORT_OFFSET, tttt: TIME_WITH_LONG_OFFSET, T: TIME_24_SIMPLE, TT: TIME_24_WITH_SECONDS, TTT: TIME_24_WITH_SHORT_OFFSET, TTTT: TIME_24_WITH_LONG_OFFSET, f: DATETIME_SHORT, ff: DATETIME_MED, fff: DATETIME_FULL, ffff: DATETIME_HUGE, F: DATETIME_SHORT_WITH_SECONDS, FF: DATETIME_MED_WITH_SECONDS, FFF: DATETIME_FULL_WITH_SECONDS, FFFF: DATETIME_HUGE_WITH_SECONDS }; var Formatter2 = class { static create(locale, opts = {}) { return new Formatter2(locale, opts); } static parseFormat(fmt) { let current = null, currentFull = "", bracketed = false; const splits = []; for (let i = 0; i < fmt.length; i++) { const c = fmt.charAt(i); if (c === "'") { if (currentFull.length > 0) { splits.push({ literal: bracketed || /^\s+$/.test(currentFull), val: currentFull }); } current = null; currentFull = ""; bracketed = !bracketed; } else if (bracketed) { currentFull += c; } else if (c === current) { currentFull += c; } else { if (currentFull.length > 0) { splits.push({ literal: /^\s+$/.test(currentFull), val: currentFull }); } currentFull = c; current = c; } } if (currentFull.length > 0) { splits.push({ literal: bracketed || /^\s+$/.test(currentFull), val: currentFull }); } return splits; } static macroTokenToFormatOpts(token) { return macroTokenToFormatOpts[token]; } constructor(locale, formatOpts) { this.opts = formatOpts; this.loc = locale; this.systemLoc = null; } formatWithSystemDefault(dt, opts) { if (this.systemLoc === null) { this.systemLoc = this.loc.redefaultToSystem(); } const df = this.systemLoc.dtFormatter(dt, { ...this.opts, ...opts }); return df.format(); } dtFormatter(dt, opts = {}) { return this.loc.dtFormatter(dt, { ...this.opts, ...opts }); } formatDateTime(dt, opts) { return this.dtFormatter(dt, opts).format(); } formatDateTimeParts(dt, opts) { return this.dtFormatter(dt, opts).formatToParts(); } formatInterval(interval, opts) { const df = this.dtFormatter(interval.start, opts); return df.dtf.formatRange(interval.start.toJSDate(), interval.end.toJSDate()); } resolvedOptions(dt, opts) { return this.dtFormatter(dt, opts).resolvedOptions(); } num(n2, p = 0) { if (this.opts.forceSimple) { return padStart(n2, p); } const opts = { ...this.opts }; if (p > 0) { opts.padTo = p; } return this.loc.numberFormatter(opts).format(n2); } formatDateTimeFromString(dt, fmt) { const knownEnglish = this.loc.listingMode() === "en", useDateTimeFormatter = this.loc.outputCalendar && this.loc.outputCalendar !== "gregory", string = (opts, extract) => this.loc.extract(dt, opts, extract), formatOffset2 = (opts) => { if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) { return "Z"; } return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : ""; }, meridiem = () => knownEnglish ? meridiemForDateTime(dt) : string({ hour: "numeric", hourCycle: "h12" }, "dayperiod"), month = (length, standalone) => knownEnglish ? monthForDateTime(dt, length) : string(standalone ? { month: length } : { month: length, day: "numeric" }, "month"), weekday = (length, standalone) => knownEnglish ? weekdayForDateTime(dt, length) : string( standalone ? { weekday: length } : { weekday: length, month: "long", day: "numeric" }, "weekday" ), maybeMacro = (token) => { const formatOpts = Formatter2.macroTokenToFormatOpts(token); if (formatOpts) { return this.formatWithSystemDefault(dt, formatOpts); } else { return token; } }, era = (length) => knownEnglish ? eraForDateTime(dt, length) : string({ era: length }, "era"), tokenToString = (token) => { switch (token) { case "S": return this.num(dt.millisecond); case "u": case "SSS": return this.num(dt.millisecond, 3); case "s": return this.num(dt.second); case "ss": return this.num(dt.second, 2); case "uu": return this.num(Math.floor(dt.millisecond / 10), 2); case "uuu": return this.num(Math.floor(dt.millisecond / 100)); case "m": return this.num(dt.minute); case "mm": return this.num(dt.minute, 2); case "h": return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12); case "hh": return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2); case "H": return this.num(dt.hour); case "HH": return this.num(dt.hour, 2); case "Z": return formatOffset2({ format: "narrow", allowZ: this.opts.allowZ }); case "ZZ": return formatOffset2({ format: "short", allowZ: this.opts.allowZ }); case "ZZZ": return formatOffset2({ format: "techie", allowZ: this.opts.allowZ }); case "ZZZZ": return dt.zone.offsetName(dt.ts, { format: "short", locale: this.loc.locale }); case "ZZZZZ": return dt.zone.offsetName(dt.ts, { format: "long", locale: this.loc.locale }); case "z": return dt.zoneName; case "a": return meridiem(); case "d": return useDateTimeFormatter ? string({ day: "numeric" }, "day") : this.num(dt.day); case "dd": return useDateTimeFormatter ? string({ day: "2-digit" }, "day") : this.num(dt.day, 2); case "c": return this.num(dt.weekday); case "ccc": return weekday("short", true); case "cccc": return weekday("long", true); case "ccccc": return weekday("narrow", true); case "E": return this.num(dt.weekday); case "EEE": return weekday("short", false); case "EEEE": return weekday("long", false); case "EEEEE": return weekday("narrow", false); case "L": return useDateTimeFormatter ? string({ month: "numeric", day: "numeric" }, "month") : this.num(dt.month); case "LL": return useDateTimeFormatter ? string({ month: "2-digit", day: "numeric" }, "month") : this.num(dt.month, 2); case "LLL": return month("short", true); case "LLLL": return month("long", true); case "LLLLL": return month("narrow", true); case "M": return useDateTimeFormatter ? string({ month: "numeric" }, "month") : this.num(dt.month); case "MM": return useDateTimeFormatter ? string({ month: "2-digit" }, "month") : this.num(dt.month, 2); case "MMM": return month("short", false); case "MMMM": return month("long", false); case "MMMMM": return month("narrow", false); case "y": return useDateTimeFormatter ? string({ year: "numeric" }, "year") : this.num(dt.year); case "yy": return useDateTimeFormatter ? string({ year: "2-digit" }, "year") : this.num(dt.year.toString().slice(-2), 2); case "yyyy": return useDateTimeFormatter ? string({ year: "numeric" }, "year") : this.num(dt.year, 4); case "yyyyyy": return useDateTimeFormatter ? string({ year: "numeric" }, "year") : this.num(dt.year, 6); case "G": return era("short"); case "GG": return era("long"); case "GGGGG": return era("narrow"); case "kk": return this.num(dt.weekYear.toString().slice(-2), 2); case "kkkk": return this.num(dt.weekYear, 4); case "W": return this.num(dt.weekNumber); case "WW": return this.num(dt.weekNumber, 2); case "o": return this.num(dt.ordinal); case "ooo": return this.num(dt.ordinal, 3); case "q": return this.num(dt.quarter); case "qq": return this.num(dt.quarter, 2); case "X": return this.num(Math.floor(dt.ts / 1e3)); case "x": return this.num(dt.ts); default: return maybeMacro(token); } }; return stringifyTokens(Formatter2.parseFormat(fmt), tokenToString); } formatDurationFromString(dur, fmt) { const tokenToField = (token) => { switch (token[0]) { case "S": return "millisecond"; case "s": return "second"; case "m": return "minute"; case "h": return "hour"; case "d": return "day"; case "w": return "week"; case "M": return "month"; case "y": return "year"; default: return null; } }, tokenToString = (lildur) => (token) => { const mapped = tokenToField(token); if (mapped) { return this.num(lildur.get(mapped), token.length); } else { return token; } }, tokens = Formatter2.parseFormat(fmt), realTokens = tokens.reduce( (found, { literal, val }) => literal ? found : found.concat(val), [] ), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t2) => t2)); return stringifyTokens(tokens, tokenToString(collapsed)); } }; var Invalid = class { constructor(reason, explanation) { this.reason = reason; this.explanation = explanation; } toMessage() { if (this.explanation) { return `${this.reason}: ${this.explanation}`; } else { return this.reason; } } }; var ianaRegex = /[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/; function combineRegexes(...regexes) { const full = regexes.reduce((f, r) => f + r.source, ""); return RegExp(`^${full}$`); } function combineExtractors(...extractors) { return (m) => extractors.reduce( ([mergedVals, mergedZone, cursor], ex) => { const [val, zone, next] = ex(m, cursor); return [{ ...mergedVals, ...val }, zone || mergedZone, next]; }, [{}, null, 1] ).slice(0, 2); } function parse(s2, ...patterns) { if (s2 == null) { return [null, null]; } for (const [regex, extractor] of patterns) { const m = regex.exec(s2); if (m) { return extractor(m); } } return [null, null]; } function simpleParse(...keys) { return (match2, cursor) => { const ret = {}; let i; for (i = 0; i < keys.length; i++) { ret[keys[i]] = parseInteger(match2[cursor + i]); } return [ret, null, cursor + i]; }; } var offsetRegex = /(?:(Z)|([+-]\d\d)(?::?(\d\d))?)/; var isoExtendedZone = `(?:${offsetRegex.source}?(?:\\[(${ianaRegex.source})\\])?)?`; var isoTimeBaseRegex = /(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/; var isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${isoExtendedZone}`); var isoTimeExtensionRegex = RegExp(`(?:T${isoTimeRegex.source})?`); var isoYmdRegex = /([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/; var isoWeekRegex = /(\d{4})-?W(\d\d)(?:-?(\d))?/; var isoOrdinalRegex = /(\d{4})-?(\d{3})/; var extractISOWeekData = simpleParse("weekYear", "weekNumber", "weekDay"); var extractISOOrdinalData = simpleParse("year", "ordinal"); var sqlYmdRegex = /(\d{4})-(\d\d)-(\d\d)/; var sqlTimeRegex = RegExp( `${isoTimeBaseRegex.source} ?(?:${offsetRegex.source}|(${ianaRegex.source}))?` ); var sqlTimeExtensionRegex = RegExp(`(?: ${sqlTimeRegex.source})?`); function int(match2, pos, fallback) { const m = match2[pos]; return isUndefined(m) ? fallback : parseInteger(m); } function extractISOYmd(match2, cursor) { const item = { year: int(match2, cursor), month: int(match2, cursor + 1, 1), day: int(match2, cursor + 2, 1) }; return [item, null, cursor + 3]; } function extractISOTime(match2, cursor) { const item = { hours: int(match2, cursor, 0), minutes: int(match2, cursor + 1, 0), seconds: int(match2, cursor + 2, 0), milliseconds: parseMillis(match2[cursor + 3]) }; return [item, null, cursor + 4]; } function extractISOOffset(match2, cursor) { const local = !match2[cursor] && !match2[cursor + 1], fullOffset = signedOffset(match2[cursor + 1], match2[cursor + 2]), zone = local ? null : FixedOffsetZone.instance(fullOffset); return [{}, zone, cursor + 3]; } function extractIANAZone(match2, cursor) { const zone = match2[cursor] ? IANAZone.create(match2[cursor]) : null; return [{}, zone, cursor + 1]; } var isoTimeOnly = RegExp(`^T?${isoTimeBaseRegex.source}$`); var isoDuration = /^-?P(?:(?:(-?\d{1,20}(?:\.\d{1,20})?)Y)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20}(?:\.\d{1,20})?)W)?(?:(-?\d{1,20}(?:\.\d{1,20})?)D)?(?:T(?:(-?\d{1,20}(?:\.\d{1,20})?)H)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,20}))?S)?)?)$/; function extractISODuration(match2) { const [s2, yearStr, monthStr, weekStr, dayStr, hourStr, minuteStr, secondStr, millisecondsStr] = match2; const hasNegativePrefix = s2[0] === "-"; const negativeSeconds = secondStr && secondStr[0] === "-"; const maybeNegate = (num, force = false) => num !== void 0 && (force || num && hasNegativePrefix) ? -num : num; return [ { years: maybeNegate(parseFloating(yearStr)), months: maybeNegate(parseFloating(monthStr)), weeks: maybeNegate(parseFloating(weekStr)), days: maybeNegate(parseFloating(dayStr)), hours: maybeNegate(parseFloating(hourStr)), minutes: maybeNegate(parseFloating(minuteStr)), seconds: maybeNegate(parseFloating(secondStr), secondStr === "-0"), milliseconds: maybeNegate(parseMillis(millisecondsStr), negativeSeconds) } ]; } var obsOffsets = { 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 }; function fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { const result = { year: yearStr.length === 2 ? untruncateYear(parseInteger(yearStr)) : parseInteger(yearStr), month: monthsShort.indexOf(monthStr) + 1, day: parseInteger(dayStr), hour: parseInteger(hourStr), minute: parseInteger(minuteStr) }; if (secondStr) result.second = parseInteger(secondStr); if (weekdayStr) { result.weekday = weekdayStr.length > 3 ? weekdaysLong.indexOf(weekdayStr) + 1 : weekdaysShort.indexOf(weekdayStr) + 1; } return result; } var 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\d)(\d\d)))$/; function extractRFC2822(match2) { const [ , weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr, obsOffset, milOffset, offHourStr, offMinuteStr ] = match2, result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); let offset2; if (obsOffset) { offset2 = obsOffsets[obsOffset]; } else if (milOffset) { offset2 = 0; } else { offset2 = signedOffset(offHourStr, offMinuteStr); } return [result, new FixedOffsetZone(offset2)]; } function preprocessRFC2822(s2) { return s2.replace(/\([^()]*\)|[\n\t]/g, " ").replace(/(\s\s+)/g, " ").trim(); } var rfc1123 = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/; var rfc850 = /^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/; var ascii = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/; function extractRFC1123Or850(match2) { const [, weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr] = match2, result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); return [result, FixedOffsetZone.utcInstance]; } function extractASCII(match2) { const [, weekdayStr, monthStr, dayStr, hourStr, minuteStr, secondStr, yearStr] = match2, result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr); return [result, FixedOffsetZone.utcInstance]; } var isoYmdWithTimeExtensionRegex = combineRegexes(isoYmdRegex, isoTimeExtensionRegex); var isoWeekWithTimeExtensionRegex = combineRegexes(isoWeekRegex, isoTimeExtensionRegex); var isoOrdinalWithTimeExtensionRegex = combineRegexes(isoOrdinalRegex, isoTimeExtensionRegex); var isoTimeCombinedRegex = combineRegexes(isoTimeRegex); var extractISOYmdTimeAndOffset = combineExtractors( extractISOYmd, extractISOTime, extractISOOffset, extractIANAZone ); var extractISOWeekTimeAndOffset = combineExtractors( extractISOWeekData, extractISOTime, extractISOOffset, extractIANAZone ); var extractISOOrdinalDateAndTime = combineExtractors( extractISOOrdinalData, extractISOTime, extractISOOffset, extractIANAZone ); var extractISOTimeAndOffset = combineExtractors( extractISOTime, extractISOOffset, extractIANAZone ); function parseISODate(s2) { return parse( s2, [isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset], [isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset], [isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDateAndTime], [isoTimeCombinedRegex, extractISOTimeAndOffset] ); } function parseRFC2822Date(s2) { return parse(preprocessRFC2822(s2), [rfc2822, extractRFC2822]); } function parseHTTPDate(s2) { return parse( s2, [rfc1123, extractRFC1123Or850], [rfc850, extractRFC1123Or850], [ascii, extractASCII] ); } function parseISODuration(s2) { return parse(s2, [isoDuration, extractISODuration]); } var extractISOTimeOnly = combineExtractors(extractISOTime); function parseISOTimeOnly(s2) { return parse(s2, [isoTimeOnly, extractISOTimeOnly]); } var sqlYmdWithTimeExtensionRegex = combineRegexes(sqlYmdRegex, sqlTimeExtensionRegex); var sqlTimeCombinedRegex = combineRegexes(sqlTimeRegex); var extractISOTimeOffsetAndIANAZone = combineExtractors( extractISOTime, extractISOOffset, extractIANAZone ); function parseSQL(s2) { return parse( s2, [sqlYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset], [sqlTimeCombinedRegex, extractISOTimeOffsetAndIANAZone] ); } var INVALID$2 = "Invalid Duration"; var lowOrderMatrix = { weeks: { days: 7, hours: 7 * 24, minutes: 7 * 24 * 60, seconds: 7 * 24 * 60 * 60, milliseconds: 7 * 24 * 60 * 60 * 1e3 }, days: { hours: 24, minutes: 24 * 60, seconds: 24 * 60 * 60, milliseconds: 24 * 60 * 60 * 1e3 }, hours: { minutes: 60, seconds: 60 * 60, milliseconds: 60 * 60 * 1e3 }, minutes: { seconds: 60, milliseconds: 60 * 1e3 }, seconds: { milliseconds: 1e3 } }; var casualMatrix = { years: { quarters: 4, months: 12, weeks: 52, days: 365, hours: 365 * 24, minutes: 365 * 24 * 60, seconds: 365 * 24 * 60 * 60, milliseconds: 365 * 24 * 60 * 60 * 1e3 }, quarters: { months: 3, weeks: 13, days: 91, hours: 91 * 24, minutes: 91 * 24 * 60, seconds: 91 * 24 * 60 * 60, milliseconds: 91 * 24 * 60 * 60 * 1e3 }, months: { weeks: 4, days: 30, hours: 30 * 24, minutes: 30 * 24 * 60, seconds: 30 * 24 * 60 * 60, milliseconds: 30 * 24 * 60 * 60 * 1e3 }, ...lowOrderMatrix }; var daysInYearAccurate = 146097 / 400; var daysInMonthAccurate = 146097 / 4800; var accurateMatrix = { years: { quarters: 4, months: 12, weeks: daysInYearAccurate / 7, days: daysInYearAccurate, hours: daysInYearAccurate * 24, minutes: daysInYearAccurate * 24 * 60, seconds: daysInYearAccurate * 24 * 60 * 60, milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1e3 }, quarters: { months: 3, weeks: daysInYearAccurate / 28, days: daysInYearAccurate / 4, hours: daysInYearAccurate * 24 / 4, minutes: daysInYearAccurate * 24 * 60 / 4, seconds: daysInYearAccurate * 24 * 60 * 60 / 4, milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1e3 / 4 }, months: { weeks: daysInMonthAccurate / 7, days: daysInMonthAccurate, hours: daysInMonthAccurate * 24, minutes: daysInMonthAccurate * 24 * 60, seconds: daysInMonthAccurate * 24 * 60 * 60, milliseconds: daysInMonthAccurate * 24 * 60 * 60 * 1e3 }, ...lowOrderMatrix }; var orderedUnits$1 = [ "years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds" ]; var reverseUnits = orderedUnits$1.slice(0).reverse(); function clone$1(dur, alts, clear = false) { const conf = { values: clear ? alts.values : { ...dur.values, ...alts.values || {} }, loc: dur.loc.clone(alts.loc), conversionAccuracy: alts.conversionAccuracy || dur.conversionAccuracy, matrix: alts.matrix || dur.matrix }; return new Duration(conf); } function durationToMillis(matrix, vals) { var _a; let sum = (_a = vals.milliseconds) != null ? _a : 0; for (const unit of reverseUnits.slice(1)) { if (vals[unit]) { sum += vals[unit] * matrix[unit]["milliseconds"]; } } return sum; } function normalizeValues(matrix, vals) { const factor = durationToMillis(matrix, vals) < 0 ? -1 : 1; orderedUnits$1.reduceRight((previous, current) => { if (!isUndefined(vals[current])) { if (previous) { const previousVal = vals[previous] * factor; const conv = matrix[current][previous]; const rollUp = Math.floor(previousVal / conv); vals[current] += rollUp * factor; vals[previous] -= rollUp * conv * factor; } return current; } else { return previous; } }, null); orderedUnits$1.reduce((previous, current) => { if (!isUndefined(vals[current])) { if (previous) { const fraction = vals[previous] % 1; vals[previous] -= fraction; vals[current] += fraction * matrix[previous][current]; } return current; } else { return previous; } }, null); } function removeZeroes(vals) { const newVals = {}; for (const [key, value] of Object.entries(vals)) { if (value !== 0) { newVals[key] = value; } } return newVals; } var Duration = class { /** * @private */ constructor(config) { const accurate = config.conversionAccuracy === "longterm" || false; let matrix = accurate ? accurateMatrix : casualMatrix; if (config.matrix) { matrix = config.matrix; } this.values = config.values; this.loc = config.loc || Locale.create(); this.conversionAccuracy = accurate ? "longterm" : "casual"; this.invalid = config.invalid || null; this.matrix = matrix; this.isLuxonDuration = true; } /** * Create Duration from a number of milliseconds. * @param {number} count of milliseconds * @param {Object} opts - options for parsing * @param {string} [opts.locale='en-US'] - the locale to use * @param {string} opts.numberingSystem - the numbering system to use * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use * @return {Duration} */ static fromMillis(count, opts) { return Duration.fromObject({ milliseconds: count }, opts); } /** * Create a Duration from a JavaScript object with keys like 'years' and 'hours'. * If this object is empty then a zero milliseconds duration is returned. * @param {Object} obj - the object to create the DateTime from * @param {number} obj.years * @param {number} obj.quarters * @param {number} obj.months * @param {number} obj.weeks * @param {number} obj.days * @param {number} obj.hours * @param {number} obj.minutes * @param {number} obj.seconds * @param {number} obj.milliseconds * @param {Object} [opts=[]] - options for creating this Duration * @param {string} [opts.locale='en-US'] - the locale to use * @param {string} opts.numberingSystem - the numbering system to use * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use * @param {string} [opts.matrix=Object] - the custom conversion system to use * @return {Duration} */ static fromObject(obj, opts = {}) { if (obj == null || typeof obj !== "object") { throw new InvalidArgumentError( `Duration.fromObject: argument expected to be an object, got ${obj === null ? "null" : typeof obj}` ); } return new Duration({ values: normalizeObject(obj, Duration.normalizeUnit), loc: Locale.fromObject(opts), conversionAccuracy: opts.conversionAccuracy, matrix: opts.matrix }); } /** * Create a Duration from DurationLike. * * @param {Object | number | Duration} durationLike * One of: * - object with keys like 'years' and 'hours'. * - number representing milliseconds * - Duration instance * @return {Duration} */ static fromDurationLike(durationLike) { if (isNumber(durationLike)) { return Duration.fromMillis(durationLike); } else if (Duration.isDuration(durationLike)) { return durationLike; } else if (typeof durationLike === "object") { return Duration.fromObject(durationLike); } else { throw new InvalidArgumentError( `Unknown duration argument ${durationLike} of type ${typeof durationLike}` ); } } /** * Create a Duration from an ISO 8601 duration string. * @param {string} text - text to parse * @param {Object} opts - options for parsing * @param {string} [opts.locale='en-US'] - the locale to use * @param {string} opts.numberingSystem - the numbering system to use * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use * @param {string} [opts.matrix=Object] - the preset conversion system to use * @see https://en.wikipedia.org/wiki/ISO_8601#Durations * @example Duration.fromISO('P3Y6M1W4DT12H30M5S').toObject() //=> { years: 3, months: 6, weeks: 1, days: 4, hours: 12, minutes: 30, seconds: 5 } * @example Duration.fromISO('PT23H').toObject() //=> { hours: 23 } * @example Duration.fromISO('P5Y3M').toObject() //=> { years: 5, months: 3 } * @return {Duration} */ static fromISO(text, opts) { const [parsed] = parseISODuration(text); if (parsed) { return Duration.fromObject(parsed, opts); } else { return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`); } } /** * Create a Duration from an ISO 8601 time string. * @param {string} text - text to parse * @param {Object} opts - options for parsing * @param {string} [opts.locale='en-US'] - the locale to use * @param {string} opts.numberingSystem - the numbering system to use * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use * @param {string} [opts.matrix=Object] - the conversion system to use * @see https://en.wikipedia.org/wiki/ISO_8601#Times * @example Duration.fromISOTime('11:22:33.444').toObject() //=> { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 } * @example Duration.fromISOTime('11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } * @example Duration.fromISOTime('T11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } * @example Duration.fromISOTime('1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } * @example Duration.fromISOTime('T1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 } * @return {Duration} */ static fromISOTime(text, opts) { const [parsed] = parseISOTimeOnly(text); if (parsed) { return Duration.fromObject(parsed, opts); } else { return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`); } } /** * Create an invalid Duration. * @param {string} reason - simple string of why this datetime is invalid. Should not contain parameters or anything else data-dependent * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information * @return {Duration} */ static invalid(reason, explanation = null) { if (!reason) { throw new InvalidArgumentError("need to specify a reason the Duration is invalid"); } const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation); if (Settings.throwOnInvalid) { throw new InvalidDurationError(invalid); } else { return new Duration({ invalid }); } } /** * @private */ static normalizeUnit(unit) { const normalized = { year: "years", years: "years", quarter: "quarters", quarters: "quarters", month: "months", months: "months", week: "weeks", weeks: "weeks", day: "days", days: "days", hour: "hours", hours: "hours", minute: "minutes", minutes: "minutes", second: "seconds", seconds: "seconds", millisecond: "milliseconds", milliseconds: "milliseconds" }[unit ? unit.toLowerCase() : unit]; if (!normalized) throw new InvalidUnitError(unit); return normalized; } /** * Check if an object is a Duration. Works across context boundaries * @param {object} o * @return {boolean} */ static isDuration(o) { return o && o.isLuxonDuration || false; } /** * Get the locale of a Duration, such 'en-GB' * @type {string} */ get locale() { return this.isValid ? this.loc.locale : null; } /** * Get the numbering system of a Duration, such 'beng'. The numbering system is used when formatting the Duration * * @type {string} */ get numberingSystem() { return this.isValid ? this.loc.numberingSystem : null; } /** * Returns a string representation of this Duration formatted according to the specified format string. You may use these tokens: * * `S` for milliseconds * * `s` for seconds * * `m` for minutes * * `h` for hours * * `d` for days * * `w` for weeks * * `M` for months * * `y` for years * Notes: * * Add padding by repeating the token, e.g. "yy" pads the years to two digits, "hhhh" pads the hours out to four digits * * Tokens can be escaped by wrapping with single quotes. * * The duration will be converted to the set of units in the format string using {@link Duration#shiftTo} and the Durations's conversion accuracy setting. * @param {string} fmt - the format string * @param {Object} opts - options * @param {boolean} [opts.floor=true] - floor numerical values * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("y d s") //=> "1 6 2" * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("yy dd sss") //=> "01 06 002" * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("M S") //=> "12 518402000" * @return {string} */ toFormat(fmt, opts = {}) { const fmtOpts = { ...opts, floor: opts.round !== false && opts.floor !== false }; return this.isValid ? Formatter2.create(this.loc, fmtOpts).formatDurationFromString(this, fmt) : INVALID$2; } /** * Returns a string representation of a Duration with all units included. * To modify its behavior use the `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat * @param opts - On option object to override the formatting. Accepts the same keys as the options parameter of the native `Int.NumberFormat` constructor, as well as `listStyle`. * @example * ```js * var dur = Duration.fromObject({ days: 1, hours: 5, minutes: 6 }) * dur.toHuman() //=> '1 day, 5 hours, 6 minutes' * dur.toHuman({ listStyle: "long" }) //=> '1 day, 5 hours, and 6 minutes' * dur.toHuman({ unitDisplay: "short" }) //=> '1 day, 5 hr, 6 min' * ``` */ toHuman(opts = {}) { if (!this.isValid) return INVALID$2; const l2 = orderedUnits$1.map((unit) => { const val = this.values[unit]; if (isUndefined(val)) { return null; } return this.loc.numberFormatter({ style: "unit", unitDisplay: "long", ...opts, unit: unit.slice(0, -1) }).format(val); }).filter((n2) => n2); return this.loc.listFormatter({ type: "conjunction", style: opts.listStyle || "narrow", ...opts }).format(l2); } /** * Returns a JavaScript object with this Duration's values. * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toObject() //=> { years: 1, days: 6, seconds: 2 } * @return {Object} */ toObject() { if (!this.isValid) return {}; return { ...this.values }; } /** * Returns an ISO 8601-compliant string representation of this Duration. * @see https://en.wikipedia.org/wiki/ISO_8601#Durations * @example Duration.fromObject({ years: 3, seconds: 45 }).toISO() //=> 'P3YT45S' * @example Duration.fromObject({ months: 4, seconds: 45 }).toISO() //=> 'P4MT45S' * @example Duration.fromObject({ months: 5 }).toISO() //=> 'P5M' * @example Duration.fromObject({ minutes: 5 }).toISO() //=> 'PT5M' * @example Duration.fromObject({ milliseconds: 6 }).toISO() //=> 'PT0.006S' * @return {string} */ toISO() { if (!this.isValid) return null; let s2 = "P"; if (this.years !== 0) s2 += this.years + "Y"; if (this.months !== 0 || this.quarters !== 0) s2 += this.months + this.quarters * 3 + "M"; if (this.weeks !== 0) s2 += this.weeks + "W"; if (this.days !== 0) s2 += this.days + "D"; if (this.hours !== 0 || this.minutes !== 0 || this.seconds !== 0 || this.milliseconds !== 0) s2 += "T"; if (this.hours !== 0) s2 += this.hours + "H"; if (this.minutes !== 0) s2 += this.minutes + "M"; if (this.seconds !== 0 || this.milliseconds !== 0) s2 += roundTo(this.seconds + this.milliseconds / 1e3, 3) + "S"; if (s2 === "P") s2 += "T0S"; return s2; } /** * Returns an ISO 8601-compliant string representation of this Duration, formatted as a time of day. * Note that this will return null if the duration is invalid, negative, or equal to or greater than 24 hours. * @see https://en.wikipedia.org/wiki/ISO_8601#Times * @param {Object} opts - options * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0 * @param {boolean} [opts.includePrefix=false] - include the `T` prefix * @param {string} [opts.format='extended'] - choose between the basic and extended format * @example Duration.fromObject({ hours: 11 }).toISOTime() //=> '11:00:00.000' * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressMilliseconds: true }) //=> '11:00:00' * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressSeconds: true }) //=> '11:00' * @example Duration.fromObject({ hours: 11 }).toISOTime({ includePrefix: true }) //=> 'T11:00:00.000' * @example Duration.fromObject({ hours: 11 }).toISOTime({ format: 'basic' }) //=> '110000.000' * @return {string} */ toISOTime(opts = {}) { if (!this.isValid) return null; const millis = this.toMillis(); if (millis < 0 || millis >= 864e5) return null; opts = { suppressMilliseconds: false, suppressSeconds: false, includePrefix: false, format: "extended", ...opts, includeOffset: false }; const dateTime = DateTime.fromMillis(millis, { zone: "UTC" }); return dateTime.toISOTime(opts); } /** * Returns an ISO 8601 representation of this Duration appropriate for use in JSON. * @return {string} */ toJSON() { return this.toISO(); } /** * Returns an ISO 8601 representation of this Duration appropriate for use in debugging. * @return {string} */ toString() { return this.toISO(); } /** * Returns an milliseconds value of this Duration. * @return {number} */ toMillis() { if (!this.isValid) return NaN; return durationToMillis(this.matrix, this.values); } /** * Returns an milliseconds value of this Duration. Alias of {@link toMillis} * @return {number} */ valueOf() { return this.toMillis(); } /** * Make this Duration longer by the specified amount. Return a newly-constructed Duration. * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() * @return {Duration} */ plus(duration) { if (!this.isValid) return this; const dur = Duration.fromDurationLike(duration), result = {}; for (const k of orderedUnits$1) { if (hasOwnProperty(dur.values, k) || hasOwnProperty(this.values, k)) { result[k] = dur.get(k) + this.get(k); } } return clone$1(this, { values: result }, true); } /** * Make this Duration shorter by the specified amount. Return a newly-constructed Duration. * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() * @return {Duration} */ minus(duration) { if (!this.isValid) return this; const dur = Duration.fromDurationLike(duration); return this.plus(dur.negate()); } /** * Scale this Duration by the specified amount. Return a newly-constructed Duration. * @param {function} fn - The function to apply to each unit. Arity is 1 or 2: the value of the unit and, optionally, the unit name. Must return a number. * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits(x => x * 2) //=> { hours: 2, minutes: 60 } * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits((x, u) => u === "hours" ? x * 2 : x) //=> { hours: 2, minutes: 30 } * @return {Duration} */ mapUnits(fn) { if (!this.isValid) return this; const result = {}; for (const k of Object.keys(this.values)) { result[k] = asNumber(fn(this.values[k], k)); } return clone$1(this, { values: result }, true); } /** * Get the value of unit. * @param {string} unit - a unit such as 'minute' or 'day' * @example Duration.fromObject({years: 2, days: 3}).get('years') //=> 2 * @example Duration.fromObject({years: 2, days: 3}).get('months') //=> 0 * @example Duration.fromObject({years: 2, days: 3}).get('days') //=> 3 * @return {number} */ get(unit) { return this[Duration.normalizeUnit(unit)]; } /** * "Set" the values of specified units. Return a newly-constructed Duration. * @param {Object} values - a mapping of units to numbers * @example dur.set({ years: 2017 }) * @example dur.set({ hours: 8, minutes: 30 }) * @return {Duration} */ set(values) { if (!this.isValid) return this; const mixed = { ...this.values, ...normalizeObject(values, Duration.normalizeUnit) }; return clone$1(this, { values: mixed }); } /** * "Set" the locale and/or numberingSystem. Returns a newly-constructed Duration. * @example dur.reconfigure({ locale: 'en-GB' }) * @return {Duration} */ reconfigure({ locale, numberingSystem, conversionAccuracy, matrix } = {}) { const loc = this.loc.clone({ locale, numberingSystem }); const opts = { loc, matrix, conversionAccuracy }; return clone$1(this, opts); } /** * Return the length of the duration in the specified unit. * @param {string} unit - a unit such as 'minutes' or 'days' * @example Duration.fromObject({years: 1}).as('days') //=> 365 * @example Duration.fromObject({years: 1}).as('months') //=> 12 * @example Duration.fromObject({hours: 60}).as('days') //=> 2.5 * @return {number} */ as(unit) { return this.isValid ? this.shiftTo(unit).get(unit) : NaN; } /** * Reduce this Duration to its canonical representation in its current units. * Assuming the overall value of the Duration is positive, this means: * - excessive values for lower-order units are converted to higher-order units (if possible, see first and second example) * - negative lower-order units are converted to higher order units (there must be such a higher order unit, otherwise * the overall value would be negative, see second example) * - fractional values for higher-order units are converted to lower-order units (if possible, see fourth example) * * If the overall value is negative, the result of this method is equivalent to `this.negate().normalize().negate()`. * @example Duration.fromObject({ years: 2, days: 5000 }).normalize().toObject() //=> { years: 15, days: 255 } * @example Duration.fromObject({ days: 5000 }).normalize().toObject() //=> { days: 5000 } * @example Duration.fromObject({ hours: 12, minutes: -45 }).normalize().toObject() //=> { hours: 11, minutes: 15 } * @example Duration.fromObject({ years: 2.5, days: 0, hours: 0 }).normalize().toObject() //=> { years: 2, days: 182, hours: 12 } * @return {Duration} */ normalize() { if (!this.isValid) return this; const vals = this.toObject(); normalizeValues(this.matrix, vals); return clone$1(this, { values: vals }, true); } /** * Rescale units to its largest representation * @example Duration.fromObject({ milliseconds: 90000 }).rescale().toObject() //=> { minutes: 1, seconds: 30 } * @return {Duration} */ rescale() { if (!this.isValid) return this; const vals = removeZeroes(this.normalize().shiftToAll().toObject()); return clone$1(this, { values: vals }, true); } /** * Convert this Duration into its representation in a different set of units. * @example Duration.fromObject({ hours: 1, seconds: 30 }).shiftTo('minutes', 'milliseconds').toObject() //=> { minutes: 60, milliseconds: 30000 } * @return {Duration} */ shiftTo(...units) { if (!this.isValid) return this; if (units.length === 0) { return this; } units = units.map((u) => Duration.normalizeUnit(u)); const built = {}, accumulated = {}, vals = this.toObject(); let lastUnit; for (const k of orderedUnits$1) { if (units.indexOf(k) >= 0) { lastUnit = k; let own = 0; for (const ak in accumulated) { own += this.matrix[ak][k] * accumulated[ak]; accumulated[ak] = 0; } if (isNumber(vals[k])) { own += vals[k]; } const i = Math.trunc(own); built[k] = i; accumulated[k] = (own * 1e3 - i * 1e3) / 1e3; } else if (isNumber(vals[k])) { accumulated[k] = vals[k]; } } for (const key in accumulated) { if (accumulated[key] !== 0) { built[lastUnit] += key === lastUnit ? accumulated[key] : accumulated[key] / this.matrix[lastUnit][key]; } } normalizeValues(this.matrix, built); return clone$1(this, { values: built }, true); } /** * Shift this Duration to all available units. * Same as shiftTo("years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds") * @return {Duration} */ shiftToAll() { if (!this.isValid) return this; return this.shiftTo( "years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds" ); } /** * Return the negative of this Duration. * @example Duration.fromObject({ hours: 1, seconds: 30 }).negate().toObject() //=> { hours: -1, seconds: -30 } * @return {Duration} */ negate() { if (!this.isValid) return this; const negated = {}; for (const k of Object.keys(this.values)) { negated[k] = this.values[k] === 0 ? 0 : -this.values[k]; } return clone$1(this, { values: negated }, true); } /** * Get the years. * @type {number} */ get years() { return this.isValid ? this.values.years || 0 : NaN; } /** * Get the quarters. * @type {number} */ get quarters() { return this.isValid ? this.values.quarters || 0 : NaN; } /** * Get the months. * @type {number} */ get months() { return this.isValid ? this.values.months || 0 : NaN; } /** * Get the weeks * @type {number} */ get weeks() { return this.isValid ? this.values.weeks || 0 : NaN; } /** * Get the days. * @type {number} */ get days() { return this.isValid ? this.values.days || 0 : NaN; } /** * Get the hours. * @type {number} */ get hours() { return this.isValid ? this.values.hours || 0 : NaN; } /** * Get the minutes. * @type {number} */ get minutes() { return this.isValid ? this.values.minutes || 0 : NaN; } /** * Get the seconds. * @return {number} */ get seconds() { return this.isValid ? this.values.seconds || 0 : NaN; } /** * Get the milliseconds. * @return {number} */ get milliseconds() { return this.isValid ? this.values.milliseconds || 0 : NaN; } /** * Returns whether the Duration is invalid. Invalid durations are returned by diff operations * on invalid DateTimes or Intervals. * @return {boolean} */ get isValid() { return this.invalid === null; } /** * Returns an error code if this Duration became invalid, or null if the Duration is valid * @return {string} */ get invalidReason() { return this.invalid ? this.invalid.reason : null; } /** * Returns an explanation of why this Duration became invalid, or null if the Duration is valid * @type {string} */ get invalidExplanation() { return this.invalid ? this.invalid.explanation : null; } /** * Equality check * Two Durations are equal iff they have the same units and the same values for each unit. * @param {Duration} other * @return {boolean} */ equals(other) { if (!this.isValid || !other.isValid) { return false; } if (!this.loc.equals(other.loc)) { return false; } function eq(v1, v2) { if (v1 === void 0 || v1 === 0) return v2 === void 0 || v2 === 0; return v1 === v2; } for (const u of orderedUnits$1) { if (!eq(this.values[u], other.values[u])) { return false; } } return true; } }; var INVALID$1 = "Invalid Interval"; function validateStartEnd(start, end) { if (!start || !start.isValid) { return Interval.invalid("missing or invalid start"); } else if (!end || !end.isValid) { return Interval.invalid("missing or invalid end"); } else if (end < start) { return Interval.invalid( "end before start", `The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}` ); } else { return null; } } var Interval = class { /** * @private */ constructor(config) { this.s = config.start; this.e = config.end; this.invalid = config.invalid || null; this.isLuxonInterval = true; } /** * Create an invalid Interval. * @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information * @return {Interval} */ static invalid(reason, explanation = null) { if (!reason) { throw new InvalidArgumentError("need to specify a reason the Interval is invalid"); } const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation); if (Settings.throwOnInvalid) { throw new InvalidIntervalError(invalid); } else { return new Interval({ invalid }); } } /** * Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end. * @param {DateTime|Date|Object} start * @param {DateTime|Date|Object} end * @return {Interval} */ static fromDateTimes(start, end) { const builtStart = friendlyDateTime(start), builtEnd = friendlyDateTime(end); const validateError = validateStartEnd(builtStart, builtEnd); if (validateError == null) { return new Interval({ start: builtStart, end: builtEnd }); } else { return validateError; } } /** * Create an Interval from a start DateTime and a Duration to extend to. * @param {DateTime|Date|Object} start * @param {Duration|Object|number} duration - the length of the Interval. * @return {Interval} */ static after(start, duration) { const dur = Duration.fromDurationLike(duration), dt = friendlyDateTime(start); return Interval.fromDateTimes(dt, dt.plus(dur)); } /** * Create an Interval from an end DateTime and a Duration to extend backwards to. * @param {DateTime|Date|Object} end * @param {Duration|Object|number} duration - the length of the Interval. * @return {Interval} */ static before(end, duration) { const dur = Duration.fromDurationLike(duration), dt = friendlyDateTime(end); return Interval.fromDateTimes(dt.minus(dur), dt); } /** * Create an Interval from an ISO 8601 string. * Accepts `/`, `/`, and `/` formats. * @param {string} text - the ISO string to parse * @param {Object} [opts] - options to pass {@link DateTime#fromISO} and optionally {@link Duration#fromISO} * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals * @return {Interval} */ static fromISO(text, opts) { const [s2, e] = (text || "").split("/", 2); if (s2 && e) { let start, startIsValid; try { start = DateTime.fromISO(s2, opts); startIsValid = start.isValid; } catch (e2) { startIsValid = false; } let end, endIsValid; try { end = DateTime.fromISO(e, opts); endIsValid = end.isValid; } catch (e2) { endIsValid = false; } if (startIsValid && endIsValid) { return Interval.fromDateTimes(start, end); } if (startIsValid) { const dur = Duration.fromISO(e, opts); if (dur.isValid) { return Interval.after(start, dur); } } else if (endIsValid) { const dur = Duration.fromISO(s2, opts); if (dur.isValid) { return Interval.before(end, dur); } } } return Interval.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`); } /** * Check if an object is an Interval. Works across context boundaries * @param {object} o * @return {boolean} */ static isInterval(o) { return o && o.isLuxonInterval || false; } /** * Returns the start of the Interval * @type {DateTime} */ get start() { return this.isValid ? this.s : null; } /** * Returns the end of the Interval * @type {DateTime} */ get end() { return this.isValid ? this.e : null; } /** * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'. * @type {boolean} */ get isValid() { return this.invalidReason === null; } /** * Returns an error code if this Interval is invalid, or null if the Interval is valid * @type {string} */ get invalidReason() { return this.invalid ? this.invalid.reason : null; } /** * Returns an explanation of why this Interval became invalid, or null if the Interval is valid * @type {string} */ get invalidExplanation() { return this.invalid ? this.invalid.explanation : null; } /** * Returns the length of the Interval in the specified unit. * @param {string} unit - the unit (such as 'hours' or 'days') to return the length in. * @return {number} */ length(unit = "milliseconds") { return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN; } /** * Returns the count of minutes, hours, days, months, or years included in the Interval, even in part. * Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day' * asks 'what dates are included in this interval?', not 'how many days long is this interval?' * @param {string} [unit='milliseconds'] - the unit of time to count. * @return {number} */ count(unit = "milliseconds") { if (!this.isValid) return NaN; const start = this.start.startOf(unit), end = this.end.startOf(unit); return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf()); } /** * Returns whether this Interval's start and end are both in the same unit of time * @param {string} unit - the unit of time to check sameness on * @return {boolean} */ hasSame(unit) { return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false; } /** * Return whether this Interval has the same start and end DateTimes. * @return {boolean} */ isEmpty() { return this.s.valueOf() === this.e.valueOf(); } /** * Return whether this Interval's start is after the specified DateTime. * @param {DateTime} dateTime * @return {boolean} */ isAfter(dateTime) { if (!this.isValid) return false; return this.s > dateTime; } /** * Return whether this Interval's end is before the specified DateTime. * @param {DateTime} dateTime * @return {boolean} */ isBefore(dateTime) { if (!this.isValid) return false; return this.e <= dateTime; } /** * Return whether this Interval contains the specified DateTime. * @param {DateTime} dateTime * @return {boolean} */ contains(dateTime) { if (!this.isValid) return false; return this.s <= dateTime && this.e > dateTime; } /** * "Sets" the start and/or end dates. Returns a newly-constructed Interval. * @param {Object} values - the values to set * @param {DateTime} values.start - the starting DateTime * @param {DateTime} values.end - the ending DateTime * @return {Interval} */ set({ start, end } = {}) { if (!this.isValid) return this; return Interval.fromDateTimes(start || this.s, end || this.e); } /** * Split this Interval at each of the specified DateTimes * @param {...DateTime} dateTimes - the unit of time to count. * @return {Array} */ splitAt(...dateTimes) { if (!this.isValid) return []; const sorted = dateTimes.map(friendlyDateTime).filter((d) => this.contains(d)).sort(), results = []; let { s: s2 } = this, i = 0; while (s2 < this.e) { const added = sorted[i] || this.e, next = +added > +this.e ? this.e : added; results.push(Interval.fromDateTimes(s2, next)); s2 = next; i += 1; } return results; } /** * Split this Interval into smaller Intervals, each of the specified length. * Left over time is grouped into a smaller interval * @param {Duration|Object|number} duration - The length of each resulting interval. * @return {Array} */ splitBy(duration) { const dur = Duration.fromDurationLike(duration); if (!this.isValid || !dur.isValid || dur.as("milliseconds") === 0) { return []; } let { s: s2 } = this, idx = 1, next; const results = []; while (s2 < this.e) { const added = this.start.plus(dur.mapUnits((x) => x * idx)); next = +added > +this.e ? this.e : added; results.push(Interval.fromDateTimes(s2, next)); s2 = next; idx += 1; } return results; } /** * Split this Interval into the specified number of smaller intervals. * @param {number} numberOfParts - The number of Intervals to divide the Interval into. * @return {Array} */ divideEqually(numberOfParts) { if (!this.isValid) return []; return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts); } /** * Return whether this Interval overlaps with the specified Interval * @param {Interval} other * @return {boolean} */ overlaps(other) { return this.e > other.s && this.s < other.e; } /** * Return whether this Interval's end is adjacent to the specified Interval's start. * @param {Interval} other * @return {boolean} */ abutsStart(other) { if (!this.isValid) return false; return +this.e === +other.s; } /** * Return whether this Interval's start is adjacent to the specified Interval's end. * @param {Interval} other * @return {boolean} */ abutsEnd(other) { if (!this.isValid) return false; return +other.e === +this.s; } /** * Return whether this Interval engulfs the start and end of the specified Interval. * @param {Interval} other * @return {boolean} */ engulfs(other) { if (!this.isValid) return false; return this.s <= other.s && this.e >= other.e; } /** * Return whether this Interval has the same start and end as the specified Interval. * @param {Interval} other * @return {boolean} */ equals(other) { if (!this.isValid || !other.isValid) { return false; } return this.s.equals(other.s) && this.e.equals(other.e); } /** * Return an Interval representing the intersection of this Interval and the specified Interval. * Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals. * Returns null if the intersection is empty, meaning, the intervals don't intersect. * @param {Interval} other * @return {Interval} */ intersection(other) { if (!this.isValid) return this; const s2 = this.s > other.s ? this.s : other.s, e = this.e < other.e ? this.e : other.e; if (s2 >= e) { return null; } else { return Interval.fromDateTimes(s2, e); } } /** * Return an Interval representing the union of this Interval and the specified Interval. * Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals. * @param {Interval} other * @return {Interval} */ union(other) { if (!this.isValid) return this; const s2 = this.s < other.s ? this.s : other.s, e = this.e > other.e ? this.e : other.e; return Interval.fromDateTimes(s2, e); } /** * Merge an array of Intervals into a equivalent minimal set of Intervals. * Combines overlapping and adjacent Intervals. * @param {Array} intervals * @return {Array} */ static merge(intervals) { const [found, final] = intervals.sort((a, b) => a.s - b.s).reduce( ([sofar, current], item) => { if (!current) { return [sofar, item]; } else if (current.overlaps(item) || current.abutsStart(item)) { return [sofar, current.union(item)]; } else { return [sofar.concat([current]), item]; } }, [[], null] ); if (final) { found.push(final); } return found; } /** * Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals. * @param {Array} intervals * @return {Array} */ static xor(intervals) { let start = null, currentCount = 0; const results = [], ends = intervals.map((i) => [ { time: i.s, type: "s" }, { time: i.e, type: "e" } ]), flattened = Array.prototype.concat(...ends), arr = flattened.sort((a, b) => a.time - b.time); for (const i of arr) { currentCount += i.type === "s" ? 1 : -1; if (currentCount === 1) { start = i.time; } else { if (start && +start !== +i.time) { results.push(Interval.fromDateTimes(start, i.time)); } start = null; } } return Interval.merge(results); } /** * Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals. * @param {...Interval} intervals * @return {Array} */ difference(...intervals) { return Interval.xor([this].concat(intervals)).map((i) => this.intersection(i)).filter((i) => i && !i.isEmpty()); } /** * Returns a string representation of this Interval appropriate for debugging. * @return {string} */ toString() { if (!this.isValid) return INVALID$1; return `[${this.s.toISO()} \u2013 ${this.e.toISO()})`; } /** * Returns a localized string representing this Interval. Accepts the same options as the * Intl.DateTimeFormat constructor and any presets defined by Luxon, such as * {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method * is browser-specific, but in general it will return an appropriate representation of the * Interval in the assigned locale. Defaults to the system's locale if no locale has been * specified. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat * @param {Object} [formatOpts=DateTime.DATE_SHORT] - Either a DateTime preset or * Intl.DateTimeFormat constructor options. * @param {Object} opts - Options to override the configuration of the start DateTime. * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 – 11/8/2022 * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 – 8, 2022 * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7–8 novembre 2022 * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 – 8:00 PM * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> Mon, Nov 07, 6:00 – 8:00 p * @return {string} */ toLocaleString(formatOpts = DATE_SHORT, opts = {}) { return this.isValid ? Formatter2.create(this.s.loc.clone(opts), formatOpts).formatInterval(this) : INVALID$1; } /** * Returns an ISO 8601-compliant string representation of this Interval. * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals * @param {Object} opts - The same options as {@link DateTime#toISO} * @return {string} */ toISO(opts) { if (!this.isValid) return INVALID$1; return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`; } /** * Returns an ISO 8601-compliant string representation of date of this Interval. * The time components are ignored. * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals * @return {string} */ toISODate() { if (!this.isValid) return INVALID$1; return `${this.s.toISODate()}/${this.e.toISODate()}`; } /** * Returns an ISO 8601-compliant string representation of time of this Interval. * The date components are ignored. * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals * @param {Object} opts - The same options as {@link DateTime#toISO} * @return {string} */ toISOTime(opts) { if (!this.isValid) return INVALID$1; return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`; } /** * Returns a string representation of this Interval formatted according to the specified format * string. **You may not want this.** See {@link Interval#toLocaleString} for a more flexible * formatting tool. * @param {string} dateFormat - The format string. This string formats the start and end time. * See {@link DateTime#toFormat} for details. * @param {Object} opts - Options. * @param {string} [opts.separator = ' – '] - A separator to place between the start and end * representations. * @return {string} */ toFormat(dateFormat, { separator = " \u2013 " } = {}) { if (!this.isValid) return INVALID$1; return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`; } /** * Return a Duration representing the time spanned by this interval. * @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration. * @param {Object} opts - options that affect the creation of the Duration * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use * @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 } * @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 } * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 } * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 } * @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 } * @return {Duration} */ toDuration(unit, opts) { if (!this.isValid) { return Duration.invalid(this.invalidReason); } return this.e.diff(this.s, unit, opts); } /** * Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes * @param {function} mapFn * @return {Interval} * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC()) * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 })) */ mapEndpoints(mapFn) { return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e)); } }; var Info = class { /** * Return whether the specified zone contains a DST. * @param {string|Zone} [zone='local'] - Zone to check. Defaults to the environment's local zone. * @return {boolean} */ static hasDST(zone = Settings.defaultZone) { const proto = DateTime.now().setZone(zone).set({ month: 12 }); return !zone.isUniversal && proto.offset !== proto.set({ month: 6 }).offset; } /** * Return whether the specified zone is a valid IANA specifier. * @param {string} zone - Zone to check * @return {boolean} */ static isValidIANAZone(zone) { return IANAZone.isValidZone(zone); } /** * Converts the input into a {@link Zone} instance. * * * If `input` is already a Zone instance, it is returned unchanged. * * If `input` is a string containing a valid time zone name, a Zone instance * with that name is returned. * * If `input` is a string that doesn't refer to a known time zone, a Zone * instance with {@link Zone#isValid} == false is returned. * * If `input is a number, a Zone instance with the specified fixed offset * in minutes is returned. * * If `input` is `null` or `undefined`, the default zone is returned. * @param {string|Zone|number} [input] - the value to be converted * @return {Zone} */ static normalizeZone(input) { return normalizeZone(input, Settings.defaultZone); } /** * Return an array of standalone month names. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long" * @param {Object} opts - options * @param {string} [opts.locale] - the locale code * @param {string} [opts.numberingSystem=null] - the numbering system * @param {string} [opts.locObj=null] - an existing locale object to use * @param {string} [opts.outputCalendar='gregory'] - the calendar * @example Info.months()[0] //=> 'January' * @example Info.months('short')[0] //=> 'Jan' * @example Info.months('numeric')[0] //=> '1' * @example Info.months('short', { locale: 'fr-CA' } )[0] //=> 'janv.' * @example Info.months('numeric', { locale: 'ar' })[0] //=> '١' * @example Info.months('long', { outputCalendar: 'islamic' })[0] //=> 'Rabiʻ I' * @return {Array} */ static months(length = "long", { locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}) { return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length); } /** * Return an array of format month names. * Format months differ from standalone months in that they're meant to appear next to the day of the month. In some languages, that * changes the string. * See {@link Info#months} * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long" * @param {Object} opts - options * @param {string} [opts.locale] - the locale code * @param {string} [opts.numberingSystem=null] - the numbering system * @param {string} [opts.locObj=null] - an existing locale object to use * @param {string} [opts.outputCalendar='gregory'] - the calendar * @return {Array} */ static monthsFormat(length = "long", { locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}) { return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length, true); } /** * Return an array of standalone week names. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat * @param {string} [length='long'] - the length of the weekday representation, such as "narrow", "short", "long". * @param {Object} opts - options * @param {string} [opts.locale] - the locale code * @param {string} [opts.numberingSystem=null] - the numbering system * @param {string} [opts.locObj=null] - an existing locale object to use * @example Info.weekdays()[0] //=> 'Monday' * @example Info.weekdays('short')[0] //=> 'Mon' * @example Info.weekdays('short', { locale: 'fr-CA' })[0] //=> 'lun.' * @example Info.weekdays('short', { locale: 'ar' })[0] //=> 'الاثنين' * @return {Array} */ static weekdays(length = "long", { locale = null, numberingSystem = null, locObj = null } = {}) { return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length); } /** * Return an array of format week names. * Format weekdays differ from standalone weekdays in that they're meant to appear next to more date information. In some languages, that * changes the string. * See {@link Info#weekdays} * @param {string} [length='long'] - the length of the month representation, such as "narrow", "short", "long". * @param {Object} opts - options * @param {string} [opts.locale=null] - the locale code * @param {string} [opts.numberingSystem=null] - the numbering system * @param {string} [opts.locObj=null] - an existing locale object to use * @return {Array} */ static weekdaysFormat(length = "long", { locale = null, numberingSystem = null, locObj = null } = {}) { return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length, true); } /** * Return an array of meridiems. * @param {Object} opts - options * @param {string} [opts.locale] - the locale code * @example Info.meridiems() //=> [ 'AM', 'PM' ] * @example Info.meridiems({ locale: 'my' }) //=> [ 'နံနက်', 'ညနေ' ] * @return {Array} */ static meridiems({ locale = null } = {}) { return Locale.create(locale).meridiems(); } /** * Return an array of eras, such as ['BC', 'AD']. The locale can be specified, but the calendar system is always Gregorian. * @param {string} [length='short'] - the length of the era representation, such as "short" or "long". * @param {Object} opts - options * @param {string} [opts.locale] - the locale code * @example Info.eras() //=> [ 'BC', 'AD' ] * @example Info.eras('long') //=> [ 'Before Christ', 'Anno Domini' ] * @example Info.eras('long', { locale: 'fr' }) //=> [ 'avant Jésus-Christ', 'après Jésus-Christ' ] * @return {Array} */ static eras(length = "short", { locale = null } = {}) { return Locale.create(locale, null, "gregory").eras(length); } /** * Return the set of available features in this environment. * Some features of Luxon are not available in all environments. For example, on older browsers, relative time formatting support is not available. Use this function to figure out if that's the case. * Keys: * * `relative`: whether this environment supports relative time formatting * @example Info.features() //=> { relative: false } * @return {Object} */ static features() { return { relative: hasRelative() }; } }; function dayDiff(earlier, later) { const utcDayStart = (dt) => dt.toUTC(0, { keepLocalTime: true }).startOf("day").valueOf(), ms = utcDayStart(later) - utcDayStart(earlier); return Math.floor(Duration.fromMillis(ms).as("days")); } function highOrderDiffs(cursor, later, units) { const differs = [ ["years", (a, b) => b.year - a.year], ["quarters", (a, b) => b.quarter - a.quarter + (b.year - a.year) * 4], ["months", (a, b) => b.month - a.month + (b.year - a.year) * 12], [ "weeks", (a, b) => { const days = dayDiff(a, b); return (days - days % 7) / 7; } ], ["days", dayDiff] ]; const results = {}; const earlier = cursor; let lowestOrder, highWater; for (const [unit, differ] of differs) { if (units.indexOf(unit) >= 0) { lowestOrder = unit; results[unit] = differ(cursor, later); highWater = earlier.plus(results); if (highWater > later) { results[unit]--; cursor = earlier.plus(results); if (cursor > later) { highWater = cursor; results[unit]--; cursor = earlier.plus(results); } } else { cursor = highWater; } } } return [cursor, results, highWater, lowestOrder]; } function diff(earlier, later, units, opts) { let [cursor, results, highWater, lowestOrder] = highOrderDiffs(earlier, later, units); const remainingMillis = later - cursor; const lowerOrderUnits = units.filter( (u) => ["hours", "minutes", "seconds", "milliseconds"].indexOf(u) >= 0 ); if (lowerOrderUnits.length === 0) { if (highWater < later) { highWater = cursor.plus({ [lowestOrder]: 1 }); } if (highWater !== cursor) { results[lowestOrder] = (results[lowestOrder] || 0) + remainingMillis / (highWater - cursor); } } const duration = Duration.fromObject(results, opts); if (lowerOrderUnits.length > 0) { return Duration.fromMillis(remainingMillis, opts).shiftTo(...lowerOrderUnits).plus(duration); } else { return duration; } } var numberingSystems = { arab: "[\u0660-\u0669]", arabext: "[\u06F0-\u06F9]", bali: "[\u1B50-\u1B59]", beng: "[\u09E6-\u09EF]", deva: "[\u0966-\u096F]", fullwide: "[\uFF10-\uFF19]", gujr: "[\u0AE6-\u0AEF]", hanidec: "[\u3007|\u4E00|\u4E8C|\u4E09|\u56DB|\u4E94|\u516D|\u4E03|\u516B|\u4E5D]", khmr: "[\u17E0-\u17E9]", knda: "[\u0CE6-\u0CEF]", laoo: "[\u0ED0-\u0ED9]", limb: "[\u1946-\u194F]", mlym: "[\u0D66-\u0D6F]", mong: "[\u1810-\u1819]", mymr: "[\u1040-\u1049]", orya: "[\u0B66-\u0B6F]", tamldec: "[\u0BE6-\u0BEF]", telu: "[\u0C66-\u0C6F]", thai: "[\u0E50-\u0E59]", tibt: "[\u0F20-\u0F29]", latn: "\\d" }; var numberingSystemsUTF16 = { arab: [1632, 1641], arabext: [1776, 1785], bali: [6992, 7001], beng: [2534, 2543], deva: [2406, 2415], fullwide: [65296, 65303], gujr: [2790, 2799], khmr: [6112, 6121], knda: [3302, 3311], laoo: [3792, 3801], limb: [6470, 6479], mlym: [3430, 3439], mong: [6160, 6169], mymr: [4160, 4169], orya: [2918, 2927], tamldec: [3046, 3055], telu: [3174, 3183], thai: [3664, 3673], tibt: [3872, 3881] }; var hanidecChars = numberingSystems.hanidec.replace(/[\[|\]]/g, "").split(""); function parseDigits(str) { let value = parseInt(str, 10); if (isNaN(value)) { value = ""; for (let i = 0; i < str.length; i++) { const code = str.charCodeAt(i); if (str[i].search(numberingSystems.hanidec) !== -1) { value += hanidecChars.indexOf(str[i]); } else { for (const key in numberingSystemsUTF16) { const [min, max] = numberingSystemsUTF16[key]; if (code >= min && code <= max) { value += code - min; } } } } return parseInt(value, 10); } else { return value; } } function digitRegex({ numberingSystem }, append = "") { return new RegExp(`${numberingSystems[numberingSystem || "latn"]}${append}`); } var MISSING_FTP = "missing Intl.DateTimeFormat.formatToParts support"; function intUnit(regex, post = (i) => i) { return { regex, deser: ([s2]) => post(parseDigits(s2)) }; } var NBSP = String.fromCharCode(160); var spaceOrNBSP = `[ ${NBSP}]`; var spaceOrNBSPRegExp = new RegExp(spaceOrNBSP, "g"); function fixListRegex(s2) { return s2.replace(/\./g, "\\.?").replace(spaceOrNBSPRegExp, spaceOrNBSP); } function stripInsensitivities(s2) { return s2.replace(/\./g, "").replace(spaceOrNBSPRegExp, " ").toLowerCase(); } function oneOf(strings, startIndex) { if (strings === null) { return null; } else { return { regex: RegExp(strings.map(fixListRegex).join("|")), deser: ([s2]) => strings.findIndex((i) => stripInsensitivities(s2) === stripInsensitivities(i)) + startIndex }; } } function offset(regex, groups) { return { regex, deser: ([, h, m]) => signedOffset(h, m), groups }; } function simple(regex) { return { regex, deser: ([s2]) => s2 }; } function escapeToken(value) { return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); } function unitForToken(token, loc) { const one = digitRegex(loc), two = digitRegex(loc, "{2}"), three = digitRegex(loc, "{3}"), four = digitRegex(loc, "{4}"), six = digitRegex(loc, "{6}"), oneOrTwo = digitRegex(loc, "{1,2}"), oneToThree = digitRegex(loc, "{1,3}"), oneToSix = digitRegex(loc, "{1,6}"), oneToNine = digitRegex(loc, "{1,9}"), twoToFour = digitRegex(loc, "{2,4}"), fourToSix = digitRegex(loc, "{4,6}"), literal = (t2) => ({ regex: RegExp(escapeToken(t2.val)), deser: ([s2]) => s2, literal: true }), unitate = (t2) => { if (token.literal) { return literal(t2); } switch (t2.val) { case "G": return oneOf(loc.eras("short"), 0); case "GG": return oneOf(loc.eras("long"), 0); case "y": return intUnit(oneToSix); case "yy": return intUnit(twoToFour, untruncateYear); case "yyyy": return intUnit(four); case "yyyyy": return intUnit(fourToSix); case "yyyyyy": return intUnit(six); case "M": return intUnit(oneOrTwo); case "MM": return intUnit(two); case "MMM": return oneOf(loc.months("short", true), 1); case "MMMM": return oneOf(loc.months("long", true), 1); case "L": return intUnit(oneOrTwo); case "LL": return intUnit(two); case "LLL": return oneOf(loc.months("short", false), 1); case "LLLL": return oneOf(loc.months("long", false), 1); case "d": return intUnit(oneOrTwo); case "dd": return intUnit(two); case "o": return intUnit(oneToThree); case "ooo": return intUnit(three); case "HH": return intUnit(two); case "H": return intUnit(oneOrTwo); case "hh": return intUnit(two); case "h": return intUnit(oneOrTwo); case "mm": return intUnit(two); case "m": return intUnit(oneOrTwo); case "q": return intUnit(oneOrTwo); case "qq": return intUnit(two); case "s": return intUnit(oneOrTwo); case "ss": return intUnit(two); case "S": return intUnit(oneToThree); case "SSS": return intUnit(three); case "u": return simple(oneToNine); case "uu": return simple(oneOrTwo); case "uuu": return intUnit(one); case "a": return oneOf(loc.meridiems(), 0); case "kkkk": return intUnit(four); case "kk": return intUnit(twoToFour, untruncateYear); case "W": return intUnit(oneOrTwo); case "WW": return intUnit(two); case "E": case "c": return intUnit(one); case "EEE": return oneOf(loc.weekdays("short", false), 1); case "EEEE": return oneOf(loc.weekdays("long", false), 1); case "ccc": return oneOf(loc.weekdays("short", true), 1); case "cccc": return oneOf(loc.weekdays("long", true), 1); case "Z": case "ZZ": return offset(new RegExp(`([+-]${oneOrTwo.source})(?::(${two.source}))?`), 2); case "ZZZ": return offset(new RegExp(`([+-]${oneOrTwo.source})(${two.source})?`), 2); case "z": return simple(/[a-z_+-/]{1,256}?/i); case " ": return simple(/[^\S\n\r]/); default: return literal(t2); } }; const unit = unitate(token) || { invalidReason: MISSING_FTP }; unit.token = token; return unit; } var partTypeStyleToTokenVal = { year: { "2-digit": "yy", numeric: "yyyyy" }, month: { numeric: "M", "2-digit": "MM", short: "MMM", long: "MMMM" }, day: { numeric: "d", "2-digit": "dd" }, weekday: { short: "EEE", long: "EEEE" }, dayperiod: "a", dayPeriod: "a", hour12: { numeric: "h", "2-digit": "hh" }, hour24: { numeric: "H", "2-digit": "HH" }, minute: { numeric: "m", "2-digit": "mm" }, second: { numeric: "s", "2-digit": "ss" }, timeZoneName: { long: "ZZZZZ", short: "ZZZ" } }; function tokenForPart(part, formatOpts, resolvedOpts) { const { type, value } = part; if (type === "literal") { const isSpace = /^\s+$/.test(value); return { literal: !isSpace, val: isSpace ? " " : value }; } const style = formatOpts[type]; let actualType = type; if (type === "hour") { if (formatOpts.hour12 != null) { actualType = formatOpts.hour12 ? "hour12" : "hour24"; } else if (formatOpts.hourCycle != null) { if (formatOpts.hourCycle === "h11" || formatOpts.hourCycle === "h12") { actualType = "hour12"; } else { actualType = "hour24"; } } else { actualType = resolvedOpts.hour12 ? "hour12" : "hour24"; } } let val = partTypeStyleToTokenVal[actualType]; if (typeof val === "object") { val = val[style]; } if (val) { return { literal: false, val }; } return void 0; } function buildRegex(units) { const re = units.map((u) => u.regex).reduce((f, r) => `${f}(${r.source})`, ""); return [`^${re}$`, units]; } function match(input, regex, handlers) { const matches = input.match(regex); if (matches) { const all = {}; let matchIndex = 1; for (const i in handlers) { if (hasOwnProperty(handlers, i)) { const h = handlers[i], groups = h.groups ? h.groups + 1 : 1; if (!h.literal && h.token) { all[h.token.val[0]] = h.deser(matches.slice(matchIndex, matchIndex + groups)); } matchIndex += groups; } } return [matches, all]; } else { return [matches, {}]; } } function dateTimeFromMatches(matches) { const toField = (token) => { switch (token) { case "S": return "millisecond"; case "s": return "second"; case "m": return "minute"; case "h": case "H": return "hour"; case "d": return "day"; case "o": return "ordinal"; case "L": case "M": return "month"; case "y": return "year"; case "E": case "c": return "weekday"; case "W": return "weekNumber"; case "k": return "weekYear"; case "q": return "quarter"; default: return null; } }; let zone = null; let specificOffset; if (!isUndefined(matches.z)) { zone = IANAZone.create(matches.z); } if (!isUndefined(matches.Z)) { if (!zone) { zone = new FixedOffsetZone(matches.Z); } specificOffset = matches.Z; } if (!isUndefined(matches.q)) { matches.M = (matches.q - 1) * 3 + 1; } if (!isUndefined(matches.h)) { if (matches.h < 12 && matches.a === 1) { matches.h += 12; } else if (matches.h === 12 && matches.a === 0) { matches.h = 0; } } if (matches.G === 0 && matches.y) { matches.y = -matches.y; } if (!isUndefined(matches.u)) { matches.S = parseMillis(matches.u); } const vals = Object.keys(matches).reduce((r, k) => { const f = toField(k); if (f) { r[f] = matches[k]; } return r; }, {}); return [vals, zone, specificOffset]; } var dummyDateTimeCache = null; function getDummyDateTime() { if (!dummyDateTimeCache) { dummyDateTimeCache = DateTime.fromMillis(1555555555555); } return dummyDateTimeCache; } function maybeExpandMacroToken(token, locale) { if (token.literal) { return token; } const formatOpts = Formatter2.macroTokenToFormatOpts(token.val); const tokens = formatOptsToTokens(formatOpts, locale); if (tokens == null || tokens.includes(void 0)) { return token; } return tokens; } function expandMacroTokens(tokens, locale) { return Array.prototype.concat(...tokens.map((t2) => maybeExpandMacroToken(t2, locale))); } function explainFromTokens(locale, input, format) { const tokens = expandMacroTokens(Formatter2.parseFormat(format), locale), units = tokens.map((t2) => unitForToken(t2, locale)), disqualifyingUnit = units.find((t2) => t2.invalidReason); if (disqualifyingUnit) { return { input, tokens, invalidReason: disqualifyingUnit.invalidReason }; } else { const [regexString, handlers] = buildRegex(units), regex = RegExp(regexString, "i"), [rawMatches, matches] = match(input, regex, handlers), [result, zone, specificOffset] = matches ? dateTimeFromMatches(matches) : [null, null, void 0]; if (hasOwnProperty(matches, "a") && hasOwnProperty(matches, "H")) { throw new ConflictingSpecificationError( "Can't include meridiem when specifying 24-hour format" ); } return { input, tokens, regex, rawMatches, matches, result, zone, specificOffset }; } } function parseFromTokens(locale, input, format) { const { result, zone, specificOffset, invalidReason } = explainFromTokens(locale, input, format); return [result, zone, specificOffset, invalidReason]; } function formatOptsToTokens(formatOpts, locale) { if (!formatOpts) { return null; } const formatter = Formatter2.create(locale, formatOpts); const df = formatter.dtFormatter(getDummyDateTime()); const parts = df.formatToParts(); const resolvedOpts = df.resolvedOptions(); return parts.map((p) => tokenForPart(p, formatOpts, resolvedOpts)); } var nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; var leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]; function unitOutOfRange(unit, value) { return new Invalid( "unit out of range", `you specified ${value} (of type ${typeof value}) as a ${unit}, which is invalid` ); } function dayOfWeek(year, month, day) { const d = new Date(Date.UTC(year, month - 1, day)); if (year < 100 && year >= 0) { d.setUTCFullYear(d.getUTCFullYear() - 1900); } const js = d.getUTCDay(); return js === 0 ? 7 : js; } function computeOrdinal(year, month, day) { return day + (isLeapYear(year) ? leapLadder : nonLeapLadder)[month - 1]; } function uncomputeOrdinal(year, ordinal) { const table = isLeapYear(year) ? leapLadder : nonLeapLadder, month0 = table.findIndex((i) => i < ordinal), day = ordinal - table[month0]; return { month: month0 + 1, day }; } function gregorianToWeek(gregObj) { const { year, month, day } = gregObj, ordinal = computeOrdinal(year, month, day), weekday = dayOfWeek(year, month, day); let weekNumber = Math.floor((ordinal - weekday + 10) / 7), weekYear; if (weekNumber < 1) { weekYear = year - 1; weekNumber = weeksInWeekYear(weekYear); } else if (weekNumber > weeksInWeekYear(year)) { weekYear = year + 1; weekNumber = 1; } else { weekYear = year; } return { weekYear, weekNumber, weekday, ...timeObject(gregObj) }; } function weekToGregorian(weekData) { const { weekYear, weekNumber, weekday } = weekData, weekdayOfJan4 = dayOfWeek(weekYear, 1, 4), yearInDays = daysInYear(weekYear); let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 3, year; if (ordinal < 1) { year = weekYear - 1; ordinal += daysInYear(year); } else if (ordinal > yearInDays) { year = weekYear + 1; ordinal -= daysInYear(weekYear); } else { year = weekYear; } const { month, day } = uncomputeOrdinal(year, ordinal); return { year, month, day, ...timeObject(weekData) }; } function gregorianToOrdinal(gregData) { const { year, month, day } = gregData; const ordinal = computeOrdinal(year, month, day); return { year, ordinal, ...timeObject(gregData) }; } function ordinalToGregorian(ordinalData) { const { year, ordinal } = ordinalData; const { month, day } = uncomputeOrdinal(year, ordinal); return { year, month, day, ...timeObject(ordinalData) }; } function hasInvalidWeekData(obj) { const validYear = isInteger(obj.weekYear), validWeek = integerBetween(obj.weekNumber, 1, weeksInWeekYear(obj.weekYear)), validWeekday = integerBetween(obj.weekday, 1, 7); if (!validYear) { return unitOutOfRange("weekYear", obj.weekYear); } else if (!validWeek) { return unitOutOfRange("week", obj.week); } else if (!validWeekday) { return unitOutOfRange("weekday", obj.weekday); } else return false; } function hasInvalidOrdinalData(obj) { const validYear = isInteger(obj.year), validOrdinal = integerBetween(obj.ordinal, 1, daysInYear(obj.year)); if (!validYear) { return unitOutOfRange("year", obj.year); } else if (!validOrdinal) { return unitOutOfRange("ordinal", obj.ordinal); } else return false; } function hasInvalidGregorianData(obj) { const validYear = isInteger(obj.year), validMonth = integerBetween(obj.month, 1, 12), validDay = integerBetween(obj.day, 1, daysInMonth(obj.year, obj.month)); if (!validYear) { return unitOutOfRange("year", obj.year); } else if (!validMonth) { return unitOutOfRange("month", obj.month); } else if (!validDay) { return unitOutOfRange("day", obj.day); } else return false; } function hasInvalidTimeData(obj) { const { hour, minute, second, millisecond } = obj; const validHour = integerBetween(hour, 0, 23) || hour === 24 && minute === 0 && second === 0 && millisecond === 0, validMinute = integerBetween(minute, 0, 59), validSecond = integerBetween(second, 0, 59), validMillisecond = integerBetween(millisecond, 0, 999); if (!validHour) { return unitOutOfRange("hour", hour); } else if (!validMinute) { return unitOutOfRange("minute", minute); } else if (!validSecond) { return unitOutOfRange("second", second); } else if (!validMillisecond) { return unitOutOfRange("millisecond", millisecond); } else return false; } var INVALID = "Invalid DateTime"; var MAX_DATE = 864e13; function unsupportedZone(zone) { return new Invalid("unsupported zone", `the zone "${zone.name}" is not supported`); } function possiblyCachedWeekData(dt) { if (dt.weekData === null) { dt.weekData = gregorianToWeek(dt.c); } return dt.weekData; } function clone(inst, alts) { const current = { ts: inst.ts, zone: inst.zone, c: inst.c, o: inst.o, loc: inst.loc, invalid: inst.invalid }; return new DateTime({ ...current, ...alts, old: current }); } function fixOffset(localTS, o, tz) { let utcGuess = localTS - o * 60 * 1e3; const o2 = tz.offset(utcGuess); if (o === o2) { return [utcGuess, o]; } utcGuess -= (o2 - o) * 60 * 1e3; const o3 = tz.offset(utcGuess); if (o2 === o3) { return [utcGuess, o2]; } return [localTS - Math.min(o2, o3) * 60 * 1e3, Math.max(o2, o3)]; } function tsToObj(ts, offset2) { ts += offset2 * 60 * 1e3; const d = new Date(ts); return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate(), hour: d.getUTCHours(), minute: d.getUTCMinutes(), second: d.getUTCSeconds(), millisecond: d.getUTCMilliseconds() }; } function objToTS(obj, offset2, zone) { return fixOffset(objToLocalTS(obj), offset2, zone); } function adjustTime(inst, dur) { const oPre = inst.o, year = inst.c.year + Math.trunc(dur.years), month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3, c = { ...inst.c, year, month, day: Math.min(inst.c.day, daysInMonth(year, month)) + Math.trunc(dur.days) + Math.trunc(dur.weeks) * 7 }, millisToAdd = Duration.fromObject({ years: dur.years - Math.trunc(dur.years), quarters: dur.quarters - Math.trunc(dur.quarters), months: dur.months - Math.trunc(dur.months), weeks: dur.weeks - Math.trunc(dur.weeks), days: dur.days - Math.trunc(dur.days), hours: dur.hours, minutes: dur.minutes, seconds: dur.seconds, milliseconds: dur.milliseconds }).as("milliseconds"), localTS = objToLocalTS(c); let [ts, o] = fixOffset(localTS, oPre, inst.zone); if (millisToAdd !== 0) { ts += millisToAdd; o = inst.zone.offset(ts); } return { ts, o }; } function parseDataToDateTime(parsed, parsedZone, opts, format, text, specificOffset) { const { setZone, zone } = opts; if (parsed && Object.keys(parsed).length !== 0 || parsedZone) { const interpretationZone = parsedZone || zone, inst = DateTime.fromObject(parsed, { ...opts, zone: interpretationZone, specificOffset }); return setZone ? inst : inst.setZone(zone); } else { return DateTime.invalid( new Invalid("unparsable", `the input "${text}" can't be parsed as ${format}`) ); } } function toTechFormat(dt, format, allowZ = true) { return dt.isValid ? Formatter2.create(Locale.create("en-US"), { allowZ, forceSimple: true }).formatDateTimeFromString(dt, format) : null; } function toISODate(o, extended) { const longFormat = o.c.year > 9999 || o.c.year < 0; let c = ""; if (longFormat && o.c.year >= 0) c += "+"; c += padStart(o.c.year, longFormat ? 6 : 4); if (extended) { c += "-"; c += padStart(o.c.month); c += "-"; c += padStart(o.c.day); } else { c += padStart(o.c.month); c += padStart(o.c.day); } return c; } function toISOTime(o, extended, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone) { let c = padStart(o.c.hour); if (extended) { c += ":"; c += padStart(o.c.minute); if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) { c += ":"; } } else { c += padStart(o.c.minute); } if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) { c += padStart(o.c.second); if (o.c.millisecond !== 0 || !suppressMilliseconds) { c += "."; c += padStart(o.c.millisecond, 3); } } if (includeOffset) { if (o.isOffsetFixed && o.offset === 0 && !extendedZone) { c += "Z"; } else if (o.o < 0) { c += "-"; c += padStart(Math.trunc(-o.o / 60)); c += ":"; c += padStart(Math.trunc(-o.o % 60)); } else { c += "+"; c += padStart(Math.trunc(o.o / 60)); c += ":"; c += padStart(Math.trunc(o.o % 60)); } } if (extendedZone) { c += "[" + o.zone.ianaName + "]"; } return c; } var defaultUnitValues = { month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 0 }; var defaultWeekUnitValues = { weekNumber: 1, weekday: 1, hour: 0, minute: 0, second: 0, millisecond: 0 }; var defaultOrdinalUnitValues = { ordinal: 1, hour: 0, minute: 0, second: 0, millisecond: 0 }; var orderedUnits = ["year", "month", "day", "hour", "minute", "second", "millisecond"]; var orderedWeekUnits = [ "weekYear", "weekNumber", "weekday", "hour", "minute", "second", "millisecond" ]; var orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"]; function normalizeUnit(unit) { const normalized = { year: "year", years: "year", month: "month", months: "month", day: "day", days: "day", hour: "hour", hours: "hour", minute: "minute", minutes: "minute", quarter: "quarter", quarters: "quarter", second: "second", seconds: "second", millisecond: "millisecond", milliseconds: "millisecond", weekday: "weekday", weekdays: "weekday", weeknumber: "weekNumber", weeksnumber: "weekNumber", weeknumbers: "weekNumber", weekyear: "weekYear", weekyears: "weekYear", ordinal: "ordinal" }[unit.toLowerCase()]; if (!normalized) throw new InvalidUnitError(unit); return normalized; } function quickDT(obj, opts) { const zone = normalizeZone(opts.zone, Settings.defaultZone), loc = Locale.fromObject(opts), tsNow = Settings.now(); let ts, o; if (!isUndefined(obj.year)) { for (const u of orderedUnits) { if (isUndefined(obj[u])) { obj[u] = defaultUnitValues[u]; } } const invalid = hasInvalidGregorianData(obj) || hasInvalidTimeData(obj); if (invalid) { return DateTime.invalid(invalid); } const offsetProvis = zone.offset(tsNow); [ts, o] = objToTS(obj, offsetProvis, zone); } else { ts = tsNow; } return new DateTime({ ts, zone, loc, o }); } function diffRelative(start, end, opts) { const round = isUndefined(opts.round) ? true : opts.round, format = (c, unit) => { c = roundTo(c, round || opts.calendary ? 0 : 2, true); const formatter = end.loc.clone(opts).relFormatter(opts); return formatter.format(c, unit); }, differ = (unit) => { if (opts.calendary) { if (!end.hasSame(start, unit)) { return end.startOf(unit).diff(start.startOf(unit), unit).get(unit); } else return 0; } else { return end.diff(start, unit).get(unit); } }; if (opts.unit) { return format(differ(opts.unit), opts.unit); } for (const unit of opts.units) { const count = differ(unit); if (Math.abs(count) >= 1) { return format(count, unit); } } return format(start > end ? -0 : 0, opts.units[opts.units.length - 1]); } function lastOpts(argList) { let opts = {}, args; if (argList.length > 0 && typeof argList[argList.length - 1] === "object") { opts = argList[argList.length - 1]; args = Array.from(argList).slice(0, argList.length - 1); } else { args = Array.from(argList); } return [opts, args]; } var DateTime = class { /** * @access private */ constructor(config) { const zone = config.zone || Settings.defaultZone; let invalid = config.invalid || (Number.isNaN(config.ts) ? new Invalid("invalid input") : null) || (!zone.isValid ? unsupportedZone(zone) : null); this.ts = isUndefined(config.ts) ? Settings.now() : config.ts; let c = null, o = null; if (!invalid) { const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone); if (unchanged) { [c, o] = [config.old.c, config.old.o]; } else { const ot = zone.offset(this.ts); c = tsToObj(this.ts, ot); invalid = Number.isNaN(c.year) ? new Invalid("invalid input") : null; c = invalid ? null : c; o = invalid ? null : ot; } } this._zone = zone; this.loc = config.loc || Locale.create(); this.invalid = invalid; this.weekData = null; this.c = c; this.o = o; this.isLuxonDateTime = true; } // CONSTRUCT /** * Create a DateTime for the current instant, in the system's time zone. * * Use Settings to override these default values if needed. * @example DateTime.now().toISO() //~> now in the ISO format * @return {DateTime} */ static now() { return new DateTime({}); } /** * Create a local DateTime * @param {number} [year] - The calendar year. If omitted (as in, call `local()` with no arguments), the current time will be used * @param {number} [month=1] - The month, 1-indexed * @param {number} [day=1] - The day of the month, 1-indexed * @param {number} [hour=0] - The hour of the day, in 24-hour time * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59 * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59 * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999 * @example DateTime.local() //~> now * @example DateTime.local({ zone: "America/New_York" }) //~> now, in US east coast time * @example DateTime.local(2017) //~> 2017-01-01T00:00:00 * @example DateTime.local(2017, 3) //~> 2017-03-01T00:00:00 * @example DateTime.local(2017, 3, 12, { locale: "fr" }) //~> 2017-03-12T00:00:00, with a French locale * @example DateTime.local(2017, 3, 12, 5) //~> 2017-03-12T05:00:00 * @example DateTime.local(2017, 3, 12, 5, { zone: "utc" }) //~> 2017-03-12T05:00:00, in UTC * @example DateTime.local(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00 * @example DateTime.local(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10 * @example DateTime.local(2017, 3, 12, 5, 45, 10, 765) //~> 2017-03-12T05:45:10.765 * @return {DateTime} */ static local() { const [opts, args] = lastOpts(arguments), [year, month, day, hour, minute, second, millisecond] = args; return quickDT({ year, month, day, hour, minute, second, millisecond }, opts); } /** * Create a DateTime in UTC * @param {number} [year] - The calendar year. If omitted (as in, call `utc()` with no arguments), the current time will be used * @param {number} [month=1] - The month, 1-indexed * @param {number} [day=1] - The day of the month * @param {number} [hour=0] - The hour of the day, in 24-hour time * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59 * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59 * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999 * @param {Object} options - configuration options for the DateTime * @param {string} [options.locale] - a locale to set on the resulting DateTime instance * @param {string} [options.outputCalendar] - the output calendar to set on the resulting DateTime instance * @param {string} [options.numberingSystem] - the numbering system to set on the resulting DateTime instance * @example DateTime.utc() //~> now * @example DateTime.utc(2017) //~> 2017-01-01T00:00:00Z * @example DateTime.utc(2017, 3) //~> 2017-03-01T00:00:00Z * @example DateTime.utc(2017, 3, 12) //~> 2017-03-12T00:00:00Z * @example DateTime.utc(2017, 3, 12, 5) //~> 2017-03-12T05:00:00Z * @example DateTime.utc(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00Z * @example DateTime.utc(2017, 3, 12, 5, 45, { locale: "fr" }) //~> 2017-03-12T05:45:00Z with a French locale * @example DateTime.utc(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10Z * @example DateTime.utc(2017, 3, 12, 5, 45, 10, 765, { locale: "fr" }) //~> 2017-03-12T05:45:10.765Z with a French locale * @return {DateTime} */ static utc() { const [opts, args] = lastOpts(arguments), [year, month, day, hour, minute, second, millisecond] = args; opts.zone = FixedOffsetZone.utcInstance; return quickDT({ year, month, day, hour, minute, second, millisecond }, opts); } /** * Create a DateTime from a JavaScript Date object. Uses the default zone. * @param {Date} date - a JavaScript Date object * @param {Object} options - configuration options for the DateTime * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into * @return {DateTime} */ static fromJSDate(date, options = {}) { const ts = isDate(date) ? date.valueOf() : NaN; if (Number.isNaN(ts)) { return DateTime.invalid("invalid input"); } const zoneToUse = normalizeZone(options.zone, Settings.defaultZone); if (!zoneToUse.isValid) { return DateTime.invalid(unsupportedZone(zoneToUse)); } return new DateTime({ ts, zone: zoneToUse, loc: Locale.fromObject(options) }); } /** * Create a DateTime from a number of milliseconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone. * @param {number} milliseconds - a number of milliseconds since 1970 UTC * @param {Object} options - configuration options for the DateTime * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into * @param {string} [options.locale] - a locale to set on the resulting DateTime instance * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance * @return {DateTime} */ static fromMillis(milliseconds, options = {}) { if (!isNumber(milliseconds)) { throw new InvalidArgumentError( `fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}` ); } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) { return DateTime.invalid("Timestamp out of range"); } else { return new DateTime({ ts: milliseconds, zone: normalizeZone(options.zone, Settings.defaultZone), loc: Locale.fromObject(options) }); } } /** * Create a DateTime from a number of seconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone. * @param {number} seconds - a number of seconds since 1970 UTC * @param {Object} options - configuration options for the DateTime * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into * @param {string} [options.locale] - a locale to set on the resulting DateTime instance * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance * @return {DateTime} */ static fromSeconds(seconds, options = {}) { if (!isNumber(seconds)) { throw new InvalidArgumentError("fromSeconds requires a numerical input"); } else { return new DateTime({ ts: seconds * 1e3, zone: normalizeZone(options.zone, Settings.defaultZone), loc: Locale.fromObject(options) }); } } /** * Create a DateTime from a JavaScript object with keys like 'year' and 'hour' with reasonable defaults. * @param {Object} obj - the object to create the DateTime from * @param {number} obj.year - a year, such as 1987 * @param {number} obj.month - a month, 1-12 * @param {number} obj.day - a day of the month, 1-31, depending on the month * @param {number} obj.ordinal - day of the year, 1-365 or 366 * @param {number} obj.weekYear - an ISO week year * @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year * @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday * @param {number} obj.hour - hour of the day, 0-23 * @param {number} obj.minute - minute of the hour, 0-59 * @param {number} obj.second - second of the minute, 0-59 * @param {number} obj.millisecond - millisecond of the second, 0-999 * @param {Object} opts - options for creating this DateTime * @param {string|Zone} [opts.zone='local'] - interpret the numbers in the context of a particular zone. Can take any value taken as the first argument to setZone() * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance * @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25' * @example DateTime.fromObject({ year: 1982 }).toISODate() //=> '1982-01-01' * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }) //~> today at 10:26:06 * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'utc' }), * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'local' }) * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'America/New_York' }) * @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13' * @return {DateTime} */ static fromObject(obj, opts = {}) { obj = obj || {}; const zoneToUse = normalizeZone(opts.zone, Settings.defaultZone); if (!zoneToUse.isValid) { return DateTime.invalid(unsupportedZone(zoneToUse)); } const tsNow = Settings.now(), offsetProvis = !isUndefined(opts.specificOffset) ? opts.specificOffset : zoneToUse.offset(tsNow), normalized = normalizeObject(obj, normalizeUnit), containsOrdinal = !isUndefined(normalized.ordinal), containsGregorYear = !isUndefined(normalized.year), containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized.weekYear || normalized.weekNumber, loc = Locale.fromObject(opts); if ((containsGregor || containsOrdinal) && definiteWeekDef) { throw new ConflictingSpecificationError( "Can't mix weekYear/weekNumber units with year/month/day or ordinals" ); } if (containsGregorMD && containsOrdinal) { throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day"); } const useWeekData = definiteWeekDef || normalized.weekday && !containsGregor; let units, defaultValues, objNow = tsToObj(tsNow, offsetProvis); if (useWeekData) { units = orderedWeekUnits; defaultValues = defaultWeekUnitValues; objNow = gregorianToWeek(objNow); } else if (containsOrdinal) { units = orderedOrdinalUnits; defaultValues = defaultOrdinalUnitValues; objNow = gregorianToOrdinal(objNow); } else { units = orderedUnits; defaultValues = defaultUnitValues; } let foundFirst = false; for (const u of units) { const v = normalized[u]; if (!isUndefined(v)) { foundFirst = true; } else if (foundFirst) { normalized[u] = defaultValues[u]; } else { normalized[u] = objNow[u]; } } const higherOrderInvalid = useWeekData ? hasInvalidWeekData(normalized) : containsOrdinal ? hasInvalidOrdinalData(normalized) : hasInvalidGregorianData(normalized), invalid = higherOrderInvalid || hasInvalidTimeData(normalized); if (invalid) { return DateTime.invalid(invalid); } const gregorian = useWeekData ? weekToGregorian(normalized) : containsOrdinal ? ordinalToGregorian(normalized) : normalized, [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse), inst = new DateTime({ ts: tsFinal, zone: zoneToUse, o: offsetFinal, loc }); if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) { return DateTime.invalid( "mismatched weekday", `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}` ); } return inst; } /** * Create a DateTime from an ISO 8601 string * @param {string} text - the ISO string * @param {Object} opts - options to affect the creation * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the time to this zone * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance * @param {string} [opts.outputCalendar] - the output calendar to set on the resulting DateTime instance * @param {string} [opts.numberingSystem] - the numbering system to set on the resulting DateTime instance * @example DateTime.fromISO('2016-05-25T09:08:34.123') * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00') * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00', {setZone: true}) * @example DateTime.fromISO('2016-05-25T09:08:34.123', {zone: 'utc'}) * @example DateTime.fromISO('2016-W05-4') * @return {DateTime} */ static fromISO(text, opts = {}) { const [vals, parsedZone] = parseISODate(text); return parseDataToDateTime(vals, parsedZone, opts, "ISO 8601", text); } /** * Create a DateTime from an RFC 2822 string * @param {string} text - the RFC 2822 string * @param {Object} opts - options to affect the creation * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since the offset is always specified in the string itself, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in. * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance * @example DateTime.fromRFC2822('25 Nov 2016 13:23:12 GMT') * @example DateTime.fromRFC2822('Fri, 25 Nov 2016 13:23:12 +0600') * @example DateTime.fromRFC2822('25 Nov 2016 13:23 Z') * @return {DateTime} */ static fromRFC2822(text, opts = {}) { const [vals, parsedZone] = parseRFC2822Date(text); return parseDataToDateTime(vals, parsedZone, opts, "RFC 2822", text); } /** * Create a DateTime from an HTTP header date * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 * @param {string} text - the HTTP header date * @param {Object} opts - options to affect the creation * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since HTTP dates are always in UTC, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in. * @param {boolean} [opts.setZone=false] - override the zone with the fixed-offset zone specified in the string. For HTTP dates, this is always UTC, so this option is equivalent to setting the `zone` option to 'utc', but this option is included for consistency with similar methods. * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance * @example DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT') * @example DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT') * @example DateTime.fromHTTP('Sun Nov 6 08:49:37 1994') * @return {DateTime} */ static fromHTTP(text, opts = {}) { const [vals, parsedZone] = parseHTTPDate(text); return parseDataToDateTime(vals, parsedZone, opts, "HTTP", opts); } /** * Create a DateTime from an input string and format string. * Defaults to en-US if no locale has been specified, regardless of the system's locale. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/parsing?id=table-of-tokens). * @param {string} text - the string to parse * @param {string} fmt - the format the string is expected to be in (see the link below for the formats) * @param {Object} opts - options to affect the creation * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance * @return {DateTime} */ static fromFormat(text, fmt, opts = {}) { if (isUndefined(text) || isUndefined(fmt)) { throw new InvalidArgumentError("fromFormat requires an input string and a format"); } const { locale = null, numberingSystem = null } = opts, localeToUse = Locale.fromOpts({ locale, numberingSystem, defaultToEN: true }), [vals, parsedZone, specificOffset, invalid] = parseFromTokens(localeToUse, text, fmt); if (invalid) { return DateTime.invalid(invalid); } else { return parseDataToDateTime(vals, parsedZone, opts, `format ${fmt}`, text, specificOffset); } } /** * @deprecated use fromFormat instead */ static fromString(text, fmt, opts = {}) { return DateTime.fromFormat(text, fmt, opts); } /** * Create a DateTime from a SQL date, time, or datetime * Defaults to en-US if no locale has been specified, regardless of the system's locale * @param {string} text - the string to parse * @param {Object} opts - options to affect the creation * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance * @example DateTime.fromSQL('2017-05-15') * @example DateTime.fromSQL('2017-05-15 09:12:34') * @example DateTime.fromSQL('2017-05-15 09:12:34.342') * @example DateTime.fromSQL('2017-05-15 09:12:34.342+06:00') * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles') * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles', { setZone: true }) * @example DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' }) * @example DateTime.fromSQL('09:12:34.342') * @return {DateTime} */ static fromSQL(text, opts = {}) { const [vals, parsedZone] = parseSQL(text); return parseDataToDateTime(vals, parsedZone, opts, "SQL", text); } /** * Create an invalid DateTime. * @param {string} reason - simple string of why this DateTime is invalid. Should not contain parameters or anything else data-dependent. * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information * @return {DateTime} */ static invalid(reason, explanation = null) { if (!reason) { throw new InvalidArgumentError("need to specify a reason the DateTime is invalid"); } const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation); if (Settings.throwOnInvalid) { throw new InvalidDateTimeError(invalid); } else { return new DateTime({ invalid }); } } /** * Check if an object is an instance of DateTime. Works across context boundaries * @param {object} o * @return {boolean} */ static isDateTime(o) { return o && o.isLuxonDateTime || false; } /** * Produce the format string for a set of options * @param formatOpts * @param localeOpts * @returns {string} */ static parseFormatForOpts(formatOpts, localeOpts = {}) { const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts)); return !tokenList ? null : tokenList.map((t2) => t2 ? t2.val : null).join(""); } /** * Produce the the fully expanded format token for the locale * Does NOT quote characters, so quoted tokens will not round trip correctly * @param fmt * @param localeOpts * @returns {string} */ static expandFormat(fmt, localeOpts = {}) { const expanded = expandMacroTokens(Formatter2.parseFormat(fmt), Locale.fromObject(localeOpts)); return expanded.map((t2) => t2.val).join(""); } // INFO /** * Get the value of unit. * @param {string} unit - a unit such as 'minute' or 'day' * @example DateTime.local(2017, 7, 4).get('month'); //=> 7 * @example DateTime.local(2017, 7, 4).get('day'); //=> 4 * @return {number} */ get(unit) { return this[unit]; } /** * Returns whether the DateTime is valid. Invalid DateTimes occur when: * * The DateTime was created from invalid calendar information, such as the 13th month or February 30 * * The DateTime was created by an operation on another invalid date * @type {boolean} */ get isValid() { return this.invalid === null; } /** * Returns an error code if this DateTime is invalid, or null if the DateTime is valid * @type {string} */ get invalidReason() { return this.invalid ? this.invalid.reason : null; } /** * Returns an explanation of why this DateTime became invalid, or null if the DateTime is valid * @type {string} */ get invalidExplanation() { return this.invalid ? this.invalid.explanation : null; } /** * Get the locale of a DateTime, such 'en-GB'. The locale is used when formatting the DateTime * * @type {string} */ get locale() { return this.isValid ? this.loc.locale : null; } /** * Get the numbering system of a DateTime, such 'beng'. The numbering system is used when formatting the DateTime * * @type {string} */ get numberingSystem() { return this.isValid ? this.loc.numberingSystem : null; } /** * Get the output calendar of a DateTime, such 'islamic'. The output calendar is used when formatting the DateTime * * @type {string} */ get outputCalendar() { return this.isValid ? this.loc.outputCalendar : null; } /** * Get the time zone associated with this DateTime. * @type {Zone} */ get zone() { return this._zone; } /** * Get the name of the time zone. * @type {string} */ get zoneName() { return this.isValid ? this.zone.name : null; } /** * Get the year * @example DateTime.local(2017, 5, 25).year //=> 2017 * @type {number} */ get year() { return this.isValid ? this.c.year : NaN; } /** * Get the quarter * @example DateTime.local(2017, 5, 25).quarter //=> 2 * @type {number} */ get quarter() { return this.isValid ? Math.ceil(this.c.month / 3) : NaN; } /** * Get the month (1-12). * @example DateTime.local(2017, 5, 25).month //=> 5 * @type {number} */ get month() { return this.isValid ? this.c.month : NaN; } /** * Get the day of the month (1-30ish). * @example DateTime.local(2017, 5, 25).day //=> 25 * @type {number} */ get day() { return this.isValid ? this.c.day : NaN; } /** * Get the hour of the day (0-23). * @example DateTime.local(2017, 5, 25, 9).hour //=> 9 * @type {number} */ get hour() { return this.isValid ? this.c.hour : NaN; } /** * Get the minute of the hour (0-59). * @example DateTime.local(2017, 5, 25, 9, 30).minute //=> 30 * @type {number} */ get minute() { return this.isValid ? this.c.minute : NaN; } /** * Get the second of the minute (0-59). * @example DateTime.local(2017, 5, 25, 9, 30, 52).second //=> 52 * @type {number} */ get second() { return this.isValid ? this.c.second : NaN; } /** * Get the millisecond of the second (0-999). * @example DateTime.local(2017, 5, 25, 9, 30, 52, 654).millisecond //=> 654 * @type {number} */ get millisecond() { return this.isValid ? this.c.millisecond : NaN; } /** * Get the week year * @see https://en.wikipedia.org/wiki/ISO_week_date * @example DateTime.local(2014, 12, 31).weekYear //=> 2015 * @type {number} */ get weekYear() { return this.isValid ? possiblyCachedWeekData(this).weekYear : NaN; } /** * Get the week number of the week year (1-52ish). * @see https://en.wikipedia.org/wiki/ISO_week_date * @example DateTime.local(2017, 5, 25).weekNumber //=> 21 * @type {number} */ get weekNumber() { return this.isValid ? possiblyCachedWeekData(this).weekNumber : NaN; } /** * Get the day of the week. * 1 is Monday and 7 is Sunday * @see https://en.wikipedia.org/wiki/ISO_week_date * @example DateTime.local(2014, 11, 31).weekday //=> 4 * @type {number} */ get weekday() { return this.isValid ? possiblyCachedWeekData(this).weekday : NaN; } /** * Get the ordinal (meaning the day of the year) * @example DateTime.local(2017, 5, 25).ordinal //=> 145 * @type {number|DateTime} */ get ordinal() { return this.isValid ? gregorianToOrdinal(this.c).ordinal : NaN; } /** * Get the human readable short month name, such as 'Oct'. * Defaults to the system's locale if no locale has been specified * @example DateTime.local(2017, 10, 30).monthShort //=> Oct * @type {string} */ get monthShort() { return this.isValid ? Info.months("short", { locObj: this.loc })[this.month - 1] : null; } /** * Get the human readable long month name, such as 'October'. * Defaults to the system's locale if no locale has been specified * @example DateTime.local(2017, 10, 30).monthLong //=> October * @type {string} */ get monthLong() { return this.isValid ? Info.months("long", { locObj: this.loc })[this.month - 1] : null; } /** * Get the human readable short weekday, such as 'Mon'. * Defaults to the system's locale if no locale has been specified * @example DateTime.local(2017, 10, 30).weekdayShort //=> Mon * @type {string} */ get weekdayShort() { return this.isValid ? Info.weekdays("short", { locObj: this.loc })[this.weekday - 1] : null; } /** * Get the human readable long weekday, such as 'Monday'. * Defaults to the system's locale if no locale has been specified * @example DateTime.local(2017, 10, 30).weekdayLong //=> Monday * @type {string} */ get weekdayLong() { return this.isValid ? Info.weekdays("long", { locObj: this.loc })[this.weekday - 1] : null; } /** * Get the UTC offset of this DateTime in minutes * @example DateTime.now().offset //=> -240 * @example DateTime.utc().offset //=> 0 * @type {number} */ get offset() { return this.isValid ? +this.o : NaN; } /** * Get the short human name for the zone's current offset, for example "EST" or "EDT". * Defaults to the system's locale if no locale has been specified * @type {string} */ get offsetNameShort() { if (this.isValid) { return this.zone.offsetName(this.ts, { format: "short", locale: this.locale }); } else { return null; } } /** * Get the long human name for the zone's current offset, for example "Eastern Standard Time" or "Eastern Daylight Time". * Defaults to the system's locale if no locale has been specified * @type {string} */ get offsetNameLong() { if (this.isValid) { return this.zone.offsetName(this.ts, { format: "long", locale: this.locale }); } else { return null; } } /** * Get whether this zone's offset ever changes, as in a DST. * @type {boolean} */ get isOffsetFixed() { return this.isValid ? this.zone.isUniversal : null; } /** * Get whether the DateTime is in a DST. * @type {boolean} */ get isInDST() { if (this.isOffsetFixed) { return false; } else { return this.offset > this.set({ month: 1, day: 1 }).offset || this.offset > this.set({ month: 5 }).offset; } } /** * Get those DateTimes which have the same local time as this DateTime, but a different offset from UTC * in this DateTime's zone. During DST changes local time can be ambiguous, for example * `2023-10-29T02:30:00` in `Europe/Berlin` can have offset `+01:00` or `+02:00`. * This method will return both possible DateTimes if this DateTime's local time is ambiguous. * @returns {DateTime[]} */ getPossibleOffsets() { if (!this.isValid || this.isOffsetFixed) { return [this]; } const dayMs = 864e5; const minuteMs = 6e4; const localTS = objToLocalTS(this.c); const oEarlier = this.zone.offset(localTS - dayMs); const oLater = this.zone.offset(localTS + dayMs); const o1 = this.zone.offset(localTS - oEarlier * minuteMs); const o2 = this.zone.offset(localTS - oLater * minuteMs); if (o1 === o2) { return [this]; } const ts1 = localTS - o1 * minuteMs; const ts2 = localTS - o2 * minuteMs; const c1 = tsToObj(ts1, o1); const c2 = tsToObj(ts2, o2); if (c1.hour === c2.hour && c1.minute === c2.minute && c1.second === c2.second && c1.millisecond === c2.millisecond) { return [clone(this, { ts: ts1 }), clone(this, { ts: ts2 })]; } return [this]; } /** * Returns true if this DateTime is in a leap year, false otherwise * @example DateTime.local(2016).isInLeapYear //=> true * @example DateTime.local(2013).isInLeapYear //=> false * @type {boolean} */ get isInLeapYear() { return isLeapYear(this.year); } /** * Returns the number of days in this DateTime's month * @example DateTime.local(2016, 2).daysInMonth //=> 29 * @example DateTime.local(2016, 3).daysInMonth //=> 31 * @type {number} */ get daysInMonth() { return daysInMonth(this.year, this.month); } /** * Returns the number of days in this DateTime's year * @example DateTime.local(2016).daysInYear //=> 366 * @example DateTime.local(2013).daysInYear //=> 365 * @type {number} */ get daysInYear() { return this.isValid ? daysInYear(this.year) : NaN; } /** * Returns the number of weeks in this DateTime's year * @see https://en.wikipedia.org/wiki/ISO_week_date * @example DateTime.local(2004).weeksInWeekYear //=> 53 * @example DateTime.local(2013).weeksInWeekYear //=> 52 * @type {number} */ get weeksInWeekYear() { return this.isValid ? weeksInWeekYear(this.weekYear) : NaN; } /** * Returns the resolved Intl options for this DateTime. * This is useful in understanding the behavior of formatting methods * @param {Object} opts - the same options as toLocaleString * @return {Object} */ resolvedLocaleOptions(opts = {}) { const { locale, numberingSystem, calendar } = Formatter2.create( this.loc.clone(opts), opts ).resolvedOptions(this); return { locale, numberingSystem, outputCalendar: calendar }; } // TRANSFORM /** * "Set" the DateTime's zone to UTC. Returns a newly-constructed DateTime. * * Equivalent to {@link DateTime#setZone}('utc') * @param {number} [offset=0] - optionally, an offset from UTC in minutes * @param {Object} [opts={}] - options to pass to `setZone()` * @return {DateTime} */ toUTC(offset2 = 0, opts = {}) { return this.setZone(FixedOffsetZone.instance(offset2), opts); } /** * "Set" the DateTime's zone to the host's local zone. Returns a newly-constructed DateTime. * * Equivalent to `setZone('local')` * @return {DateTime} */ toLocal() { return this.setZone(Settings.defaultZone); } /** * "Set" the DateTime's zone to specified zone. Returns a newly-constructed DateTime. * * By default, the setter keeps the underlying time the same (as in, the same timestamp), but the new instance will report different local times and consider DSTs when making computations, as with {@link DateTime#plus}. You may wish to use {@link DateTime#toLocal} and {@link DateTime#toUTC} which provide simple convenience wrappers for commonly used zones. * @param {string|Zone} [zone='local'] - a zone identifier. As a string, that can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3', or the strings 'local' or 'utc'. You may also supply an instance of a {@link DateTime#Zone} class. * @param {Object} opts - options * @param {boolean} [opts.keepLocalTime=false] - If true, adjust the underlying time so that the local time stays the same, but in the target zone. You should rarely need this. * @return {DateTime} */ setZone(zone, { keepLocalTime = false, keepCalendarTime = false } = {}) { zone = normalizeZone(zone, Settings.defaultZone); if (zone.equals(this.zone)) { return this; } else if (!zone.isValid) { return DateTime.invalid(unsupportedZone(zone)); } else { let newTS = this.ts; if (keepLocalTime || keepCalendarTime) { const offsetGuess = zone.offset(this.ts); const asObj = this.toObject(); [newTS] = objToTS(asObj, offsetGuess, zone); } return clone(this, { ts: newTS, zone }); } } /** * "Set" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime. * @param {Object} properties - the properties to set * @example DateTime.local(2017, 5, 25).reconfigure({ locale: 'en-GB' }) * @return {DateTime} */ reconfigure({ locale, numberingSystem, outputCalendar } = {}) { const loc = this.loc.clone({ locale, numberingSystem, outputCalendar }); return clone(this, { loc }); } /** * "Set" the locale. Returns a newly-constructed DateTime. * Just a convenient alias for reconfigure({ locale }) * @example DateTime.local(2017, 5, 25).setLocale('en-GB') * @return {DateTime} */ setLocale(locale) { return this.reconfigure({ locale }); } /** * "Set" the values of specified units. Returns a newly-constructed DateTime. * You can only set units with this method; for "setting" metadata, see {@link DateTime#reconfigure} and {@link DateTime#setZone}. * @param {Object} values - a mapping of units to numbers * @example dt.set({ year: 2017 }) * @example dt.set({ hour: 8, minute: 30 }) * @example dt.set({ weekday: 5 }) * @example dt.set({ year: 2005, ordinal: 234 }) * @return {DateTime} */ set(values) { if (!this.isValid) return this; const normalized = normalizeObject(values, normalizeUnit), settingWeekStuff = !isUndefined(normalized.weekYear) || !isUndefined(normalized.weekNumber) || !isUndefined(normalized.weekday), containsOrdinal = !isUndefined(normalized.ordinal), containsGregorYear = !isUndefined(normalized.year), containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized.weekYear || normalized.weekNumber; if ((containsGregor || containsOrdinal) && definiteWeekDef) { throw new ConflictingSpecificationError( "Can't mix weekYear/weekNumber units with year/month/day or ordinals" ); } if (containsGregorMD && containsOrdinal) { throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day"); } let mixed; if (settingWeekStuff) { mixed = weekToGregorian({ ...gregorianToWeek(this.c), ...normalized }); } else if (!isUndefined(normalized.ordinal)) { mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized }); } else { mixed = { ...this.toObject(), ...normalized }; if (isUndefined(normalized.day)) { mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day); } } const [ts, o] = objToTS(mixed, this.o, this.zone); return clone(this, { ts, o }); } /** * Add a period of time to this DateTime and return the resulting DateTime * * Adding hours, minutes, seconds, or milliseconds increases the timestamp by the right number of milliseconds. Adding days, months, or years shifts the calendar, accounting for DSTs and leap years along the way. Thus, `dt.plus({ hours: 24 })` may result in a different time than `dt.plus({ days: 1 })` if there's a DST shift in between. * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() * @example DateTime.now().plus(123) //~> in 123 milliseconds * @example DateTime.now().plus({ minutes: 15 }) //~> in 15 minutes * @example DateTime.now().plus({ days: 1 }) //~> this time tomorrow * @example DateTime.now().plus({ days: -1 }) //~> this time yesterday * @example DateTime.now().plus({ hours: 3, minutes: 13 }) //~> in 3 hr, 13 min * @example DateTime.now().plus(Duration.fromObject({ hours: 3, minutes: 13 })) //~> in 3 hr, 13 min * @return {DateTime} */ plus(duration) { if (!this.isValid) return this; const dur = Duration.fromDurationLike(duration); return clone(this, adjustTime(this, dur)); } /** * Subtract a period of time to this DateTime and return the resulting DateTime * See {@link DateTime#plus} * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject() @return {DateTime} */ minus(duration) { if (!this.isValid) return this; const dur = Duration.fromDurationLike(duration).negate(); return clone(this, adjustTime(this, dur)); } /** * "Set" this DateTime to the beginning of a unit of time. * @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'. * @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01' * @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01' * @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays * @example DateTime.local(2014, 3, 3, 5, 30).startOf('day').toISOTime(); //=> '00:00.000-05:00' * @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00' * @return {DateTime} */ startOf(unit) { if (!this.isValid) return this; const o = {}, normalizedUnit = Duration.normalizeUnit(unit); switch (normalizedUnit) { case "years": o.month = 1; case "quarters": case "months": o.day = 1; case "weeks": case "days": o.hour = 0; case "hours": o.minute = 0; case "minutes": o.second = 0; case "seconds": o.millisecond = 0; break; } if (normalizedUnit === "weeks") { o.weekday = 1; } if (normalizedUnit === "quarters") { const q = Math.ceil(this.month / 3); o.month = (q - 1) * 3 + 1; } return this.set(o); } /** * "Set" this DateTime to the end (meaning the last millisecond) of a unit of time * @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'. * @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00' * @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00' * @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays * @example DateTime.local(2014, 3, 3, 5, 30).endOf('day').toISO(); //=> '2014-03-03T23:59:59.999-05:00' * @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00' * @return {DateTime} */ endOf(unit) { return this.isValid ? this.plus({ [unit]: 1 }).startOf(unit).minus(1) : this; } // OUTPUT /** * Returns a string representation of this DateTime formatted according to the specified format string. * **You may not want this.** See {@link DateTime#toLocaleString} for a more flexible formatting tool. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/formatting?id=table-of-tokens). * Defaults to en-US if no locale has been specified, regardless of the system's locale. * @param {string} fmt - the format string * @param {Object} opts - opts to override the configuration options on this DateTime * @example DateTime.now().toFormat('yyyy LLL dd') //=> '2017 Apr 22' * @example DateTime.now().setLocale('fr').toFormat('yyyy LLL dd') //=> '2017 avr. 22' * @example DateTime.now().toFormat('yyyy LLL dd', { locale: "fr" }) //=> '2017 avr. 22' * @example DateTime.now().toFormat("HH 'hours and' mm 'minutes'") //=> '20 hours and 55 minutes' * @return {string} */ toFormat(fmt, opts = {}) { return this.isValid ? Formatter2.create(this.loc.redefaultToEN(opts)).formatDateTimeFromString(this, fmt) : INVALID; } /** * Returns a localized string representing this date. Accepts the same options as the Intl.DateTimeFormat constructor and any presets defined by Luxon, such as `DateTime.DATE_FULL` or `DateTime.TIME_SIMPLE`. * The exact behavior of this method is browser-specific, but in general it will return an appropriate representation * of the DateTime in the assigned locale. * Defaults to the system's locale if no locale has been specified * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat * @param formatOpts {Object} - Intl.DateTimeFormat constructor options and configuration options * @param {Object} opts - opts to override the configuration options on this DateTime * @example DateTime.now().toLocaleString(); //=> 4/20/2017 * @example DateTime.now().setLocale('en-gb').toLocaleString(); //=> '20/04/2017' * @example DateTime.now().toLocaleString(DateTime.DATE_FULL); //=> 'April 20, 2017' * @example DateTime.now().toLocaleString(DateTime.DATE_FULL, { locale: 'fr' }); //=> '28 août 2022' * @example DateTime.now().toLocaleString(DateTime.TIME_SIMPLE); //=> '11:32 AM' * @example DateTime.now().toLocaleString(DateTime.DATETIME_SHORT); //=> '4/20/2017, 11:32 AM' * @example DateTime.now().toLocaleString({ weekday: 'long', month: 'long', day: '2-digit' }); //=> 'Thursday, April 20' * @example DateTime.now().toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> 'Thu, Apr 20, 11:27 AM' * @example DateTime.now().toLocaleString({ hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }); //=> '11:32' * @return {string} */ toLocaleString(formatOpts = DATE_SHORT, opts = {}) { return this.isValid ? Formatter2.create(this.loc.clone(opts), formatOpts).formatDateTime(this) : INVALID; } /** * Returns an array of format "parts", meaning individual tokens along with metadata. This is allows callers to post-process individual sections of the formatted output. * Defaults to the system's locale if no locale has been specified * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts * @param opts {Object} - Intl.DateTimeFormat constructor options, same as `toLocaleString`. * @example DateTime.now().toLocaleParts(); //=> [ * //=> { type: 'day', value: '25' }, * //=> { type: 'literal', value: '/' }, * //=> { type: 'month', value: '05' }, * //=> { type: 'literal', value: '/' }, * //=> { type: 'year', value: '1982' } * //=> ] */ toLocaleParts(opts = {}) { return this.isValid ? Formatter2.create(this.loc.clone(opts), opts).formatDateTimeParts(this) : []; } /** * Returns an ISO 8601-compliant string representation of this DateTime * @param {Object} opts - options * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' * @param {boolean} [opts.extendedZone=false] - add the time zone format extension * @param {string} [opts.format='extended'] - choose between the basic and extended format * @example DateTime.utc(1983, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z' * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00' * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335' * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400' * @return {string} */ toISO({ format = "extended", suppressSeconds = false, suppressMilliseconds = false, includeOffset = true, extendedZone = false } = {}) { if (!this.isValid) { return null; } const ext = format === "extended"; let c = toISODate(this, ext); c += "T"; c += toISOTime(this, ext, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone); return c; } /** * Returns an ISO 8601-compliant string representation of this DateTime's date component * @param {Object} opts - options * @param {string} [opts.format='extended'] - choose between the basic and extended format * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25' * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525' * @return {string} */ toISODate({ format = "extended" } = {}) { if (!this.isValid) { return null; } return toISODate(this, format === "extended"); } /** * Returns an ISO 8601-compliant string representation of this DateTime's week date * @example DateTime.utc(1982, 5, 25).toISOWeekDate() //=> '1982-W21-2' * @return {string} */ toISOWeekDate() { return toTechFormat(this, "kkkk-'W'WW-c"); } /** * Returns an ISO 8601-compliant string representation of this DateTime's time component * @param {Object} opts - options * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0 * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0 * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' * @param {boolean} [opts.extendedZone=true] - add the time zone format extension * @param {boolean} [opts.includePrefix=false] - include the `T` prefix * @param {string} [opts.format='extended'] - choose between the basic and extended format * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z' * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z' * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z' * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z' * @return {string} */ toISOTime({ suppressMilliseconds = false, suppressSeconds = false, includeOffset = true, includePrefix = false, extendedZone = false, format = "extended" } = {}) { if (!this.isValid) { return null; } let c = includePrefix ? "T" : ""; return c + toISOTime( this, format === "extended", suppressSeconds, suppressMilliseconds, includeOffset, extendedZone ); } /** * Returns an RFC 2822-compatible string representation of this DateTime * @example DateTime.utc(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 +0000' * @example DateTime.local(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 -0400' * @return {string} */ toRFC2822() { return toTechFormat(this, "EEE, dd LLL yyyy HH:mm:ss ZZZ", false); } /** * Returns a string representation of this DateTime appropriate for use in HTTP headers. The output is always expressed in GMT. * Specifically, the string conforms to RFC 1123. * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 * @example DateTime.utc(2014, 7, 13).toHTTP() //=> 'Sun, 13 Jul 2014 00:00:00 GMT' * @example DateTime.utc(2014, 7, 13, 19).toHTTP() //=> 'Sun, 13 Jul 2014 19:00:00 GMT' * @return {string} */ toHTTP() { return toTechFormat(this.toUTC(), "EEE, dd LLL yyyy HH:mm:ss 'GMT'"); } /** * Returns a string representation of this DateTime appropriate for use in SQL Date * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13' * @return {string} */ toSQLDate() { if (!this.isValid) { return null; } return toISODate(this, true); } /** * Returns a string representation of this DateTime appropriate for use in SQL Time * @param {Object} opts - options * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset. * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00' * @example DateTime.utc().toSQL() //=> '05:15:16.345' * @example DateTime.now().toSQL() //=> '05:15:16.345 -04:00' * @example DateTime.now().toSQL({ includeOffset: false }) //=> '05:15:16.345' * @example DateTime.now().toSQL({ includeZone: false }) //=> '05:15:16.345 America/New_York' * @return {string} */ toSQLTime({ includeOffset = true, includeZone = false, includeOffsetSpace = true } = {}) { let fmt = "HH:mm:ss.SSS"; if (includeZone || includeOffset) { if (includeOffsetSpace) { fmt += " "; } if (includeZone) { fmt += "z"; } else if (includeOffset) { fmt += "ZZ"; } } return toTechFormat(this, fmt, true); } /** * Returns a string representation of this DateTime appropriate for use in SQL DateTime * @param {Object} opts - options * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset. * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00' * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00' * @example DateTime.utc(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 Z' * @example DateTime.local(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 -04:00' * @example DateTime.local(2014, 7, 13).toSQL({ includeOffset: false }) //=> '2014-07-13 00:00:00.000' * @example DateTime.local(2014, 7, 13).toSQL({ includeZone: true }) //=> '2014-07-13 00:00:00.000 America/New_York' * @return {string} */ toSQL(opts = {}) { if (!this.isValid) { return null; } return `${this.toSQLDate()} ${this.toSQLTime(opts)}`; } /** * Returns a string representation of this DateTime appropriate for debugging * @return {string} */ toString() { return this.isValid ? this.toISO() : INVALID; } /** * Returns the epoch milliseconds of this DateTime. Alias of {@link DateTime#toMillis} * @return {number} */ valueOf() { return this.toMillis(); } /** * Returns the epoch milliseconds of this DateTime. * @return {number} */ toMillis() { return this.isValid ? this.ts : NaN; } /** * Returns the epoch seconds of this DateTime. * @return {number} */ toSeconds() { return this.isValid ? this.ts / 1e3 : NaN; } /** * Returns the epoch seconds (as a whole number) of this DateTime. * @return {number} */ toUnixInteger() { return this.isValid ? Math.floor(this.ts / 1e3) : NaN; } /** * Returns an ISO 8601 representation of this DateTime appropriate for use in JSON. * @return {string} */ toJSON() { return this.toISO(); } /** * Returns a BSON serializable equivalent to this DateTime. * @return {Date} */ toBSON() { return this.toJSDate(); } /** * Returns a JavaScript object with this DateTime's year, month, day, and so on. * @param opts - options for generating the object * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output * @example DateTime.now().toObject() //=> { year: 2017, month: 4, day: 22, hour: 20, minute: 49, second: 42, millisecond: 268 } * @return {Object} */ toObject(opts = {}) { if (!this.isValid) return {}; const base = { ...this.c }; if (opts.includeConfig) { base.outputCalendar = this.outputCalendar; base.numberingSystem = this.loc.numberingSystem; base.locale = this.loc.locale; } return base; } /** * Returns a JavaScript Date equivalent to this DateTime. * @return {Date} */ toJSDate() { return new Date(this.isValid ? this.ts : NaN); } // COMPARE /** * Return the difference between two DateTimes as a Duration. * @param {DateTime} otherDateTime - the DateTime to compare this one to * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration. * @param {Object} opts - options that affect the creation of the Duration * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use * @example * var i1 = DateTime.fromISO('1982-05-25T09:45'), * i2 = DateTime.fromISO('1983-10-14T10:30'); * i2.diff(i1).toObject() //=> { milliseconds: 43807500000 } * i2.diff(i1, 'hours').toObject() //=> { hours: 12168.75 } * i2.diff(i1, ['months', 'days']).toObject() //=> { months: 16, days: 19.03125 } * i2.diff(i1, ['months', 'days', 'hours']).toObject() //=> { months: 16, days: 19, hours: 0.75 } * @return {Duration} */ diff(otherDateTime, unit = "milliseconds", opts = {}) { if (!this.isValid || !otherDateTime.isValid) { return Duration.invalid("created by diffing an invalid DateTime"); } const durOpts = { locale: this.locale, numberingSystem: this.numberingSystem, ...opts }; const units = maybeArray(unit).map(Duration.normalizeUnit), otherIsLater = otherDateTime.valueOf() > this.valueOf(), earlier = otherIsLater ? this : otherDateTime, later = otherIsLater ? otherDateTime : this, diffed = diff(earlier, later, units, durOpts); return otherIsLater ? diffed.negate() : diffed; } /** * Return the difference between this DateTime and right now. * See {@link DateTime#diff} * @param {string|string[]} [unit=['milliseconds']] - the unit or units units (such as 'hours' or 'days') to include in the duration * @param {Object} opts - options that affect the creation of the Duration * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use * @return {Duration} */ diffNow(unit = "milliseconds", opts = {}) { return this.diff(DateTime.now(), unit, opts); } /** * Return an Interval spanning between this DateTime and another DateTime * @param {DateTime} otherDateTime - the other end point of the Interval * @return {Interval} */ until(otherDateTime) { return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this; } /** * Return whether this DateTime is in the same unit of time as another DateTime. * Higher-order units must also be identical for this function to return `true`. * Note that time zones are **ignored** in this comparison, which compares the **local** calendar time. Use {@link DateTime#setZone} to convert one of the dates if needed. * @param {DateTime} otherDateTime - the other DateTime * @param {string} unit - the unit of time to check sameness on * @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day * @return {boolean} */ hasSame(otherDateTime, unit) { if (!this.isValid) return false; const inputMs = otherDateTime.valueOf(); const adjustedToZone = this.setZone(otherDateTime.zone, { keepLocalTime: true }); return adjustedToZone.startOf(unit) <= inputMs && inputMs <= adjustedToZone.endOf(unit); } /** * Equality check * Two DateTimes are equal if and only if they represent the same millisecond, have the same zone and location, and are both valid. * To compare just the millisecond values, use `+dt1 === +dt2`. * @param {DateTime} other - the other DateTime * @return {boolean} */ equals(other) { return this.isValid && other.isValid && this.valueOf() === other.valueOf() && this.zone.equals(other.zone) && this.loc.equals(other.loc); } /** * Returns a string representation of a this time relative to now, such as "in two days". Can only internationalize if your * platform supports Intl.RelativeTimeFormat. Rounds down by default. * @param {Object} options - options that affect the output * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now. * @param {string} [options.style="long"] - the style of units, must be "long", "short", or "narrow" * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of "years", "quarters", "months", "weeks", "days", "hours", "minutes", or "seconds" * @param {boolean} [options.round=true] - whether to round the numbers in the output. * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding. * @param {string} options.locale - override the locale of this DateTime * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this * @example DateTime.now().plus({ days: 1 }).toRelative() //=> "in 1 day" * @example DateTime.now().setLocale("es").toRelative({ days: 1 }) //=> "dentro de 1 día" * @example DateTime.now().plus({ days: 1 }).toRelative({ locale: "fr" }) //=> "dans 23 heures" * @example DateTime.now().minus({ days: 2 }).toRelative() //=> "2 days ago" * @example DateTime.now().minus({ days: 2 }).toRelative({ unit: "hours" }) //=> "48 hours ago" * @example DateTime.now().minus({ hours: 36 }).toRelative({ round: false }) //=> "1.5 days ago" */ toRelative(options = {}) { if (!this.isValid) return null; const base = options.base || DateTime.fromObject({}, { zone: this.zone }), padding = options.padding ? this < base ? -options.padding : options.padding : 0; let units = ["years", "months", "days", "hours", "minutes", "seconds"]; let unit = options.unit; if (Array.isArray(options.unit)) { units = options.unit; unit = void 0; } return diffRelative(base, this.plus(padding), { ...options, numeric: "always", units, unit }); } /** * Returns a string representation of this date relative to today, such as "yesterday" or "next month". * Only internationalizes on platforms that supports Intl.RelativeTimeFormat. * @param {Object} options - options that affect the output * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now. * @param {string} options.locale - override the locale of this DateTime * @param {string} options.unit - use a specific unit; if omitted, the method will pick the unit. Use one of "years", "quarters", "months", "weeks", or "days" * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar() //=> "tomorrow" * @example DateTime.now().setLocale("es").plus({ days: 1 }).toRelative() //=> ""mañana" * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar({ locale: "fr" }) //=> "demain" * @example DateTime.now().minus({ days: 2 }).toRelativeCalendar() //=> "2 days ago" */ toRelativeCalendar(options = {}) { if (!this.isValid) return null; return diffRelative(options.base || DateTime.fromObject({}, { zone: this.zone }), this, { ...options, numeric: "auto", units: ["years", "months", "days"], calendary: true }); } /** * Return the min of several date times * @param {...DateTime} dateTimes - the DateTimes from which to choose the minimum * @return {DateTime} the min DateTime, or undefined if called with no argument */ static min(...dateTimes) { if (!dateTimes.every(DateTime.isDateTime)) { throw new InvalidArgumentError("min requires all arguments be DateTimes"); } return bestBy(dateTimes, (i) => i.valueOf(), Math.min); } /** * Return the max of several date times * @param {...DateTime} dateTimes - the DateTimes from which to choose the maximum * @return {DateTime} the max DateTime, or undefined if called with no argument */ static max(...dateTimes) { if (!dateTimes.every(DateTime.isDateTime)) { throw new InvalidArgumentError("max requires all arguments be DateTimes"); } return bestBy(dateTimes, (i) => i.valueOf(), Math.max); } // MISC /** * Explain how a string would be parsed by fromFormat() * @param {string} text - the string to parse * @param {string} fmt - the format the string is expected to be in (see description) * @param {Object} options - options taken by fromFormat() * @return {Object} */ static fromFormatExplain(text, fmt, options = {}) { const { locale = null, numberingSystem = null } = options, localeToUse = Locale.fromOpts({ locale, numberingSystem, defaultToEN: true }); return explainFromTokens(localeToUse, text, fmt); } /** * @deprecated use fromFormatExplain instead */ static fromStringExplain(text, fmt, options = {}) { return DateTime.fromFormatExplain(text, fmt, options); } // FORMAT PRESETS /** * {@link DateTime#toLocaleString} format like 10/14/1983 * @type {Object} */ static get DATE_SHORT() { return DATE_SHORT; } /** * {@link DateTime#toLocaleString} format like 'Oct 14, 1983' * @type {Object} */ static get DATE_MED() { return DATE_MED; } /** * {@link DateTime#toLocaleString} format like 'Fri, Oct 14, 1983' * @type {Object} */ static get DATE_MED_WITH_WEEKDAY() { return DATE_MED_WITH_WEEKDAY; } /** * {@link DateTime#toLocaleString} format like 'October 14, 1983' * @type {Object} */ static get DATE_FULL() { return DATE_FULL; } /** * {@link DateTime#toLocaleString} format like 'Tuesday, October 14, 1983' * @type {Object} */ static get DATE_HUGE() { return DATE_HUGE; } /** * {@link DateTime#toLocaleString} format like '09:30 AM'. Only 12-hour if the locale is. * @type {Object} */ static get TIME_SIMPLE() { return TIME_SIMPLE; } /** * {@link DateTime#toLocaleString} format like '09:30:23 AM'. Only 12-hour if the locale is. * @type {Object} */ static get TIME_WITH_SECONDS() { return TIME_WITH_SECONDS; } /** * {@link DateTime#toLocaleString} format like '09:30:23 AM EDT'. Only 12-hour if the locale is. * @type {Object} */ static get TIME_WITH_SHORT_OFFSET() { return TIME_WITH_SHORT_OFFSET; } /** * {@link DateTime#toLocaleString} format like '09:30:23 AM Eastern Daylight Time'. Only 12-hour if the locale is. * @type {Object} */ static get TIME_WITH_LONG_OFFSET() { return TIME_WITH_LONG_OFFSET; } /** * {@link DateTime#toLocaleString} format like '09:30', always 24-hour. * @type {Object} */ static get TIME_24_SIMPLE() { return TIME_24_SIMPLE; } /** * {@link DateTime#toLocaleString} format like '09:30:23', always 24-hour. * @type {Object} */ static get TIME_24_WITH_SECONDS() { return TIME_24_WITH_SECONDS; } /** * {@link DateTime#toLocaleString} format like '09:30:23 EDT', always 24-hour. * @type {Object} */ static get TIME_24_WITH_SHORT_OFFSET() { return TIME_24_WITH_SHORT_OFFSET; } /** * {@link DateTime#toLocaleString} format like '09:30:23 Eastern Daylight Time', always 24-hour. * @type {Object} */ static get TIME_24_WITH_LONG_OFFSET() { return TIME_24_WITH_LONG_OFFSET; } /** * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_SHORT() { return DATETIME_SHORT; } /** * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_SHORT_WITH_SECONDS() { return DATETIME_SHORT_WITH_SECONDS; } /** * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30 AM'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_MED() { return DATETIME_MED; } /** * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30:33 AM'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_MED_WITH_SECONDS() { return DATETIME_MED_WITH_SECONDS; } /** * {@link DateTime#toLocaleString} format like 'Fri, 14 Oct 1983, 9:30 AM'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_MED_WITH_WEEKDAY() { return DATETIME_MED_WITH_WEEKDAY; } /** * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30 AM EDT'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_FULL() { return DATETIME_FULL; } /** * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30:33 AM EDT'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_FULL_WITH_SECONDS() { return DATETIME_FULL_WITH_SECONDS; } /** * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30 AM Eastern Daylight Time'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_HUGE() { return DATETIME_HUGE; } /** * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time'. Only 12-hour if the locale is. * @type {Object} */ static get DATETIME_HUGE_WITH_SECONDS() { return DATETIME_HUGE_WITH_SECONDS; } }; function friendlyDateTime(dateTimeish) { if (DateTime.isDateTime(dateTimeish)) { return dateTimeish; } else if (dateTimeish && dateTimeish.valueOf && isNumber(dateTimeish.valueOf())) { return DateTime.fromJSDate(dateTimeish); } else if (dateTimeish && typeof dateTimeish === "object") { return DateTime.fromObject(dateTimeish); } else { throw new InvalidArgumentError( `Unknown datetime argument: ${dateTimeish}, of type ${typeof dateTimeish}` ); } } var DEFAULT_QUERY_SETTINGS = { renderNullAs: "\\-", taskCompletionTracking: false, taskCompletionUseEmojiShorthand: false, taskCompletionText: "completion", taskCompletionDateFormat: "yyyy-MM-dd", recursiveSubTaskCompletion: false, warnOnEmptyResult: true, refreshEnabled: true, refreshInterval: 2500, defaultDateFormat: "MMMM dd, yyyy", defaultDateTimeFormat: "h:mm a - MMMM dd, yyyy", maxRecursiveRenderDepth: 4, tableIdColumnName: "File", tableGroupColumnName: "Group", showResultCount: true }; var DEFAULT_EXPORT_SETTINGS = { allowHtml: true }; ({ ...DEFAULT_QUERY_SETTINGS, ...DEFAULT_EXPORT_SETTINGS, ...{ inlineQueryPrefix: "=", inlineJsQueryPrefix: "$=", inlineQueriesInCodeblocks: true, enableInlineDataview: true, enableDataviewJs: false, enableInlineDataviewJs: false, prettyRenderInlineFields: true, prettyRenderInlineFieldsInLivePreview: true, dataviewJsKeyword: "dataviewjs" } }); var Success = class { constructor(value) { __publicField(this, "value"); __publicField(this, "successful"); this.value = value; this.successful = true; } map(f) { return new Success(f(this.value)); } flatMap(f) { return f(this.value); } mapErr(f) { return this; } bimap(succ, _fail) { return this.map(succ); } orElse(_value) { return this.value; } cast() { return this; } orElseThrow(_message) { return this.value; } }; var Failure = class { constructor(error) { __publicField(this, "error"); __publicField(this, "successful"); this.error = error; this.successful = false; } map(_f) { return this; } flatMap(_f) { return this; } mapErr(f) { return new Failure(f(this.error)); } bimap(_succ, fail) { return this.mapErr(fail); } orElse(value) { return value; } cast() { return this; } orElseThrow(message) { if (message) throw new Error(message(this.error)); else throw new Error("" + this.error); } }; var Result; (function(Result2) { function success(value) { return new Success(value); } Result2.success = success; function failure(error) { return new Failure(error); } Result2.failure = failure; function flatMap2(first, second, f) { if (first.successful) { if (second.successful) return f(first.value, second.value); else return failure(second.error); } else { return failure(first.error); } } Result2.flatMap2 = flatMap2; function map2(first, second, f) { return flatMap2(first, second, (a, b) => success(f(a, b))); } Result2.map2 = map2; })(Result || (Result = {})); var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {}; var parsimmon_umd_min = { exports: {} }; parsimmon_umd_min.exports; (function(module3, exports2) { !function(n2, t2) { module3.exports = t2(); }("undefined" != typeof self ? self : commonjsGlobal, function() { return function(n2) { var t2 = {}; function r(e) { if (t2[e]) return t2[e].exports; var u = t2[e] = { i: e, l: false, exports: {} }; return n2[e].call(u.exports, u, u.exports, r), u.l = true, u.exports; } return r.m = n2, r.c = t2, r.d = function(n3, t3, e) { r.o(n3, t3) || Object.defineProperty(n3, t3, { configurable: false, enumerable: true, get: e }); }, r.r = function(n3) { Object.defineProperty(n3, "__esModule", { value: true }); }, r.n = function(n3) { var t3 = n3 && n3.__esModule ? function() { return n3.default; } : function() { return n3; }; return r.d(t3, "a", t3), t3; }, r.o = function(n3, t3) { return Object.prototype.hasOwnProperty.call(n3, t3); }, r.p = "", r(r.s = 0); }([function(n2, t2, r) { function e(n3) { if (!(this instanceof e)) return new e(n3); this._ = n3; } var u = e.prototype; function o(n3, t3) { for (var r2 = 0; r2 < n3; r2++) t3(r2); } function i(n3, t3, r2) { return function(n4, t4) { o(t4.length, function(r3) { n4(t4[r3], r3, t4); }); }(function(r3, e2, u2) { t3 = n3(t3, r3, e2, u2); }, r2), t3; } function a(n3, t3) { return i(function(t4, r2, e2, u2) { return t4.concat([n3(r2, e2, u2)]); }, [], t3); } function f(n3, t3) { var r2 = { v: 0, buf: t3 }; return o(n3, function() { var n4; r2 = { v: r2.v << 1 | (n4 = r2.buf, n4[0] >> 7), buf: function(n5) { var t4 = i(function(n6, t5, r3, e2) { return n6.concat(r3 === e2.length - 1 ? Buffer.from([t5, 0]).readUInt16BE(0) : e2.readUInt16BE(r3)); }, [], n5); return Buffer.from(a(function(n6) { return (n6 << 1 & 65535) >> 8; }, t4)); }(r2.buf) }; }), r2; } function c() { return "undefined" != typeof Buffer; } function s2() { if (!c()) throw new Error("Buffer global does not exist; please use webpack if you need to parse Buffers in the browser."); } function l2(n3) { s2(); var t3 = i(function(n4, t4) { return n4 + t4; }, 0, n3); if (t3 % 8 != 0) throw new Error("The bits [" + n3.join(", ") + "] add up to " + t3 + " which is not an even number of bytes; the total should be divisible by 8"); var r2, u2 = t3 / 8, o2 = (r2 = function(n4) { return n4 > 48; }, i(function(n4, t4) { return n4 || (r2(t4) ? t4 : n4); }, null, n3)); if (o2) throw new Error(o2 + " bit range requested exceeds 48 bit (6 byte) Number max."); return new e(function(t4, r3) { var e2 = u2 + r3; return e2 > t4.length ? x(r3, u2.toString() + " bytes") : b(e2, i(function(n4, t5) { var r4 = f(t5, n4.buf); return { coll: n4.coll.concat(r4.v), buf: r4.buf }; }, { coll: [], buf: t4.slice(r3, e2) }, n3).coll); }); } function h(n3, t3) { return new e(function(r2, e2) { return s2(), e2 + t3 > r2.length ? x(e2, t3 + " bytes for " + n3) : b(e2 + t3, r2.slice(e2, e2 + t3)); }); } function p(n3, t3) { if ("number" != typeof (r2 = t3) || Math.floor(r2) !== r2 || t3 < 0 || t3 > 6) throw new Error(n3 + " requires integer length in range [0, 6]."); var r2; } function d(n3) { return p("uintBE", n3), h("uintBE(" + n3 + ")", n3).map(function(t3) { return t3.readUIntBE(0, n3); }); } function v(n3) { return p("uintLE", n3), h("uintLE(" + n3 + ")", n3).map(function(t3) { return t3.readUIntLE(0, n3); }); } function g(n3) { return p("intBE", n3), h("intBE(" + n3 + ")", n3).map(function(t3) { return t3.readIntBE(0, n3); }); } function m(n3) { return p("intLE", n3), h("intLE(" + n3 + ")", n3).map(function(t3) { return t3.readIntLE(0, n3); }); } function y(n3) { return n3 instanceof e; } function E(n3) { return "[object Array]" === {}.toString.call(n3); } function w(n3) { return c() && Buffer.isBuffer(n3); } function b(n3, t3) { return { status: true, index: n3, value: t3, furthest: -1, expected: [] }; } function x(n3, t3) { return E(t3) || (t3 = [t3]), { status: false, index: -1, value: null, furthest: n3, expected: t3 }; } function B(n3, t3) { if (!t3) return n3; if (n3.furthest > t3.furthest) return n3; var r2 = n3.furthest === t3.furthest ? function(n4, t4) { if (function() { if (void 0 !== e._supportsSet) return e._supportsSet; var n5 = "undefined" != typeof Set; return e._supportsSet = n5, n5; }() && Array.from) { for (var r3 = new Set(n4), u2 = 0; u2 < t4.length; u2++) r3.add(t4[u2]); var o2 = Array.from(r3); return o2.sort(), o2; } for (var i2 = {}, a2 = 0; a2 < n4.length; a2++) i2[n4[a2]] = true; for (var f2 = 0; f2 < t4.length; f2++) i2[t4[f2]] = true; var c2 = []; for (var s3 in i2) ({}).hasOwnProperty.call(i2, s3) && c2.push(s3); return c2.sort(), c2; }(n3.expected, t3.expected) : t3.expected; return { status: n3.status, index: n3.index, value: n3.value, furthest: t3.furthest, expected: r2 }; } var j = {}; function S(n3, t3) { if (w(n3)) return { offset: t3, line: -1, column: -1 }; n3 in j || (j[n3] = {}); for (var r2 = j[n3], e2 = 0, u2 = 0, o2 = 0, i2 = t3; i2 >= 0; ) { if (i2 in r2) { e2 = r2[i2].line, 0 === o2 && (o2 = r2[i2].lineStart); break; } ("\n" === n3.charAt(i2) || "\r" === n3.charAt(i2) && "\n" !== n3.charAt(i2 + 1)) && (u2++, 0 === o2 && (o2 = i2 + 1)), i2--; } var a2 = e2 + u2, f2 = t3 - o2; return r2[t3] = { line: a2, lineStart: o2 }, { offset: t3, line: a2 + 1, column: f2 + 1 }; } function _(n3) { if (!y(n3)) throw new Error("not a parser: " + n3); } function L(n3, t3) { return "string" == typeof n3 ? n3.charAt(t3) : n3[t3]; } function O(n3) { if ("number" != typeof n3) throw new Error("not a number: " + n3); } function k(n3) { if ("function" != typeof n3) throw new Error("not a function: " + n3); } function P(n3) { if ("string" != typeof n3) throw new Error("not a string: " + n3); } var q = 2, A = 3, I = 8, F = 5 * I, M = 4 * I, z = " "; function R(n3, t3) { return new Array(t3 + 1).join(n3); } function U(n3, t3, r2) { var e2 = t3 - n3.length; return e2 <= 0 ? n3 : R(r2, e2) + n3; } function W(n3, t3, r2, e2) { return { from: n3 - t3 > 0 ? n3 - t3 : 0, to: n3 + r2 > e2 ? e2 : n3 + r2 }; } function D(n3, t3) { var r2, e2, u2, o2, f2, c2 = t3.index, s3 = c2.offset, l3 = 1; if (s3 === n3.length) return "Got the end of the input"; if (w(n3)) { var h2 = s3 - s3 % I, p2 = s3 - h2, d2 = W(h2, F, M + I, n3.length), v2 = a(function(n4) { return a(function(n5) { return U(n5.toString(16), 2, "0"); }, n4); }, function(n4, t4) { var r3 = n4.length, e3 = [], u3 = 0; if (r3 <= t4) return [n4.slice()]; for (var o3 = 0; o3 < r3; o3++) e3[u3] || e3.push([]), e3[u3].push(n4[o3]), (o3 + 1) % t4 == 0 && u3++; return e3; }(n3.slice(d2.from, d2.to).toJSON().data, I)); o2 = function(n4) { return 0 === n4.from && 1 === n4.to ? { from: n4.from, to: n4.to } : { from: n4.from / I, to: Math.floor(n4.to / I) }; }(d2), e2 = h2 / I, r2 = 3 * p2, p2 >= 4 && (r2 += 1), l3 = 2, u2 = a(function(n4) { return n4.length <= 4 ? n4.join(" ") : n4.slice(0, 4).join(" ") + " " + n4.slice(4).join(" "); }, v2), (f2 = (8 * (o2.to > 0 ? o2.to - 1 : o2.to)).toString(16).length) < 2 && (f2 = 2); } else { var g2 = n3.split(/\r\n|[\n\r\u2028\u2029]/); r2 = c2.column - 1, e2 = c2.line - 1, o2 = W(e2, q, A, g2.length), u2 = g2.slice(o2.from, o2.to), f2 = o2.to.toString().length; } var m2 = e2 - o2.from; return w(n3) && (f2 = (8 * (o2.to > 0 ? o2.to - 1 : o2.to)).toString(16).length) < 2 && (f2 = 2), i(function(t4, e3, u3) { var i2, a2 = u3 === m2, c3 = a2 ? "> " : z; return i2 = w(n3) ? U((8 * (o2.from + u3)).toString(16), f2, "0") : U((o2.from + u3 + 1).toString(), f2, " "), [].concat(t4, [c3 + i2 + " | " + e3], a2 ? [z + R(" ", f2) + " | " + U("", r2, " ") + R("^", l3)] : []); }, [], u2).join("\n"); } function N(n3, t3) { return ["\n", "-- PARSING FAILED " + R("-", 50), "\n\n", D(n3, t3), "\n\n", (r2 = t3.expected, 1 === r2.length ? "Expected:\n\n" + r2[0] : "Expected one of the following: \n\n" + r2.join(", ")), "\n"].join(""); var r2; } function G(n3) { return void 0 !== n3.flags ? n3.flags : [n3.global ? "g" : "", n3.ignoreCase ? "i" : "", n3.multiline ? "m" : "", n3.unicode ? "u" : "", n3.sticky ? "y" : ""].join(""); } function C() { for (var n3 = [].slice.call(arguments), t3 = n3.length, r2 = 0; r2 < t3; r2 += 1) _(n3[r2]); return e(function(r3, e2) { for (var u2, o2 = new Array(t3), i2 = 0; i2 < t3; i2 += 1) { if (!(u2 = B(n3[i2]._(r3, e2), u2)).status) return u2; o2[i2] = u2.value, e2 = u2.index; } return B(b(e2, o2), u2); }); } function J() { var n3 = [].slice.call(arguments); if (0 === n3.length) throw new Error("seqMap needs at least one argument"); var t3 = n3.pop(); return k(t3), C.apply(null, n3).map(function(n4) { return t3.apply(null, n4); }); } function T() { var n3 = [].slice.call(arguments), t3 = n3.length; if (0 === t3) return Y("zero alternates"); for (var r2 = 0; r2 < t3; r2 += 1) _(n3[r2]); return e(function(t4, r3) { for (var e2, u2 = 0; u2 < n3.length; u2 += 1) if ((e2 = B(n3[u2]._(t4, r3), e2)).status) return e2; return e2; }); } function V(n3, t3) { return H(n3, t3).or(X([])); } function H(n3, t3) { return _(n3), _(t3), J(n3, t3.then(n3).many(), function(n4, t4) { return [n4].concat(t4); }); } function K(n3) { P(n3); var t3 = "'" + n3 + "'"; return e(function(r2, e2) { var u2 = e2 + n3.length, o2 = r2.slice(e2, u2); return o2 === n3 ? b(u2, o2) : x(e2, t3); }); } function Q(n3, t3) { !function(n4) { if (!(n4 instanceof RegExp)) throw new Error("not a regexp: " + n4); for (var t4 = G(n4), r3 = 0; r3 < t4.length; r3++) { var e2 = t4.charAt(r3); if ("i" !== e2 && "m" !== e2 && "u" !== e2 && "s" !== e2) throw new Error('unsupported regexp flag "' + e2 + '": ' + n4); } }(n3), arguments.length >= 2 ? O(t3) : t3 = 0; var r2 = function(n4) { return RegExp("^(?:" + n4.source + ")", G(n4)); }(n3), u2 = "" + n3; return e(function(n4, e2) { var o2 = r2.exec(n4.slice(e2)); if (o2) { if (0 <= t3 && t3 <= o2.length) { var i2 = o2[0], a2 = o2[t3]; return b(e2 + i2.length, a2); } return x(e2, "valid match group (0 to " + o2.length + ") in " + u2); } return x(e2, u2); }); } function X(n3) { return e(function(t3, r2) { return b(r2, n3); }); } function Y(n3) { return e(function(t3, r2) { return x(r2, n3); }); } function Z(n3) { if (y(n3)) return e(function(t3, r2) { var e2 = n3._(t3, r2); return e2.index = r2, e2.value = "", e2; }); if ("string" == typeof n3) return Z(K(n3)); if (n3 instanceof RegExp) return Z(Q(n3)); throw new Error("not a string, regexp, or parser: " + n3); } function $(n3) { return _(n3), e(function(t3, r2) { var e2 = n3._(t3, r2), u2 = t3.slice(r2, e2.index); return e2.status ? x(r2, 'not "' + u2 + '"') : b(r2, null); }); } function nn(n3) { return k(n3), e(function(t3, r2) { var e2 = L(t3, r2); return r2 < t3.length && n3(e2) ? b(r2 + 1, e2) : x(r2, "a character/byte matching " + n3); }); } function tn(n3, t3) { arguments.length < 2 && (t3 = n3, n3 = void 0); var r2 = e(function(n4, e2) { return r2._ = t3()._, r2._(n4, e2); }); return n3 ? r2.desc(n3) : r2; } function rn() { return Y("fantasy-land/empty"); } u.parse = function(n3) { if ("string" != typeof n3 && !w(n3)) throw new Error(".parse must be called with a string or Buffer as its argument"); var t3, r2 = this.skip(an)._(n3, 0); return t3 = r2.status ? { status: true, value: r2.value } : { status: false, index: S(n3, r2.furthest), expected: r2.expected }, delete j[n3], t3; }, u.tryParse = function(n3) { var t3 = this.parse(n3); if (t3.status) return t3.value; var r2 = N(n3, t3), e2 = new Error(r2); throw e2.type = "ParsimmonError", e2.result = t3, e2; }, u.assert = function(n3, t3) { return this.chain(function(r2) { return n3(r2) ? X(r2) : Y(t3); }); }, u.or = function(n3) { return T(this, n3); }, u.trim = function(n3) { return this.wrap(n3, n3); }, u.wrap = function(n3, t3) { return J(n3, this, t3, function(n4, t4) { return t4; }); }, u.thru = function(n3) { return n3(this); }, u.then = function(n3) { return _(n3), C(this, n3).map(function(n4) { return n4[1]; }); }, u.many = function() { var n3 = this; return e(function(t3, r2) { for (var e2 = [], u2 = void 0; ; ) { if (!(u2 = B(n3._(t3, r2), u2)).status) return B(b(r2, e2), u2); if (r2 === u2.index) throw new Error("infinite loop detected in .many() parser --- calling .many() on a parser which can accept zero characters is usually the cause"); r2 = u2.index, e2.push(u2.value); } }); }, u.tieWith = function(n3) { return P(n3), this.map(function(t3) { if (function(n4) { if (!E(n4)) throw new Error("not an array: " + n4); }(t3), t3.length) { P(t3[0]); for (var r2 = t3[0], e2 = 1; e2 < t3.length; e2++) P(t3[e2]), r2 += n3 + t3[e2]; return r2; } return ""; }); }, u.tie = function() { return this.tieWith(""); }, u.times = function(n3, t3) { var r2 = this; return arguments.length < 2 && (t3 = n3), O(n3), O(t3), e(function(e2, u2) { for (var o2 = [], i2 = void 0, a2 = void 0, f2 = 0; f2 < n3; f2 += 1) { if (a2 = B(i2 = r2._(e2, u2), a2), !i2.status) return a2; u2 = i2.index, o2.push(i2.value); } for (; f2 < t3 && (a2 = B(i2 = r2._(e2, u2), a2), i2.status); f2 += 1) u2 = i2.index, o2.push(i2.value); return B(b(u2, o2), a2); }); }, u.result = function(n3) { return this.map(function() { return n3; }); }, u.atMost = function(n3) { return this.times(0, n3); }, u.atLeast = function(n3) { return J(this.times(n3), this.many(), function(n4, t3) { return n4.concat(t3); }); }, u.map = function(n3) { k(n3); var t3 = this; return e(function(r2, e2) { var u2 = t3._(r2, e2); return u2.status ? B(b(u2.index, n3(u2.value)), u2) : u2; }); }, u.contramap = function(n3) { k(n3); var t3 = this; return e(function(r2, e2) { var u2 = t3.parse(n3(r2.slice(e2))); return u2.status ? b(e2 + r2.length, u2.value) : u2; }); }, u.promap = function(n3, t3) { return k(n3), k(t3), this.contramap(n3).map(t3); }, u.skip = function(n3) { return C(this, n3).map(function(n4) { return n4[0]; }); }, u.mark = function() { return J(en, this, en, function(n3, t3, r2) { return { start: n3, value: t3, end: r2 }; }); }, u.node = function(n3) { return J(en, this, en, function(t3, r2, e2) { return { name: n3, value: r2, start: t3, end: e2 }; }); }, u.sepBy = function(n3) { return V(this, n3); }, u.sepBy1 = function(n3) { return H(this, n3); }, u.lookahead = function(n3) { return this.skip(Z(n3)); }, u.notFollowedBy = function(n3) { return this.skip($(n3)); }, u.desc = function(n3) { E(n3) || (n3 = [n3]); var t3 = this; return e(function(r2, e2) { var u2 = t3._(r2, e2); return u2.status || (u2.expected = n3), u2; }); }, u.fallback = function(n3) { return this.or(X(n3)); }, u.ap = function(n3) { return J(n3, this, function(n4, t3) { return n4(t3); }); }, u.chain = function(n3) { var t3 = this; return e(function(r2, e2) { var u2 = t3._(r2, e2); return u2.status ? B(n3(u2.value)._(r2, u2.index), u2) : u2; }); }, u.concat = u.or, u.empty = rn, u.of = X, u["fantasy-land/ap"] = u.ap, u["fantasy-land/chain"] = u.chain, u["fantasy-land/concat"] = u.concat, u["fantasy-land/empty"] = u.empty, u["fantasy-land/of"] = u.of, u["fantasy-land/map"] = u.map; var en = e(function(n3, t3) { return b(t3, S(n3, t3)); }), un = e(function(n3, t3) { return t3 >= n3.length ? x(t3, "any character/byte") : b(t3 + 1, L(n3, t3)); }), on = e(function(n3, t3) { return b(n3.length, n3.slice(t3)); }), an = e(function(n3, t3) { return t3 < n3.length ? x(t3, "EOF") : b(t3, null); }), fn = Q(/[0-9]/).desc("a digit"), cn = Q(/[0-9]*/).desc("optional digits"), sn = Q(/[a-z]/i).desc("a letter"), ln = Q(/[a-z]*/i).desc("optional letters"), hn = Q(/\s*/).desc("optional whitespace"), pn = Q(/\s+/).desc("whitespace"), dn = K("\r"), vn = K("\n"), gn = K("\r\n"), mn = T(gn, vn, dn).desc("newline"), yn = T(mn, an); e.all = on, e.alt = T, e.any = un, e.cr = dn, e.createLanguage = function(n3) { var t3 = {}; for (var r2 in n3) ({}).hasOwnProperty.call(n3, r2) && function(r3) { t3[r3] = tn(function() { return n3[r3](t3); }); }(r2); return t3; }, e.crlf = gn, e.custom = function(n3) { return e(n3(b, x)); }, e.digit = fn, e.digits = cn, e.empty = rn, e.end = yn, e.eof = an, e.fail = Y, e.formatError = N, e.index = en, e.isParser = y, e.lazy = tn, e.letter = sn, e.letters = ln, e.lf = vn, e.lookahead = Z, e.makeFailure = x, e.makeSuccess = b, e.newline = mn, e.noneOf = function(n3) { return nn(function(t3) { return n3.indexOf(t3) < 0; }).desc("none of '" + n3 + "'"); }, e.notFollowedBy = $, e.of = X, e.oneOf = function(n3) { for (var t3 = n3.split(""), r2 = 0; r2 < t3.length; r2++) t3[r2] = "'" + t3[r2] + "'"; return nn(function(t4) { return n3.indexOf(t4) >= 0; }).desc(t3); }, e.optWhitespace = hn, e.Parser = e, e.range = function(n3, t3) { return nn(function(r2) { return n3 <= r2 && r2 <= t3; }).desc(n3 + "-" + t3); }, e.regex = Q, e.regexp = Q, e.sepBy = V, e.sepBy1 = H, e.seq = C, e.seqMap = J, e.seqObj = function() { for (var n3, t3 = {}, r2 = 0, u2 = (n3 = arguments, Array.prototype.slice.call(n3)), o2 = u2.length, i2 = 0; i2 < o2; i2 += 1) { var a2 = u2[i2]; if (!y(a2)) { if (E(a2) && 2 === a2.length && "string" == typeof a2[0] && y(a2[1])) { var f2 = a2[0]; if (Object.prototype.hasOwnProperty.call(t3, f2)) throw new Error("seqObj: duplicate key " + f2); t3[f2] = true, r2++; continue; } throw new Error("seqObj arguments must be parsers or [string, parser] array pairs."); } } if (0 === r2) throw new Error("seqObj expects at least one named parser, found zero"); return e(function(n4, t4) { for (var r3, e2 = {}, i3 = 0; i3 < o2; i3 += 1) { var a3, f3; if (E(u2[i3]) ? (a3 = u2[i3][0], f3 = u2[i3][1]) : (a3 = null, f3 = u2[i3]), !(r3 = B(f3._(n4, t4), r3)).status) return r3; a3 && (e2[a3] = r3.value), t4 = r3.index; } return B(b(t4, e2), r3); }); }, e.string = K, e.succeed = X, e.takeWhile = function(n3) { return k(n3), e(function(t3, r2) { for (var e2 = r2; e2 < t3.length && n3(L(t3, e2)); ) e2++; return b(e2, t3.slice(r2, e2)); }); }, e.test = nn, e.whitespace = pn, e["fantasy-land/empty"] = rn, e["fantasy-land/of"] = X, e.Binary = { bitSeq: l2, bitSeqObj: function(n3) { s2(); var t3 = {}, r2 = 0, e2 = a(function(n4) { if (E(n4)) { var e3 = n4; if (2 !== e3.length) throw new Error("[" + e3.join(", ") + "] should be length 2, got length " + e3.length); if (P(e3[0]), O(e3[1]), Object.prototype.hasOwnProperty.call(t3, e3[0])) throw new Error("duplicate key in bitSeqObj: " + e3[0]); return t3[e3[0]] = true, r2++, e3; } return O(n4), [null, n4]; }, n3); if (r2 < 1) throw new Error("bitSeqObj expects at least one named pair, got [" + n3.join(", ") + "]"); var u2 = a(function(n4) { return n4[0]; }, e2); return l2(a(function(n4) { return n4[1]; }, e2)).map(function(n4) { return i(function(n5, t4) { return null !== t4[0] && (n5[t4[0]] = t4[1]), n5; }, {}, a(function(t4, r3) { return [t4, n4[r3]]; }, u2)); }); }, byte: function(n3) { if (s2(), O(n3), n3 > 255) throw new Error("Value specified to byte constructor (" + n3 + "=0x" + n3.toString(16) + ") is larger in value than a single byte."); var t3 = (n3 > 15 ? "0x" : "0x0") + n3.toString(16); return e(function(r2, e2) { var u2 = L(r2, e2); return u2 === n3 ? b(e2 + 1, u2) : x(e2, t3); }); }, buffer: function(n3) { return h("buffer", n3).map(function(n4) { return Buffer.from(n4); }); }, encodedString: function(n3, t3) { return h("string", t3).map(function(t4) { return t4.toString(n3); }); }, uintBE: d, uint8BE: d(1), uint16BE: d(2), uint32BE: d(4), uintLE: v, uint8LE: v(1), uint16LE: v(2), uint32LE: v(4), intBE: g, int8BE: g(1), int16BE: g(2), int32BE: g(4), intLE: m, int8LE: m(1), int16LE: m(2), int32LE: m(4), floatBE: h("floatBE", 4).map(function(n3) { return n3.readFloatBE(0); }), floatLE: h("floatLE", 4).map(function(n3) { return n3.readFloatLE(0); }), doubleBE: h("doubleBE", 8).map(function(n3) { return n3.readDoubleBE(0); }), doubleLE: h("doubleLE", 8).map(function(n3) { return n3.readDoubleLE(0); }) }, n2.exports = e; }]); }); })(parsimmon_umd_min, parsimmon_umd_min.exports); var parsimmon_umd_minExports = parsimmon_umd_min.exports; var emojiRegex = () => { return /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26F9(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC3\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC08\uDC26](?:\u200D\u2B1B)?|[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC2\uDECE-\uDEDB\uDEE0-\uDEE8]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g; }; function normalizeDuration(dur) { if (dur === void 0 || dur === null) return dur; return dur.shiftToAll().normalize(); } function getFileTitle(path) { if (path.includes("/")) path = path.substring(path.lastIndexOf("/") + 1); if (path.endsWith(".md")) path = path.substring(0, path.length - 3); return path; } parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regex(new RegExp(emojiRegex(), "")), parsimmon_umd_minExports.regex(/[0-9\p{Letter}_-]+/u).map((str) => str.toLocaleLowerCase()), parsimmon_umd_minExports.whitespace.map((_) => "-"), parsimmon_umd_minExports.any.map((_) => "")).many().map((result) => result.join("")); var HEADER_CANONICALIZER = parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regex(new RegExp(emojiRegex(), "")), parsimmon_umd_minExports.regex(/[0-9\p{Letter}_-]+/u), parsimmon_umd_minExports.whitespace.map((_) => " "), parsimmon_umd_minExports.any.map((_) => " ")).many().map((result) => { return result.join("").split(/\s+/).join(" ").trim(); }); function normalizeHeaderForLink(header) { return HEADER_CANONICALIZER.tryParse(header); } function renderMinimalDuration(dur) { dur = normalizeDuration(dur); dur = Duration.fromObject(Object.fromEntries(Object.entries(dur.toObject()).filter(([, quantity]) => quantity != 0))); return dur.toHuman(); } var Values; (function(Values2) { function toString(field, setting = DEFAULT_QUERY_SETTINGS, recursive = false) { let wrapped = wrapValue(field); if (!wrapped) return setting.renderNullAs; switch (wrapped.type) { case "null": return setting.renderNullAs; case "string": return wrapped.value; case "number": case "boolean": return "" + wrapped.value; case "html": return wrapped.value.outerHTML; case "widget": return wrapped.value.markdown(); case "link": return wrapped.value.markdown(); case "function": return ""; case "array": let result = ""; if (recursive) result += "["; result += wrapped.value.map((f) => toString(f, setting, true)).join(", "); if (recursive) result += "]"; return result; case "object": return "{ " + Object.entries(wrapped.value).map((e) => e[0] + ": " + toString(e[1], setting, true)).join(", ") + " }"; case "date": if (wrapped.value.second == 0 && wrapped.value.hour == 0 && wrapped.value.minute == 0) { return wrapped.value.toFormat(setting.defaultDateFormat); } return wrapped.value.toFormat(setting.defaultDateTimeFormat); case "duration": return renderMinimalDuration(wrapped.value); } } Values2.toString = toString; function wrapValue(val) { if (isNull(val)) return { type: "null", value: val }; else if (isNumber2(val)) return { type: "number", value: val }; else if (isString3(val)) return { type: "string", value: val }; else if (isBoolean(val)) return { type: "boolean", value: val }; else if (isDuration(val)) return { type: "duration", value: val }; else if (isDate2(val)) return { type: "date", value: val }; else if (isWidget(val)) return { type: "widget", value: val }; else if (isArray(val)) return { type: "array", value: val }; else if (isLink(val)) return { type: "link", value: val }; else if (isFunction(val)) return { type: "function", value: val }; else if (isHtml(val)) return { type: "html", value: val }; else if (isObject(val)) return { type: "object", value: val }; else return void 0; } Values2.wrapValue = wrapValue; function mapLeaves(val, func) { if (isObject(val)) { let result = {}; for (let [key, value] of Object.entries(val)) result[key] = mapLeaves(value, func); return result; } else if (isArray(val)) { let result = []; for (let value of val) result.push(mapLeaves(value, func)); return result; } else { return func(val); } } Values2.mapLeaves = mapLeaves; function compareValue(val1, val2, linkNormalizer) { var _a, _b; if (val1 === void 0) val1 = null; if (val2 === void 0) val2 = null; if (val1 === null && val2 === null) return 0; else if (val1 === null) return -1; else if (val2 === null) return 1; let wrap1 = wrapValue(val1); let wrap2 = wrapValue(val2); if (wrap1 === void 0 && wrap2 === void 0) return 0; else if (wrap1 === void 0) return -1; else if (wrap2 === void 0) return 1; if (wrap1.type != wrap2.type) return wrap1.type.localeCompare(wrap2.type); if (wrap1.value === wrap2.value) return 0; switch (wrap1.type) { case "string": return wrap1.value.localeCompare(wrap2.value); case "number": if (wrap1.value < wrap2.value) return -1; else if (wrap1.value == wrap2.value) return 0; return 1; case "null": return 0; case "boolean": if (wrap1.value == wrap2.value) return 0; else return wrap1.value ? 1 : -1; case "link": let link1 = wrap1.value; let link2 = wrap2.value; let normalize = linkNormalizer != null ? linkNormalizer : (x) => x; let pathCompare = normalize(link1.path).localeCompare(normalize(link2.path)); if (pathCompare != 0) return pathCompare; let typeCompare = link1.type.localeCompare(link2.type); if (typeCompare != 0) return typeCompare; if (link1.subpath && !link2.subpath) return 1; if (!link1.subpath && link2.subpath) return -1; if (!link1.subpath && !link2.subpath) return 0; return ((_a = link1.subpath) != null ? _a : "").localeCompare((_b = link2.subpath) != null ? _b : ""); case "date": return wrap1.value < wrap2.value ? -1 : wrap1.value.equals(wrap2.value) ? 0 : 1; case "duration": return wrap1.value < wrap2.value ? -1 : wrap1.value.equals(wrap2.value) ? 0 : 1; case "array": let f1 = wrap1.value; let f2 = wrap2.value; for (let index = 0; index < Math.min(f1.length, f2.length); index++) { let comp = compareValue(f1[index], f2[index]); if (comp != 0) return comp; } return f1.length - f2.length; case "object": let o1 = wrap1.value; let o2 = wrap2.value; let k1 = Array.from(Object.keys(o1)); let k2 = Array.from(Object.keys(o2)); k1.sort(); k2.sort(); let keyCompare = compareValue(k1, k2); if (keyCompare != 0) return keyCompare; for (let key of k1) { let comp = compareValue(o1[key], o2[key]); if (comp != 0) return comp; } return 0; case "widget": case "html": case "function": return 0; } } Values2.compareValue = compareValue; function typeOf(val) { var _a; return (_a = wrapValue(val)) == null ? void 0 : _a.type; } Values2.typeOf = typeOf; function isTruthy(field) { let wrapped = wrapValue(field); if (!wrapped) return false; switch (wrapped.type) { case "number": return wrapped.value != 0; case "string": return wrapped.value.length > 0; case "boolean": return wrapped.value; case "link": return !!wrapped.value.path; case "date": return wrapped.value.toMillis() != 0; case "duration": return wrapped.value.as("seconds") != 0; case "object": return Object.keys(wrapped.value).length > 0; case "array": return wrapped.value.length > 0; case "null": return false; case "html": case "widget": case "function": return true; } } Values2.isTruthy = isTruthy; function deepCopy(field) { if (field === null || field === void 0) return field; if (Values2.isArray(field)) { return [].concat(field.map((v) => deepCopy(v))); } else if (Values2.isObject(field)) { let result = {}; for (let [key, value] of Object.entries(field)) result[key] = deepCopy(value); return result; } else { return field; } } Values2.deepCopy = deepCopy; function isString3(val) { return typeof val == "string"; } Values2.isString = isString3; function isNumber2(val) { return typeof val == "number"; } Values2.isNumber = isNumber2; function isDate2(val) { return val instanceof DateTime; } Values2.isDate = isDate2; function isDuration(val) { return val instanceof Duration; } Values2.isDuration = isDuration; function isNull(val) { return val === null || val === void 0; } Values2.isNull = isNull; function isArray(val) { return Array.isArray(val); } Values2.isArray = isArray; function isBoolean(val) { return typeof val === "boolean"; } Values2.isBoolean = isBoolean; function isLink(val) { return val instanceof Link; } Values2.isLink = isLink; function isWidget(val) { return val instanceof Widget; } Values2.isWidget = isWidget; function isHtml(val) { if (typeof HTMLElement !== "undefined") { return val instanceof HTMLElement; } else { return false; } } Values2.isHtml = isHtml; function isObject(val) { return typeof val == "object" && !isHtml(val) && !isWidget(val) && !isArray(val) && !isDuration(val) && !isDate2(val) && !isLink(val) && val !== void 0 && !isNull(val); } Values2.isObject = isObject; function isFunction(val) { return typeof val == "function"; } Values2.isFunction = isFunction; })(Values || (Values = {})); var Groupings; (function(Groupings2) { function isElementGroup(entry) { return Values.isObject(entry) && Object.keys(entry).length == 2 && "key" in entry && "rows" in entry; } Groupings2.isElementGroup = isElementGroup; function isGrouping(entry) { for (let element of entry) if (!isElementGroup(element)) return false; return true; } Groupings2.isGrouping = isGrouping; function count(elements) { if (isGrouping(elements)) { let result = 0; for (let subgroup of elements) result += count(subgroup.rows); return result; } else { return elements.length; } } Groupings2.count = count; })(Groupings || (Groupings = {})); var Link = class { constructor(fields) { /** The file path this link points to. */ __publicField(this, "path"); /** The display name associated with the link. */ __publicField(this, "display"); /** The block ID or header this link points to within a file, if relevant. */ __publicField(this, "subpath"); /** Is this link an embedded link (!)? */ __publicField(this, "embed"); /** The type of this link, which determines what 'subpath' refers to, if anything. */ __publicField(this, "type"); Object.assign(this, fields); } /** Create a link to a specific file. */ static file(path, embed = false, display) { return new Link({ path, embed, display, subpath: void 0, type: "file" }); } static infer(linkpath, embed = false, display) { if (linkpath.includes("#^")) { let split = linkpath.split("#^"); return Link.block(split[0], split[1], embed, display); } else if (linkpath.includes("#")) { let split = linkpath.split("#"); return Link.header(split[0], split[1], embed, display); } else return Link.file(linkpath, embed, display); } /** Create a link to a specific file and header in that file. */ static header(path, header, embed, display) { return new Link({ path, embed, display, subpath: normalizeHeaderForLink(header), type: "header" }); } /** Create a link to a specific file and block in that file. */ static block(path, blockId, embed, display) { return new Link({ path, embed, display, subpath: blockId, type: "block" }); } static fromObject(object) { return new Link(object); } /** Checks for link equality (i.e., that the links are pointing to the same exact location). */ equals(other) { if (other == void 0 || other == null) return false; return this.path == other.path && this.type == other.type && this.subpath == other.subpath; } /** Convert this link to it's markdown representation. */ toString() { return this.markdown(); } /** Convert this link to a raw object which is serialization-friendly. */ toObject() { return { path: this.path, type: this.type, subpath: this.subpath, display: this.display, embed: this.embed }; } /** Update this link with a new path. */ //@ts-ignore; error appeared after updating Obsidian to 0.15.4; it also updated other packages but didn't say which withPath(path) { return new Link(Object.assign({}, this, { path })); } /** Return a new link which points to the same location but with a new display value. */ withDisplay(display) { return new Link(Object.assign({}, this, { display })); } /** Convert a file link into a link to a specific header. */ withHeader(header) { return Link.header(this.path, header, this.embed, this.display); } /** Convert any link into a link to its file. */ toFile() { return Link.file(this.path, this.embed, this.display); } /** Convert this link into an embedded link. */ toEmbed() { if (this.embed) { return this; } else { let link = new Link(this); link.embed = true; return link; } } /** Convert this link into a non-embedded link. */ fromEmbed() { if (!this.embed) { return this; } else { let link = new Link(this); link.embed = false; return link; } } /** Convert this link to markdown so it can be rendered. */ markdown() { let result = (this.embed ? "!" : "") + "[[" + this.obsidianLink(); if (this.display) { result += "|" + this.display; } else { result += "|" + getFileTitle(this.path); if (this.type == "header" || this.type == "block") result += " > " + this.subpath; } result += "]]"; return result; } /** Convert the inner part of the link to something that Obsidian can open / understand. */ obsidianLink() { var _a, _b; const escaped = this.path.replaceAll("|", "\\|"); if (this.type == "header") return escaped + "#" + ((_a = this.subpath) == null ? void 0 : _a.replaceAll("|", "\\|")); if (this.type == "block") return escaped + "#^" + ((_b = this.subpath) == null ? void 0 : _b.replaceAll("|", "\\|")); else return escaped; } /** The stripped name of the file this link points to. */ fileName() { return getFileTitle(this.path).replace(".md", ""); } }; var Widget = class { constructor($widget) { __publicField(this, "$widget"); this.$widget = $widget; } }; var ListPairWidget = class extends Widget { constructor(key, value) { super("dataview:list-pair"); __publicField(this, "key"); __publicField(this, "value"); this.key = key; this.value = value; } markdown() { return `${Values.toString(this.key)}: ${Values.toString(this.value)}`; } }; var ExternalLinkWidget = class extends Widget { constructor(url, display) { super("dataview:external-link"); __publicField(this, "url"); __publicField(this, "display"); this.url = url; this.display = display; } markdown() { var _a; return `[${(_a = this.display) != null ? _a : this.url}](${this.url})`; } }; var Widgets; (function(Widgets2) { function listPair(key, value) { return new ListPairWidget(key, value); } Widgets2.listPair = listPair; function externalLink(url, display) { return new ExternalLinkWidget(url, display); } Widgets2.externalLink = externalLink; function isListPair(widget) { return widget.$widget === "dataview:list-pair"; } Widgets2.isListPair = isListPair; function isExternalLink(widget) { return widget.$widget === "dataview:external-link"; } Widgets2.isExternalLink = isExternalLink; function isBuiltin(widget) { return isListPair(widget) || isExternalLink(widget); } Widgets2.isBuiltin = isBuiltin; })(Widgets || (Widgets = {})); var Fields; (function(Fields2) { function variable(name) { return { type: "variable", name }; } Fields2.variable = variable; function literal(value) { return { type: "literal", value }; } Fields2.literal = literal; function binaryOp(left, op, right) { return { type: "binaryop", left, op, right }; } Fields2.binaryOp = binaryOp; function index(obj, index2) { return { type: "index", object: obj, index: index2 }; } Fields2.index = index; function indexVariable(name) { let parts = name.split("."); let result = Fields2.variable(parts[0]); for (let index2 = 1; index2 < parts.length; index2++) { result = Fields2.index(result, Fields2.literal(parts[index2])); } return result; } Fields2.indexVariable = indexVariable; function lambda(args, value) { return { type: "lambda", arguments: args, value }; } Fields2.lambda = lambda; function func(func2, args) { return { type: "function", func: func2, arguments: args }; } Fields2.func = func; function list(values) { return { type: "list", values }; } Fields2.list = list; function object(values) { return { type: "object", values }; } Fields2.object = object; function negate(child) { return { type: "negated", child }; } Fields2.negate = negate; function isCompareOp(op) { return op == "<=" || op == "<" || op == ">" || op == ">=" || op == "!=" || op == "="; } Fields2.isCompareOp = isCompareOp; Fields2.NULL = Fields2.literal(null); })(Fields || (Fields = {})); var Sources; (function(Sources2) { function tag(tag2) { return { type: "tag", tag: tag2 }; } Sources2.tag = tag; function csv(path) { return { type: "csv", path }; } Sources2.csv = csv; function folder(prefix) { return { type: "folder", folder: prefix }; } Sources2.folder = folder; function link(file, incoming) { return { type: "link", file, direction: incoming ? "incoming" : "outgoing" }; } Sources2.link = link; function binaryOp(left, op, right) { return { type: "binaryop", left, op, right }; } Sources2.binaryOp = binaryOp; function and(left, right) { return { type: "binaryop", left, op: "&", right }; } Sources2.and = and; function or(left, right) { return { type: "binaryop", left, op: "|", right }; } Sources2.or = or; function negate(child) { return { type: "negate", child }; } Sources2.negate = negate; function empty() { return { type: "empty" }; } Sources2.empty = empty; })(Sources || (Sources = {})); var EMOJI_REGEX = new RegExp(emojiRegex(), ""); var DURATION_TYPES = { year: Duration.fromObject({ years: 1 }), years: Duration.fromObject({ years: 1 }), yr: Duration.fromObject({ years: 1 }), yrs: Duration.fromObject({ years: 1 }), month: Duration.fromObject({ months: 1 }), months: Duration.fromObject({ months: 1 }), mo: Duration.fromObject({ months: 1 }), mos: Duration.fromObject({ months: 1 }), week: Duration.fromObject({ weeks: 1 }), weeks: Duration.fromObject({ weeks: 1 }), wk: Duration.fromObject({ weeks: 1 }), wks: Duration.fromObject({ weeks: 1 }), w: Duration.fromObject({ weeks: 1 }), day: Duration.fromObject({ days: 1 }), days: Duration.fromObject({ days: 1 }), d: Duration.fromObject({ days: 1 }), hour: Duration.fromObject({ hours: 1 }), hours: Duration.fromObject({ hours: 1 }), hr: Duration.fromObject({ hours: 1 }), hrs: Duration.fromObject({ hours: 1 }), h: Duration.fromObject({ hours: 1 }), minute: Duration.fromObject({ minutes: 1 }), minutes: Duration.fromObject({ minutes: 1 }), min: Duration.fromObject({ minutes: 1 }), mins: Duration.fromObject({ minutes: 1 }), m: Duration.fromObject({ minutes: 1 }), second: Duration.fromObject({ seconds: 1 }), seconds: Duration.fromObject({ seconds: 1 }), sec: Duration.fromObject({ seconds: 1 }), secs: Duration.fromObject({ seconds: 1 }), s: Duration.fromObject({ seconds: 1 }) }; var DATE_SHORTHANDS = { now: () => DateTime.local(), today: () => DateTime.local().startOf("day"), yesterday: () => DateTime.local().startOf("day").minus(Duration.fromObject({ days: 1 })), tomorrow: () => DateTime.local().startOf("day").plus(Duration.fromObject({ days: 1 })), sow: () => DateTime.local().startOf("week"), "start-of-week": () => DateTime.local().startOf("week"), eow: () => DateTime.local().endOf("week"), "end-of-week": () => DateTime.local().endOf("week"), soy: () => DateTime.local().startOf("year"), "start-of-year": () => DateTime.local().startOf("year"), eoy: () => DateTime.local().endOf("year"), "end-of-year": () => DateTime.local().endOf("year"), som: () => DateTime.local().startOf("month"), "start-of-month": () => DateTime.local().startOf("month"), eom: () => DateTime.local().endOf("month"), "end-of-month": () => DateTime.local().endOf("month") }; var KEYWORDS = ["FROM", "WHERE", "LIMIT", "GROUP", "FLATTEN"]; function splitOnUnescapedPipe(link) { let pipe = -1; while ((pipe = link.indexOf("|", pipe + 1)) >= 0) { if (pipe > 0 && link[pipe - 1] == "\\") continue; return [link.substring(0, pipe).replace(/\\\|/g, "|"), link.substring(pipe + 1)]; } return [link.replace(/\\\|/g, "|"), void 0]; } function parseInnerLink(rawlink) { let [link, display] = splitOnUnescapedPipe(rawlink); return Link.infer(link, false, display); } function createBinaryParser(child, sep, combine) { return parsimmon_umd_minExports.seqMap(child, parsimmon_umd_minExports.seq(parsimmon_umd_minExports.optWhitespace, sep, parsimmon_umd_minExports.optWhitespace, child).many(), (first, rest) => { if (rest.length == 0) return first; let node = combine(first, rest[0][1], rest[0][3]); for (let index = 1; index < rest.length; index++) { node = combine(node, rest[index][1], rest[index][3]); } return node; }); } function chainOpt(base, ...funcs) { return parsimmon_umd_minExports.custom((success, failure) => { return (input, i) => { let result = base._(input, i); if (!result.status) return result; for (let func of funcs) { let next = func(result.value)._(input, result.index); if (!next.status) return result; result = next; } return result; }; }); } var EXPRESSION = parsimmon_umd_minExports.createLanguage({ // A floating point number; the decimal point is optional. number: (q) => parsimmon_umd_minExports.regexp(/-?[0-9]+(\.[0-9]+)?/).map((str) => Number.parseFloat(str)).desc("number"), // A quote-surrounded string which supports escape characters ('\'). string: (q) => parsimmon_umd_minExports.string('"').then(parsimmon_umd_minExports.alt(q.escapeCharacter, parsimmon_umd_minExports.noneOf('"\\')).atLeast(0).map((chars2) => chars2.join(""))).skip(parsimmon_umd_minExports.string('"')).desc("string"), escapeCharacter: (_) => parsimmon_umd_minExports.string("\\").then(parsimmon_umd_minExports.any).map((escaped) => { if (escaped === '"') return '"'; if (escaped === "\\") return "\\"; else return "\\" + escaped; }), // A boolean true/false value. bool: (_) => parsimmon_umd_minExports.regexp(/true|false|True|False/).map((str) => str.toLowerCase() == "true").desc("boolean ('true' or 'false')"), // A tag of the form '#stuff/hello-there'. tag: (_) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("#"), parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(/[^\u2000-\u206F\u2E00-\u2E7F'!"#$%&()*+,.:;<=>?@^`{|}~\[\]\\\s]/).desc("text")).many(), (start, rest) => start + rest.join("")).desc("tag ('#hello/stuff')"), // A variable identifier, which is alphanumeric and must start with a letter or... emoji. identifier: (_) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(/\p{Letter}/u), parsimmon_umd_minExports.regexp(EMOJI_REGEX).desc("text")), parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(/[0-9\p{Letter}_-]/u), parsimmon_umd_minExports.regexp(EMOJI_REGEX).desc("text")).many(), (first, rest) => first + rest.join("")).desc("variable identifier"), // An Obsidian link of the form [[]]. link: (_) => parsimmon_umd_minExports.regexp(/\[\[([^\[\]]*?)\]\]/u, 1).map((linkInner) => parseInnerLink(linkInner)).desc("file link"), // An embeddable link which can start with '!'. This overlaps with the normal negation operator, so it is only // provided for metadata parsing. embedLink: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("!").atMost(1), q.link, (p, l2) => { if (p.length > 0) l2.embed = true; return l2; }).desc("file link"), // Binary plus or minus operator. binaryPlusMinus: (_) => parsimmon_umd_minExports.regexp(/\+|-/).map((str) => str).desc("'+' or '-'"), // Binary times or divide operator. binaryMulDiv: (_) => parsimmon_umd_minExports.regexp(/\*|\/|%/).map((str) => str).desc("'*' or '/' or '%'"), // Binary comparison operator. binaryCompareOp: (_) => parsimmon_umd_minExports.regexp(/>=|<=|!=|>|<|=/).map((str) => str).desc("'>=' or '<=' or '!=' or '=' or '>' or '<'"), // Binary boolean combination operator. binaryBooleanOp: (_) => parsimmon_umd_minExports.regexp(/and|or|&|\|/i).map((str) => { if (str.toLowerCase() == "and") return "&"; else if (str.toLowerCase() == "or") return "|"; else return str; }).desc("'and' or 'or'"), // A date which can be YYYY-MM[-DDTHH:mm:ss]. rootDate: (_) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/\d{4}/), parsimmon_umd_minExports.string("-"), parsimmon_umd_minExports.regexp(/\d{2}/), (year, _2, month) => { return DateTime.fromObject({ year: Number.parseInt(year), month: Number.parseInt(month) }); }).desc("date in format YYYY-MM[-DDTHH-MM-SS.MS]"), dateShorthand: (_) => parsimmon_umd_minExports.alt(...Object.keys(DATE_SHORTHANDS).sort((a, b) => b.length - a.length).map(parsimmon_umd_minExports.string)), date: (q) => chainOpt(q.rootDate, (ym) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("-"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, day) => ym.set({ day: Number.parseInt(day) })), (ymd) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("T"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, hour) => ymd.set({ hour: Number.parseInt(hour) })), (ymdh) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string(":"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, minute) => ymdh.set({ minute: Number.parseInt(minute) })), (ymdhm) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string(":"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, second) => ymdhm.set({ second: Number.parseInt(second) })), (ymdhms) => parsimmon_umd_minExports.alt( parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("."), parsimmon_umd_minExports.regexp(/\d{3}/), (_, millisecond) => ymdhms.set({ millisecond: Number.parseInt(millisecond) })), parsimmon_umd_minExports.succeed(ymdhms) // pass ), (dt) => parsimmon_umd_minExports.alt(parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("+").or(parsimmon_umd_minExports.string("-")), parsimmon_umd_minExports.regexp(/\d{1,2}(:\d{2})?/), (pm, hr) => dt.setZone("UTC" + pm + hr, { keepLocalTime: true })), parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("Z"), () => dt.setZone("utc", { keepLocalTime: true })), parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("["), parsimmon_umd_minExports.regexp(/[0-9A-Za-z+-\/]+/u), parsimmon_umd_minExports.string("]"), (_a, zone, _b) => dt.setZone(zone, { keepLocalTime: true })))).assert((dt) => dt.isValid, "valid date").desc("date in format YYYY-MM[-DDTHH-MM-SS.MS]"), // A date, plus various shorthand times of day it could be. datePlus: (q) => parsimmon_umd_minExports.alt(q.dateShorthand.map((d) => DATE_SHORTHANDS[d]()), q.date).desc("date in format YYYY-MM[-DDTHH-MM-SS.MS] or in shorthand"), // A duration of time. durationType: (_) => parsimmon_umd_minExports.alt(...Object.keys(DURATION_TYPES).sort((a, b) => b.length - a.length).map(parsimmon_umd_minExports.string)), duration: (q) => parsimmon_umd_minExports.seqMap(q.number, parsimmon_umd_minExports.optWhitespace, q.durationType, (count, _, t2) => DURATION_TYPES[t2].mapUnits((x) => x * count)).sepBy1(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace).or(parsimmon_umd_minExports.optWhitespace)).map((durations) => durations.reduce((p, c) => p.plus(c))).desc("duration like 4hr2min"), // A raw null value. rawNull: (_) => parsimmon_umd_minExports.string("null"), // Source parsing. tagSource: (q) => q.tag.map((tag) => Sources.tag(tag)), csvSource: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("csv(").skip(parsimmon_umd_minExports.optWhitespace), q.string, parsimmon_umd_minExports.string(")"), (_1, path, _2) => Sources.csv(path)), linkIncomingSource: (q) => q.link.map((link) => Sources.link(link.path, true)), linkOutgoingSource: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("outgoing(").skip(parsimmon_umd_minExports.optWhitespace), q.link, parsimmon_umd_minExports.string(")"), (_1, link, _2) => Sources.link(link.path, false)), folderSource: (q) => q.string.map((str) => Sources.folder(str)), parensSource: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("("), parsimmon_umd_minExports.optWhitespace, q.source, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (_1, _2, field, _3, _4) => field), negateSource: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.alt(parsimmon_umd_minExports.string("-"), parsimmon_umd_minExports.string("!")), q.atomSource, (_, source) => Sources.negate(source)), atomSource: (q) => parsimmon_umd_minExports.alt(q.parensSource, q.negateSource, q.linkOutgoingSource, q.linkIncomingSource, q.folderSource, q.tagSource, q.csvSource), binaryOpSource: (q) => createBinaryParser(q.atomSource, q.binaryBooleanOp.map((s2) => s2), Sources.binaryOp), source: (q) => q.binaryOpSource, // Field parsing. variableField: (q) => q.identifier.chain((r) => { if (KEYWORDS.includes(r.toUpperCase())) { return parsimmon_umd_minExports.fail("Variable fields cannot be a keyword (" + KEYWORDS.join(" or ") + ")"); } else { return parsimmon_umd_minExports.succeed(Fields.variable(r)); } }).desc("variable"), numberField: (q) => q.number.map((val) => Fields.literal(val)).desc("number"), stringField: (q) => q.string.map((val) => Fields.literal(val)).desc("string"), boolField: (q) => q.bool.map((val) => Fields.literal(val)).desc("boolean"), dateField: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("date("), parsimmon_umd_minExports.optWhitespace, q.datePlus, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (prefix, _1, date, _2, postfix) => Fields.literal(date)).desc("date"), durationField: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("dur("), parsimmon_umd_minExports.optWhitespace, q.duration, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (prefix, _1, dur, _2, postfix) => Fields.literal(dur)).desc("duration"), nullField: (q) => q.rawNull.map((_) => Fields.NULL), linkField: (q) => q.link.map((f) => Fields.literal(f)), listField: (q) => q.field.sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace)).wrap(parsimmon_umd_minExports.string("[").skip(parsimmon_umd_minExports.optWhitespace), parsimmon_umd_minExports.optWhitespace.then(parsimmon_umd_minExports.string("]"))).map((l2) => Fields.list(l2)).desc("list ('[1, 2, 3]')"), objectField: (q) => parsimmon_umd_minExports.seqMap(q.identifier.or(q.string), parsimmon_umd_minExports.string(":").trim(parsimmon_umd_minExports.optWhitespace), q.field, (name, _sep, value) => { return { name, value }; }).sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace)).wrap(parsimmon_umd_minExports.string("{").skip(parsimmon_umd_minExports.optWhitespace), parsimmon_umd_minExports.optWhitespace.then(parsimmon_umd_minExports.string("}"))).map((vals) => { let res = {}; for (let entry of vals) res[entry.name] = entry.value; return Fields.object(res); }).desc("object ('{ a: 1, b: 2 }')"), atomInlineField: (q) => parsimmon_umd_minExports.alt(q.date, q.duration.map((d) => normalizeDuration(d)), q.string, q.tag, q.embedLink, q.bool, q.number, q.rawNull), inlineFieldList: (q) => q.atomInlineField.sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace).lookahead(q.atomInlineField)), inlineField: (q) => parsimmon_umd_minExports.alt(parsimmon_umd_minExports.seqMap(q.atomInlineField, parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace), q.inlineFieldList, (f, _s, l2) => [f].concat(l2)), q.atomInlineField), atomField: (q) => parsimmon_umd_minExports.alt( // Place embed links above negated fields as they are the special parser case '![[thing]]' and are generally unambiguous. q.embedLink.map((l2) => Fields.literal(l2)), q.negatedField, q.linkField, q.listField, q.objectField, q.lambdaField, q.parensField, q.boolField, q.numberField, q.stringField, q.dateField, q.durationField, q.nullField, q.variableField ), indexField: (q) => parsimmon_umd_minExports.seqMap(q.atomField, parsimmon_umd_minExports.alt(q.dotPostfix, q.indexPostfix, q.functionPostfix).many(), (obj, postfixes) => { let result = obj; for (let post of postfixes) { switch (post.type) { case "dot": result = Fields.index(result, Fields.literal(post.field)); break; case "index": result = Fields.index(result, post.field); break; case "function": result = Fields.func(result, post.fields); break; } } return result; }), negatedField: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("!"), q.indexField, (_, field) => Fields.negate(field)).desc("negated field"), parensField: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("("), parsimmon_umd_minExports.optWhitespace, q.field, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (_1, _2, field, _3, _4) => field), lambdaField: (q) => parsimmon_umd_minExports.seqMap(q.identifier.sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace)).wrap(parsimmon_umd_minExports.string("(").trim(parsimmon_umd_minExports.optWhitespace), parsimmon_umd_minExports.string(")").trim(parsimmon_umd_minExports.optWhitespace)), parsimmon_umd_minExports.string("=>").trim(parsimmon_umd_minExports.optWhitespace), q.field, (ident, _ignore, value) => { return { type: "lambda", arguments: ident, value }; }), dotPostfix: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("."), q.identifier, (_, field) => { return { type: "dot", field }; }), indexPostfix: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("["), parsimmon_umd_minExports.optWhitespace, q.field, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string("]"), (_, _2, field, _3, _4) => { return { type: "index", field }; }), functionPostfix: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("("), parsimmon_umd_minExports.optWhitespace, q.field.sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace)), parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (_, _1, fields, _2, _3) => { return { type: "function", fields }; }), // The precedence hierarchy of operators - multiply/divide, add/subtract, compare, and then boolean operations. binaryMulDivField: (q) => createBinaryParser(q.indexField, q.binaryMulDiv, Fields.binaryOp), binaryPlusMinusField: (q) => createBinaryParser(q.binaryMulDivField, q.binaryPlusMinus, Fields.binaryOp), binaryCompareField: (q) => createBinaryParser(q.binaryPlusMinusField, q.binaryCompareOp, Fields.binaryOp), binaryBooleanField: (q) => createBinaryParser(q.binaryCompareField, q.binaryBooleanOp, Fields.binaryOp), binaryOpField: (q) => q.binaryBooleanField, field: (q) => q.binaryOpField }); function parseField(text) { try { return Result.success(EXPRESSION.field.tryParse(text)); } catch (error) { return Result.failure("" + error); } } var QueryFields; (function(QueryFields2) { function named(name, field) { return { name, field }; } QueryFields2.named = named; function sortBy(field, dir2) { return { field, direction: dir2 }; } QueryFields2.sortBy = sortBy; })(QueryFields || (QueryFields = {})); function captureRaw(base) { return parsimmon_umd_minExports.custom((success, failure) => { return (input, i) => { let result = base._(input, i); if (!result.status) return result; return Object.assign({}, result, { value: [result.value, input.substring(i, result.index)] }); }; }); } function stripNewlines(text) { return text.split(/[\r\n]+/).map((t2) => t2.trim()).join(""); } function precededByWhitespaceIfNotEof(if_eof, parser) { return parsimmon_umd_minExports.eof.map(if_eof).or(parsimmon_umd_minExports.whitespace.then(parser)); } var QUERY_LANGUAGE = parsimmon_umd_minExports.createLanguage({ // Simple atom parsing, like words, identifiers, numbers. queryType: (q) => parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(/TABLE|LIST|TASK|CALENDAR/i)).map((str) => str.toLowerCase()).desc("query type ('TABLE', 'LIST', 'TASK', or 'CALENDAR')"), explicitNamedField: (q) => parsimmon_umd_minExports.seqMap(EXPRESSION.field.skip(parsimmon_umd_minExports.whitespace), parsimmon_umd_minExports.regexp(/AS/i).skip(parsimmon_umd_minExports.whitespace), EXPRESSION.identifier.or(EXPRESSION.string), (field, _as, ident) => QueryFields.named(ident, field)), comment: () => parsimmon_umd_minExports.Parser((input, i) => { let line = input.substring(i); if (!line.startsWith("//")) return parsimmon_umd_minExports.makeFailure(i, "Not a comment"); line = line.split("\n")[0]; let comment = line.substring(2).trim(); return parsimmon_umd_minExports.makeSuccess(i + line.length, comment); }), namedField: (q) => parsimmon_umd_minExports.alt(q.explicitNamedField, captureRaw(EXPRESSION.field).map(([value, text]) => QueryFields.named(stripNewlines(text), value))), sortField: (q) => parsimmon_umd_minExports.seqMap(EXPRESSION.field.skip(parsimmon_umd_minExports.optWhitespace), parsimmon_umd_minExports.regexp(/ASCENDING|DESCENDING|ASC|DESC/i).atMost(1), (field, dir2) => { let direction = dir2.length == 0 ? "ascending" : dir2[0].toLowerCase(); if (direction == "desc") direction = "descending"; if (direction == "asc") direction = "ascending"; return { field, direction }; }), headerClause: (q) => q.queryType.chain((type) => { switch (type) { case "table": { return precededByWhitespaceIfNotEof(() => ({ type, fields: [], showId: true }), parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/WITHOUT\s+ID/i).skip(parsimmon_umd_minExports.optWhitespace).atMost(1), parsimmon_umd_minExports.sepBy(q.namedField, parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace)), (withoutId, fields) => { return { type, fields, showId: withoutId.length == 0 }; })); } case "list": return precededByWhitespaceIfNotEof(() => ({ type, format: void 0, showId: true }), parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/WITHOUT\s+ID/i).skip(parsimmon_umd_minExports.optWhitespace).atMost(1), EXPRESSION.field.atMost(1), (withoutId, format) => { return { type, format: format.length == 1 ? format[0] : void 0, showId: withoutId.length == 0 }; })); case "task": return parsimmon_umd_minExports.succeed({ type }); case "calendar": return parsimmon_umd_minExports.whitespace.then(parsimmon_umd_minExports.seqMap(q.namedField, (field) => { return { type, showId: true, field }; })); default: return parsimmon_umd_minExports.fail(`Unrecognized query type '${type}'`); } }).desc("TABLE or LIST or TASK or CALENDAR"), fromClause: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/FROM/i), parsimmon_umd_minExports.whitespace, EXPRESSION.source, (_1, _2, source) => source), whereClause: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/WHERE/i), parsimmon_umd_minExports.whitespace, EXPRESSION.field, (where, _, field) => { return { type: "where", clause: field }; }).desc("WHERE "), sortByClause: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/SORT/i), parsimmon_umd_minExports.whitespace, q.sortField.sepBy1(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace)), (sort, _1, fields) => { return { type: "sort", fields }; }).desc("SORT field [ASC/DESC]"), limitClause: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/LIMIT/i), parsimmon_umd_minExports.whitespace, EXPRESSION.field, (limit, _1, field) => { return { type: "limit", amount: field }; }).desc("LIMIT "), flattenClause: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/FLATTEN/i).skip(parsimmon_umd_minExports.whitespace), q.namedField, (_, field) => { return { type: "flatten", field }; }).desc("FLATTEN [AS ]"), groupByClause: (q) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/GROUP BY/i).skip(parsimmon_umd_minExports.whitespace), q.namedField, (_, field) => { return { type: "group", field }; }).desc("GROUP BY [AS ]"), // Full query parsing. clause: (q) => parsimmon_umd_minExports.alt(q.fromClause, q.whereClause, q.sortByClause, q.limitClause, q.groupByClause, q.flattenClause), query: (q) => parsimmon_umd_minExports.seqMap(q.headerClause.trim(optionalWhitespaceOrComment), q.fromClause.trim(optionalWhitespaceOrComment).atMost(1), q.clause.trim(optionalWhitespaceOrComment).many(), (header, from, clauses) => { return { header, source: from.length == 0 ? Sources.folder("") : from[0], operations: clauses, settings: DEFAULT_QUERY_SETTINGS }; }) }); var optionalWhitespaceOrComment = parsimmon_umd_minExports.alt(parsimmon_umd_minExports.whitespace, QUERY_LANGUAGE.comment).many().map((arr) => arr.join("")); var getAPI = (app) => { var _a; if (app) return (_a = app.plugins.plugins.dataview) == null ? void 0 : _a.api; else return window.DataviewAPI; }; var isPluginEnabled3 = (app) => app.plugins.enabledPlugins.has("dataview"); exports.DATE_SHORTHANDS = DATE_SHORTHANDS; exports.DURATION_TYPES = DURATION_TYPES; exports.EXPRESSION = EXPRESSION; exports.KEYWORDS = KEYWORDS; exports.QUERY_LANGUAGE = QUERY_LANGUAGE; exports.getAPI = getAPI; exports.isPluginEnabled = isPluginEnabled3; exports.parseField = parseField; } }); // src/main.ts var main_exports = {}; __export(main_exports, { default: () => ChemPlugin }); module.exports = __toCommonJS(main_exports); var import_obsidian7 = require("obsidian"); // src/settings/v1.ts var DEFAULT_SETTINGS_V1 = { darkTheme: "dark", lightTheme: "light", sample: "CC(=O)NC1=C-C=C-C=C1-C(=O)O", width: "300" }; // src/settings/v2.ts var DEFAULT_SETTINGS_V2 = { version: "v2", darkTheme: "dark", lightTheme: "light", sample1: "OC(=O)C(C)=CC1=CC=CC=C1", sample2: "OC(C(=O)O[C@H]1C[N+]2(CCCOC3=CC=CC=C3)CCC1CC2)(C1=CC=CS1)C1=CC=CS1", imgWidth: 300, copy: { scale: 2, transparent: true, theme: "default" }, dataview: false, inlineSmiles: false, inlineSmilesPrefix: "$smiles=", options: {} }; // src/settings/v3.ts var DEFAULT_SETTINGS_V3 = { version: "v3", darkTheme: "dark", lightTheme: "light", sample1: "OC(=O)C(C)=CC1=CC=CC=C1", sample2: "OC(C(=O)O[C@H]1C[N+]2(CCCOC3=CC=CC=C3)CCC1CC2)(C1=CC=CS1)C1=CC=CS1", copy: { scale: 2, transparent: true, theme: "default" }, dataview: false, inlineSmiles: false, inlineSmilesPrefix: "$smiles=", core: "smiles-drawer", commonOptions: { width: 300, scale: 1, unifiedWidth: 300, compactDrawing: false, explicitHydrogens: false, explicitMethyl: false }, smilesDrawerOptions: { moleculeOptions: {}, reactionOptions: {} }, rdkitOptions: {} }; // src/settings/base.ts var DEFAULT_SETTINGS = DEFAULT_SETTINGS_V3; var migrate_1_3 = (v1) => { v1 = { ...DEFAULT_SETTINGS_V1, ...v1 }; return { ...DEFAULT_SETTINGS_V3, darkTheme: v1.darkTheme, lightTheme: v1.lightTheme, sample1: v1.sample, commonOptions: { ...DEFAULT_SETTINGS_V3.commonOptions, unifiedWidth: parseInt(v1.width) } }; }; var migrate_2_3 = (v2) => { v2 = { ...DEFAULT_SETTINGS_V2, ...v2 }; return { ...DEFAULT_SETTINGS_V3, darkTheme: v2.darkTheme, lightTheme: v2.lightTheme, sample1: v2.sample1, sample2: v2.sample2, copy: { scale: v2.copy.scale, transparent: v2.copy.transparent, theme: v2.copy.theme }, dataview: v2.dataview, inlineSmiles: v2.inlineSmiles, inlineSmilesPrefix: v2.inlineSmilesPrefix, commonOptions: { width: v2.options.width, scale: v2.options.scale, unifiedWidth: v2.imgWidth, compactDrawing: v2.options.compactDrawing, explicitHydrogens: v2.options.explicitHydrogens, explicitMethyl: v2.options.terminalCarbons }, smilesDrawerOptions: { ...DEFAULT_SETTINGS_V3.smilesDrawerOptions, moleculeOptions: v2.options }, rdkitOptions: { explicitMethyl: v2.options.terminalCarbons } }; }; var migrate_3_3 = (draft) => { return { ...DEFAULT_SETTINGS, ...draft }; }; var migrateSettings = (draft) => { if (!draft || Object.keys(draft).length === 0) return DEFAULT_SETTINGS; if (!("version" in draft)) return migrate_1_3(draft); else if (draft.version === "v2") return migrate_2_3(draft); else if (draft.version === "v3") return migrate_3_3(draft); return DEFAULT_SETTINGS; }; // src/lib/themes/rdkitThemes.ts var RDKitThemes = { "rdkit-default": { "-1": [0, 0, 0], "0": [0.1, 0.1, 0.1], "6": [0, 0, 0], "7": [0, 0, 1], "8": [1, 0, 0], "9": [0.2, 0.8, 0.8], "15": [1, 0.5, 0], "16": [0.8, 0.8, 0], "17": [0, 0.802, 0], "35": [0.5, 0.3, 0.1], "53": [0.63, 0.12, 0.94], "201": [0.68, 0.85, 0.9] }, "rdkit-avalon": { "-1": [0, 0, 0], "0": [0.1, 0.1, 0.1], "6": [0, 0, 0], "7": [0, 0, 1], "8": [1, 0, 0], "9": [0, 0.498, 0], "15": [0.498, 0, 0.498], "16": [0.498, 0.247, 0], "17": [0, 0.498, 0], "35": [0, 0.498, 0], "53": [0.247, 0, 0.498], "201": [0.68, 0.85, 0.9] }, "rdkit-cdk": { "-1": [0, 0, 0], "0": [0.1, 0.1, 0.1], "6": [0, 0, 0], "7": [0.188, 0.314, 0.972], "8": [1, 0.051, 0.051], "9": [0.565, 0.878, 0.314], "15": [1, 0.5, 0], "16": [0.776, 0.776, 0.173], "17": [0.122, 0.498, 0.122], "35": [0.651, 0.161, 0.161], "53": [0.58, 0, 0.58], "5": [1, 0.71, 0.71], "201": [0.68, 0.85, 0.9] }, "rdkit-dark": { "-1": [0.8, 0.8, 0.8], "0": [0.9, 0.9, 0.9], "6": [0.9, 0.9, 0.9], "7": [0.33, 0.41, 0.92], "8": [1, 0.2, 0.2], "9": [0.2, 0.8, 0.8], "15": [1, 0.5, 0], "16": [0.8, 0.8, 0], "17": [0, 0.802, 0], "35": [0.71, 0.4, 0.07], "53": [0.89, 4e-3, 1], "201": [0.68, 0.85, 0.9] } }; // src/lib/themes/smilesDrawerThemes.ts var SDThemes = { dark: { C: "#fff", O: "#e74c3c", N: "#3498db", F: "#27ae60", CL: "#16a085", BR: "#d35400", I: "#8e44ad", P: "#d35400", S: "#f1c40f", B: "#e67e22", SI: "#e67e22", H: "#aaa", BACKGROUND: "#141414" }, light: { C: "#222", O: "#e74c3c", N: "#3498db", F: "#27ae60", CL: "#16a085", BR: "#d35400", I: "#8e44ad", P: "#d35400", S: "#f1c40f", B: "#e67e22", SI: "#e67e22", H: "#666", BACKGROUND: "#fff" }, oldschool: { C: "#000", O: "#000", N: "#000", F: "#000", CL: "#000", BR: "#000", I: "#000", P: "#000", S: "#000", B: "#000", SI: "#000", H: "#000", BACKGROUND: "#fff" }, "oldschool-dark": { C: "#fff", O: "#fff", N: "#fff", F: "#fff", CL: "#fff", BR: "#fff", I: "#fff", P: "#fff", S: "#fff", B: "#fff", SI: "#fff", H: "#fff", BACKGROUND: "#000" }, solarized: { C: "#586e75", O: "#dc322f", N: "#268bd2", F: "#859900", CL: "#16a085", BR: "#cb4b16", I: "#6c71c4", P: "#d33682", S: "#b58900", B: "#2aa198", SI: "#2aa198", H: "#657b83", BACKGROUND: "#fff" }, "solarized-dark": { C: "#93a1a1", O: "#dc322f", N: "#268bd2", F: "#859900", CL: "#16a085", BR: "#cb4b16", I: "#6c71c4", P: "#d33682", S: "#b58900", B: "#2aa198", SI: "#2aa198", H: "#839496", BACKGROUND: "#fff" }, matrix: { C: "#678c61", O: "#2fc079", N: "#4f7e7e", F: "#90d762", CL: "#82d967", BR: "#23755a", I: "#409931", P: "#c1ff8a", S: "#faff00", B: "#50b45a", SI: "#409931", H: "#426644", BACKGROUND: "#fff" }, github: { C: "#24292f", O: "#cf222e", N: "#0969da", F: "#2da44e", CL: "#6fdd8b", BR: "#bc4c00", I: "#8250df", P: "#bf3989", S: "#d4a72c", B: "#fb8f44", SI: "#bc4c00", H: "#57606a", BACKGROUND: "#fff" }, carbon: { C: "#161616", O: "#da1e28", N: "#0f62fe", F: "#198038", CL: "#007d79", BR: "#fa4d56", I: "#8a3ffc", P: "#ff832b", S: "#f1c21b", B: "#8a3800", SI: "#e67e22", H: "#525252", BACKGROUND: "#fff" }, cyberpunk: { C: "#ea00d9", O: "#ff3131", N: "#0abdc6", F: "#00ff9f", CL: "#00fe00", BR: "#fe9f20", I: "#ff00ff", P: "#fe7f00", S: "#fcee0c", B: "#ff00ff", SI: "#ffffff", H: "#913cb1", BACKGROUND: "#fff" }, gruvbox: { C: "#665c54", O: "#cc241d", N: "#458588", F: "#98971a", CL: "#79740e", BR: "#d65d0e", I: "#b16286", P: "#af3a03", S: "#d79921", B: "#689d6a", SI: "#427b58", H: "#7c6f64", BACKGROUND: "#fbf1c7" }, "gruvbox-dark": { C: "#ebdbb2", O: "#cc241d", N: "#458588", F: "#98971a", CL: "#b8bb26", BR: "#d65d0e", I: "#b16286", P: "#fe8019", S: "#d79921", B: "#8ec07c", SI: "#83a598", H: "#bdae93", BACKGROUND: "#282828" }, "catppuccin-dark": { C: "#cdd6f4", O: "#f38ba8", N: "#89b4fa", F: "#f9e2af", CL: "#cba6f7", BR: "#a6e3a1", I: "#b4befe", P: "#fab387", S: "#f5e0dc", B: "#eba0ac", SI: "#89dceb", H: "#94e2d5", BACKGROUND: "#1e1e2e" }, "catppuccin-light": { C: "#4c4f69", O: "#d20f39", N: "#1e66f5", F: "#df8e1d", CL: "#8839ef", BR: "#40a02b", I: "#7287fd", P: "#fe640b", S: "#dc8a78", B: "#e64553", SI: "#04a5e5", H: "#179299", BACKGROUND: "#eff1f5" }, custom: { C: "#222", O: "#e74c3c", N: "#3498db", F: "#27ae60", CL: "#16a085", BR: "#d35400", I: "#8e44ad", P: "#d35400", S: "#f1c40f", B: "#e67e22", SI: "#e67e22", H: "#666", BACKGROUND: "#fff" } }; // src/lib/themes/helpers.ts function hex2RGB(hex) { hex = hex.slice(1); if (hex.length === 3) { hex = hex.split("").map((l) => l.repeat(2)).join(""); } const hexInt = parseInt(hex, 16); const r = hexInt >> 16 & 255; const g = hexInt >> 8 & 255; const b = hexInt & 255; return [r / 255, g / 255, b / 255]; } function RGB2hex(rgb) { return "#" + rgb.map( (v) => Math.round(v * 255).toString(16).padStart(2, "0") ).join(""); } // src/lib/themes/theme.ts var themeList = { // from smiles-drawer light: "Light", dark: "Dark", oldschool: "Oldschool", "oldschool-dark": "Oldschool Dark", solarized: "Solarized", "solarized-dark": "Solarized Dark", matrix: "Matrix", github: "GitHub", carbon: "Carbon", cyberpunk: "Cyberpunk", gruvbox: "Gruvbox", "gruvbox-dark": "Gruvbox Dark", "catppuccin-light": "Catppuccin Latte", "catppuccin-dark": "Catppuccin Mocha", // from RDKit "rdkit-default": "Light (RDKit)", "rdkit-avalon": "Avalon (RDKit)", "rdkit-cdk": "CDK (RDKit)", "rdkit-dark": "Dark (RDKit)" }; var convertToSDTheme = (name) => { if (Object.keys(SDThemes).includes(name)) { return SDThemes[name]; } else if (Object.keys(RDKitThemes).includes(name)) { const p = RDKitThemes[name]; const t2 = {}; Keys_RD2SD.forEach((v, k) => { if (p[v]) t2[k] = RGB2hex(p[v]); }); t2["BACKGROUND"] = getSDBGColor(name); return t2; } return SDThemes["light"]; }; var convertToRDKitTheme = (name) => { if (Object.keys(SDThemes).includes(name)) { const t2 = SDThemes[name]; const p = {}; Keys_SD2RD.forEach((v, k) => { p[k] = hex2RGB(t2[v]); }); return p; } else if (Object.keys(RDKitThemes).includes(name)) { return RDKitThemes[name]; } return RDKitThemes["rdkit-default"]; }; var getSDBGColor = (name) => { if (Object.keys(SDThemes).includes(name)) { const t2 = SDThemes[name]; return t2["BACKGROUND"]; } else if (Object.keys(RDKitThemes).includes(name)) { return RGB2hex(RDKitThemes[name][6].map((v) => 1 - v)); } return "#000"; }; var Keys_RD2SD = /* @__PURE__ */ new Map([ ["C", 6], ["O", 8], ["N", 7], ["F", 9], ["CL", 17], ["BR", 35], ["I", 53], ["P", 15], ["S", 16], ["B", 5], ["SI", 14], ["H", 1] ]); var Keys_SD2RD = /* @__PURE__ */ new Map([ [-1, "H"], [0, "C"], [6, "C"], [7, "N"], [8, "O"], [9, "F"], [14, "SI"], [15, "P"], [16, "S"], [17, "CL"], [35, "BR"], [53, "I"], [5, "B"], [201, "H"] ]); // src/lib/core/smilesDrawerCore.ts var import_smiles_drawer = __toESM(require_app()); // src/lib/core/smilesDrawerOptions.ts var collectThemes = () => { const newThemes = {}; Object.keys(RDKitThemes).forEach( (name) => newThemes[name] = { ...SDThemes["light"], // TODO: 0.4.2 check this override ...convertToSDTheme(name) } ); return { ...SDThemes, ...newThemes }; }; var DEFAULT_SD_OPTIONS = { moleculeOptions: { width: 300, height: 300, scale: 1, bondThickness: 1, shortBondLength: 0.8, bondSpacing: 5.1, atomVisualization: "default", isomeric: true, debug: false, terminalCarbons: false, explicitHydrogens: true, overlapSensitivity: 0.42, overlapResolutionIterations: 1, compactDrawing: false, fontFamily: "Arial, Helvetica, sans-serif", fontSizeLarge: 11, fontSizeSmall: 3, padding: 2, experimentalSSSR: true, kkThreshold: 0.1, kkInnerThreshold: 0.1, kkMaxIteration: 2e4, kkMaxInnerIteration: 50, kkMaxEnergy: 1e9, themes: collectThemes() }, reactionOptions: { fontSize: 9, fontFamily: "Arial, Helvetica, sans-serif", spacing: 10, plus: { size: 9, thickness: 1 }, arrow: { length: 120, headSize: 6, thickness: 1, margin: 3 } } }; // src/lib/themes/getCurrentTheme.ts var getCurrentTheme = (settings) => { return document.body.hasClass("theme-dark") && !document.body.hasClass("theme-light") ? settings.darkTheme : settings.lightTheme; }; // node_modules/i18next/dist/esm/i18next.js var isString = (obj) => typeof obj === "string"; var defer = () => { let res; let rej; const promise = new Promise((resolve, reject) => { res = resolve; rej = reject; }); promise.resolve = res; promise.reject = rej; return promise; }; var makeString = (object) => { if (object == null) return ""; return "" + object; }; var copy = (a, s, t2) => { a.forEach((m) => { if (s[m]) t2[m] = s[m]; }); }; var lastOfPathSeparatorRegExp = /###/g; var cleanKey = (key) => key && key.indexOf("###") > -1 ? key.replace(lastOfPathSeparatorRegExp, ".") : key; var canNotTraverseDeeper = (object) => !object || isString(object); var getLastOfPath = (object, path, Empty) => { const stack = !isString(path) ? path : path.split("."); let stackIndex = 0; while (stackIndex < stack.length - 1) { if (canNotTraverseDeeper(object)) return {}; const key = cleanKey(stack[stackIndex]); if (!object[key] && Empty) object[key] = new Empty(); if (Object.prototype.hasOwnProperty.call(object, key)) { object = object[key]; } else { object = {}; } ++stackIndex; } if (canNotTraverseDeeper(object)) return {}; return { obj: object, k: cleanKey(stack[stackIndex]) }; }; var setPath = (object, path, newValue) => { const { obj, k } = getLastOfPath(object, path, Object); if (obj !== void 0 || path.length === 1) { obj[k] = newValue; return; } let e = path[path.length - 1]; let p = path.slice(0, path.length - 1); let last = getLastOfPath(object, p, Object); while (last.obj === void 0 && p.length) { e = `${p[p.length - 1]}.${e}`; p = p.slice(0, p.length - 1); last = getLastOfPath(object, p, Object); if (last && last.obj && typeof last.obj[`${last.k}.${e}`] !== "undefined") { last.obj = void 0; } } last.obj[`${last.k}.${e}`] = newValue; }; var pushPath = (object, path, newValue, concat) => { const { obj, k } = getLastOfPath(object, path, Object); obj[k] = obj[k] || []; obj[k].push(newValue); }; var getPath = (object, path) => { const { obj, k } = getLastOfPath(object, path); if (!obj) return void 0; return obj[k]; }; var getPathWithDefaults = (data, defaultData, key) => { const value = getPath(data, key); if (value !== void 0) { return value; } return getPath(defaultData, key); }; var deepExtend = (target, source, overwrite) => { for (const prop in source) { if (prop !== "__proto__" && prop !== "constructor") { if (prop in target) { if (isString(target[prop]) || target[prop] instanceof String || isString(source[prop]) || source[prop] instanceof String) { if (overwrite) target[prop] = source[prop]; } else { deepExtend(target[prop], source[prop], overwrite); } } else { target[prop] = source[prop]; } } } return target; }; var regexEscape = (str) => str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); var _entityMap = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", "/": "/" }; var escape = (data) => { if (isString(data)) { return data.replace(/[&<>"'\/]/g, (s) => _entityMap[s]); } return data; }; var RegExpCache = class { constructor(capacity) { this.capacity = capacity; this.regExpMap = /* @__PURE__ */ new Map(); this.regExpQueue = []; } getRegExp(pattern) { const regExpFromCache = this.regExpMap.get(pattern); if (regExpFromCache !== void 0) { return regExpFromCache; } const regExpNew = new RegExp(pattern); if (this.regExpQueue.length === this.capacity) { this.regExpMap.delete(this.regExpQueue.shift()); } this.regExpMap.set(pattern, regExpNew); this.regExpQueue.push(pattern); return regExpNew; } }; var chars = [" ", ",", "?", "!", ";"]; var looksLikeObjectPathRegExpCache = new RegExpCache(20); var looksLikeObjectPath = (key, nsSeparator, keySeparator) => { nsSeparator = nsSeparator || ""; keySeparator = keySeparator || ""; const possibleChars = chars.filter((c) => nsSeparator.indexOf(c) < 0 && keySeparator.indexOf(c) < 0); if (possibleChars.length === 0) return true; const r = looksLikeObjectPathRegExpCache.getRegExp(`(${possibleChars.map((c) => c === "?" ? "\\?" : c).join("|")})`); let matched = !r.test(key); if (!matched) { const ki = key.indexOf(keySeparator); if (ki > 0 && !r.test(key.substring(0, ki))) { matched = true; } } return matched; }; var deepFind = function(obj, path) { let keySeparator = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "."; if (!obj) return void 0; if (obj[path]) return obj[path]; const tokens = path.split(keySeparator); let current = obj; for (let i = 0; i < tokens.length; ) { if (!current || typeof current !== "object") { return void 0; } let next; let nextPath = ""; for (let j = i; j < tokens.length; ++j) { if (j !== i) { nextPath += keySeparator; } nextPath += tokens[j]; next = current[nextPath]; if (next !== void 0) { if (["string", "number", "boolean"].indexOf(typeof next) > -1 && j < tokens.length - 1) { continue; } i += j - i + 1; break; } } current = next; } return current; }; var getCleanedCode = (code) => code && code.replace("_", "-"); var consoleLogger = { type: "logger", log(args) { this.output("log", args); }, warn(args) { this.output("warn", args); }, error(args) { this.output("error", args); }, output(type, args) { if (console && console[type]) console[type].apply(console, args); } }; var Logger = class { constructor(concreteLogger) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; this.init(concreteLogger, options); } init(concreteLogger) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; this.prefix = options.prefix || "i18next:"; this.logger = concreteLogger || consoleLogger; this.options = options; this.debug = options.debug; } log() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return this.forward(args, "log", "", true); } warn() { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return this.forward(args, "warn", "", true); } error() { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return this.forward(args, "error", ""); } deprecate() { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } return this.forward(args, "warn", "WARNING DEPRECATED: ", true); } forward(args, lvl, prefix, debugOnly) { if (debugOnly && !this.debug) return null; if (isString(args[0])) args[0] = `${prefix}${this.prefix} ${args[0]}`; return this.logger[lvl](args); } create(moduleName) { return new Logger(this.logger, { ...{ prefix: `${this.prefix}:${moduleName}:` }, ...this.options }); } clone(options) { options = options || this.options; options.prefix = options.prefix || this.prefix; return new Logger(this.logger, options); } }; var baseLogger = new Logger(); var EventEmitter = class { constructor() { this.observers = {}; } on(events, listener) { events.split(" ").forEach((event) => { if (!this.observers[event]) this.observers[event] = /* @__PURE__ */ new Map(); const numListeners = this.observers[event].get(listener) || 0; this.observers[event].set(listener, numListeners + 1); }); return this; } off(event, listener) { if (!this.observers[event]) return; if (!listener) { delete this.observers[event]; return; } this.observers[event].delete(listener); } emit(event) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } if (this.observers[event]) { const cloned = Array.from(this.observers[event].entries()); cloned.forEach((_ref) => { let [observer, numTimesAdded] = _ref; for (let i = 0; i < numTimesAdded; i++) { observer(...args); } }); } if (this.observers["*"]) { const cloned = Array.from(this.observers["*"].entries()); cloned.forEach((_ref2) => { let [observer, numTimesAdded] = _ref2; for (let i = 0; i < numTimesAdded; i++) { observer.apply(observer, [event, ...args]); } }); } } }; var ResourceStore = class extends EventEmitter { constructor(data) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : { ns: ["translation"], defaultNS: "translation" }; super(); this.data = data || {}; this.options = options; if (this.options.keySeparator === void 0) { this.options.keySeparator = "."; } if (this.options.ignoreJSONStructure === void 0) { this.options.ignoreJSONStructure = true; } } addNamespaces(ns) { if (this.options.ns.indexOf(ns) < 0) { this.options.ns.push(ns); } } removeNamespaces(ns) { const index = this.options.ns.indexOf(ns); if (index > -1) { this.options.ns.splice(index, 1); } } getResource(lng, ns, key) { let options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {}; const keySeparator = options.keySeparator !== void 0 ? options.keySeparator : this.options.keySeparator; const ignoreJSONStructure = options.ignoreJSONStructure !== void 0 ? options.ignoreJSONStructure : this.options.ignoreJSONStructure; let path; if (lng.indexOf(".") > -1) { path = lng.split("."); } else { path = [lng, ns]; if (key) { if (Array.isArray(key)) { path.push(...key); } else if (isString(key) && keySeparator) { path.push(...key.split(keySeparator)); } else { path.push(key); } } } const result = getPath(this.data, path); if (!result && !ns && !key && lng.indexOf(".") > -1) { lng = path[0]; ns = path[1]; key = path.slice(2).join("."); } if (result || !ignoreJSONStructure || !isString(key)) return result; return deepFind(this.data && this.data[lng] && this.data[lng][ns], key, keySeparator); } addResource(lng, ns, key, value) { let options = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : { silent: false }; const keySeparator = options.keySeparator !== void 0 ? options.keySeparator : this.options.keySeparator; let path = [lng, ns]; if (key) path = path.concat(keySeparator ? key.split(keySeparator) : key); if (lng.indexOf(".") > -1) { path = lng.split("."); value = ns; ns = path[1]; } this.addNamespaces(ns); setPath(this.data, path, value); if (!options.silent) this.emit("added", lng, ns, key, value); } addResources(lng, ns, resources) { let options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : { silent: false }; for (const m in resources) { if (isString(resources[m]) || Array.isArray(resources[m])) this.addResource(lng, ns, m, resources[m], { silent: true }); } if (!options.silent) this.emit("added", lng, ns, resources); } addResourceBundle(lng, ns, resources, deep, overwrite) { let options = arguments.length > 5 && arguments[5] !== void 0 ? arguments[5] : { silent: false, skipCopy: false }; let path = [lng, ns]; if (lng.indexOf(".") > -1) { path = lng.split("."); deep = resources; resources = ns; ns = path[1]; } this.addNamespaces(ns); let pack = getPath(this.data, path) || {}; if (!options.skipCopy) resources = JSON.parse(JSON.stringify(resources)); if (deep) { deepExtend(pack, resources, overwrite); } else { pack = { ...pack, ...resources }; } setPath(this.data, path, pack); if (!options.silent) this.emit("added", lng, ns, resources); } removeResourceBundle(lng, ns) { if (this.hasResourceBundle(lng, ns)) { delete this.data[lng][ns]; } this.removeNamespaces(ns); this.emit("removed", lng, ns); } hasResourceBundle(lng, ns) { return this.getResource(lng, ns) !== void 0; } getResourceBundle(lng, ns) { if (!ns) ns = this.options.defaultNS; if (this.options.compatibilityAPI === "v1") return { ...{}, ...this.getResource(lng, ns) }; return this.getResource(lng, ns); } getDataByLanguage(lng) { return this.data[lng]; } hasLanguageSomeTranslations(lng) { const data = this.getDataByLanguage(lng); const n = data && Object.keys(data) || []; return !!n.find((v) => data[v] && Object.keys(data[v]).length > 0); } toJSON() { return this.data; } }; var postProcessor = { processors: {}, addPostProcessor(module2) { this.processors[module2.name] = module2; }, handle(processors, value, key, options, translator) { processors.forEach((processor) => { if (this.processors[processor]) value = this.processors[processor].process(value, key, options, translator); }); return value; } }; var checkedLoadedFor = {}; var Translator = class extends EventEmitter { constructor(services) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; super(); copy(["resourceStore", "languageUtils", "pluralResolver", "interpolator", "backendConnector", "i18nFormat", "utils"], services, this); this.options = options; if (this.options.keySeparator === void 0) { this.options.keySeparator = "."; } this.logger = baseLogger.create("translator"); } changeLanguage(lng) { if (lng) this.language = lng; } exists(key) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : { interpolation: {} }; if (key === void 0 || key === null) { return false; } const resolved = this.resolve(key, options); return resolved && resolved.res !== void 0; } extractFromKey(key, options) { let nsSeparator = options.nsSeparator !== void 0 ? options.nsSeparator : this.options.nsSeparator; if (nsSeparator === void 0) nsSeparator = ":"; const keySeparator = options.keySeparator !== void 0 ? options.keySeparator : this.options.keySeparator; let namespaces = options.ns || this.options.defaultNS || []; const wouldCheckForNsInKey = nsSeparator && key.indexOf(nsSeparator) > -1; const seemsNaturalLanguage = !this.options.userDefinedKeySeparator && !options.keySeparator && !this.options.userDefinedNsSeparator && !options.nsSeparator && !looksLikeObjectPath(key, nsSeparator, keySeparator); if (wouldCheckForNsInKey && !seemsNaturalLanguage) { const m = key.match(this.interpolator.nestingRegexp); if (m && m.length > 0) { return { key, namespaces: isString(namespaces) ? [namespaces] : namespaces }; } const parts = key.split(nsSeparator); if (nsSeparator !== keySeparator || nsSeparator === keySeparator && this.options.ns.indexOf(parts[0]) > -1) namespaces = parts.shift(); key = parts.join(keySeparator); } return { key, namespaces: isString(namespaces) ? [namespaces] : namespaces }; } translate(keys, options, lastKey) { if (typeof options !== "object" && this.options.overloadTranslationOptionHandler) { options = this.options.overloadTranslationOptionHandler(arguments); } if (typeof options === "object") options = { ...options }; if (!options) options = {}; if (keys === void 0 || keys === null) return ""; if (!Array.isArray(keys)) keys = [String(keys)]; const returnDetails = options.returnDetails !== void 0 ? options.returnDetails : this.options.returnDetails; const keySeparator = options.keySeparator !== void 0 ? options.keySeparator : this.options.keySeparator; const { key, namespaces } = this.extractFromKey(keys[keys.length - 1], options); const namespace = namespaces[namespaces.length - 1]; const lng = options.lng || this.language; const appendNamespaceToCIMode = options.appendNamespaceToCIMode || this.options.appendNamespaceToCIMode; if (lng && lng.toLowerCase() === "cimode") { if (appendNamespaceToCIMode) { const nsSeparator = options.nsSeparator || this.options.nsSeparator; if (returnDetails) { return { res: `${namespace}${nsSeparator}${key}`, usedKey: key, exactUsedKey: key, usedLng: lng, usedNS: namespace, usedParams: this.getUsedParamsDetails(options) }; } return `${namespace}${nsSeparator}${key}`; } if (returnDetails) { return { res: key, usedKey: key, exactUsedKey: key, usedLng: lng, usedNS: namespace, usedParams: this.getUsedParamsDetails(options) }; } return key; } const resolved = this.resolve(keys, options); let res = resolved && resolved.res; const resUsedKey = resolved && resolved.usedKey || key; const resExactUsedKey = resolved && resolved.exactUsedKey || key; const resType = Object.prototype.toString.apply(res); const noObject = ["[object Number]", "[object Function]", "[object RegExp]"]; const joinArrays = options.joinArrays !== void 0 ? options.joinArrays : this.options.joinArrays; const handleAsObjectInI18nFormat = !this.i18nFormat || this.i18nFormat.handleAsObject; const handleAsObject = !isString(res) && typeof res !== "boolean" && typeof res !== "number"; if (handleAsObjectInI18nFormat && res && handleAsObject && noObject.indexOf(resType) < 0 && !(isString(joinArrays) && Array.isArray(res))) { if (!options.returnObjects && !this.options.returnObjects) { if (!this.options.returnedObjectHandler) { this.logger.warn("accessing an object - but returnObjects options is not enabled!"); } const r = this.options.returnedObjectHandler ? this.options.returnedObjectHandler(resUsedKey, res, { ...options, ns: namespaces }) : `key '${key} (${this.language})' returned an object instead of string.`; if (returnDetails) { resolved.res = r; resolved.usedParams = this.getUsedParamsDetails(options); return resolved; } return r; } if (keySeparator) { const resTypeIsArray = Array.isArray(res); const copy2 = resTypeIsArray ? [] : {}; const newKeyToUse = resTypeIsArray ? resExactUsedKey : resUsedKey; for (const m in res) { if (Object.prototype.hasOwnProperty.call(res, m)) { const deepKey = `${newKeyToUse}${keySeparator}${m}`; copy2[m] = this.translate(deepKey, { ...options, ...{ joinArrays: false, ns: namespaces } }); if (copy2[m] === deepKey) copy2[m] = res[m]; } } res = copy2; } } else if (handleAsObjectInI18nFormat && isString(joinArrays) && Array.isArray(res)) { res = res.join(joinArrays); if (res) res = this.extendTranslation(res, keys, options, lastKey); } else { let usedDefault = false; let usedKey = false; const needsPluralHandling = options.count !== void 0 && !isString(options.count); const hasDefaultValue = Translator.hasDefaultValue(options); const defaultValueSuffix = needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count, options) : ""; const defaultValueSuffixOrdinalFallback = options.ordinal && needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count, { ordinal: false }) : ""; const needsZeroSuffixLookup = needsPluralHandling && !options.ordinal && options.count === 0 && this.pluralResolver.shouldUseIntlApi(); const defaultValue = needsZeroSuffixLookup && options[`defaultValue${this.options.pluralSeparator}zero`] || options[`defaultValue${defaultValueSuffix}`] || options[`defaultValue${defaultValueSuffixOrdinalFallback}`] || options.defaultValue; if (!this.isValidLookup(res) && hasDefaultValue) { usedDefault = true; res = defaultValue; } if (!this.isValidLookup(res)) { usedKey = true; res = key; } const missingKeyNoValueFallbackToKey = options.missingKeyNoValueFallbackToKey || this.options.missingKeyNoValueFallbackToKey; const resForMissing = missingKeyNoValueFallbackToKey && usedKey ? void 0 : res; const updateMissing = hasDefaultValue && defaultValue !== res && this.options.updateMissing; if (usedKey || usedDefault || updateMissing) { this.logger.log(updateMissing ? "updateKey" : "missingKey", lng, namespace, key, updateMissing ? defaultValue : res); if (keySeparator) { const fk = this.resolve(key, { ...options, keySeparator: false }); if (fk && fk.res) this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format."); } let lngs = []; const fallbackLngs = this.languageUtils.getFallbackCodes(this.options.fallbackLng, options.lng || this.language); if (this.options.saveMissingTo === "fallback" && fallbackLngs && fallbackLngs[0]) { for (let i = 0; i < fallbackLngs.length; i++) { lngs.push(fallbackLngs[i]); } } else if (this.options.saveMissingTo === "all") { lngs = this.languageUtils.toResolveHierarchy(options.lng || this.language); } else { lngs.push(options.lng || this.language); } const send = (l, k, specificDefaultValue) => { const defaultForMissing = hasDefaultValue && specificDefaultValue !== res ? specificDefaultValue : resForMissing; if (this.options.missingKeyHandler) { this.options.missingKeyHandler(l, namespace, k, defaultForMissing, updateMissing, options); } else if (this.backendConnector && this.backendConnector.saveMissing) { this.backendConnector.saveMissing(l, namespace, k, defaultForMissing, updateMissing, options); } this.emit("missingKey", l, namespace, k, res); }; if (this.options.saveMissing) { if (this.options.saveMissingPlurals && needsPluralHandling) { lngs.forEach((language) => { const suffixes = this.pluralResolver.getSuffixes(language, options); if (needsZeroSuffixLookup && options[`defaultValue${this.options.pluralSeparator}zero`] && suffixes.indexOf(`${this.options.pluralSeparator}zero`) < 0) { suffixes.push(`${this.options.pluralSeparator}zero`); } suffixes.forEach((suffix) => { send([language], key + suffix, options[`defaultValue${suffix}`] || defaultValue); }); }); } else { send(lngs, key, defaultValue); } } } res = this.extendTranslation(res, keys, options, resolved, lastKey); if (usedKey && res === key && this.options.appendNamespaceToMissingKey) res = `${namespace}:${key}`; if ((usedKey || usedDefault) && this.options.parseMissingKeyHandler) { if (this.options.compatibilityAPI !== "v1") { res = this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey ? `${namespace}:${key}` : key, usedDefault ? res : void 0); } else { res = this.options.parseMissingKeyHandler(res); } } } if (returnDetails) { resolved.res = res; resolved.usedParams = this.getUsedParamsDetails(options); return resolved; } return res; } extendTranslation(res, key, options, resolved, lastKey) { var _this = this; if (this.i18nFormat && this.i18nFormat.parse) { res = this.i18nFormat.parse(res, { ...this.options.interpolation.defaultVariables, ...options }, options.lng || this.language || resolved.usedLng, resolved.usedNS, resolved.usedKey, { resolved }); } else if (!options.skipInterpolation) { if (options.interpolation) this.interpolator.init({ ...options, ...{ interpolation: { ...this.options.interpolation, ...options.interpolation } } }); const skipOnVariables = isString(res) && (options && options.interpolation && options.interpolation.skipOnVariables !== void 0 ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables); let nestBef; if (skipOnVariables) { const nb = res.match(this.interpolator.nestingRegexp); nestBef = nb && nb.length; } let data = options.replace && !isString(options.replace) ? options.replace : options; if (this.options.interpolation.defaultVariables) data = { ...this.options.interpolation.defaultVariables, ...data }; res = this.interpolator.interpolate(res, data, options.lng || this.language || resolved.usedLng, options); if (skipOnVariables) { const na = res.match(this.interpolator.nestingRegexp); const nestAft = na && na.length; if (nestBef < nestAft) options.nest = false; } if (!options.lng && this.options.compatibilityAPI !== "v1" && resolved && resolved.res) options.lng = this.language || resolved.usedLng; if (options.nest !== false) res = this.interpolator.nest(res, function() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (lastKey && lastKey[0] === args[0] && !options.context) { _this.logger.warn(`It seems you are nesting recursively key: ${args[0]} in key: ${key[0]}`); return null; } return _this.translate(...args, key); }, options); if (options.interpolation) this.interpolator.reset(); } const postProcess = options.postProcess || this.options.postProcess; const postProcessorNames = isString(postProcess) ? [postProcess] : postProcess; if (res !== void 0 && res !== null && postProcessorNames && postProcessorNames.length && options.applyPostProcessor !== false) { res = postProcessor.handle(postProcessorNames, res, key, this.options && this.options.postProcessPassResolved ? { i18nResolved: { ...resolved, usedParams: this.getUsedParamsDetails(options) }, ...options } : options, this); } return res; } resolve(keys) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; let found; let usedKey; let exactUsedKey; let usedLng; let usedNS; if (isString(keys)) keys = [keys]; keys.forEach((k) => { if (this.isValidLookup(found)) return; const extracted = this.extractFromKey(k, options); const key = extracted.key; usedKey = key; let namespaces = extracted.namespaces; if (this.options.fallbackNS) namespaces = namespaces.concat(this.options.fallbackNS); const needsPluralHandling = options.count !== void 0 && !isString(options.count); const needsZeroSuffixLookup = needsPluralHandling && !options.ordinal && options.count === 0 && this.pluralResolver.shouldUseIntlApi(); const needsContextHandling = options.context !== void 0 && (isString(options.context) || typeof options.context === "number") && options.context !== ""; const codes = options.lngs ? options.lngs : this.languageUtils.toResolveHierarchy(options.lng || this.language, options.fallbackLng); namespaces.forEach((ns) => { if (this.isValidLookup(found)) return; usedNS = ns; if (!checkedLoadedFor[`${codes[0]}-${ns}`] && this.utils && this.utils.hasLoadedNamespace && !this.utils.hasLoadedNamespace(usedNS)) { checkedLoadedFor[`${codes[0]}-${ns}`] = true; this.logger.warn(`key "${usedKey}" for languages "${codes.join(", ")}" won't get resolved as namespace "${usedNS}" was not yet loaded`, "This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!"); } codes.forEach((code) => { if (this.isValidLookup(found)) return; usedLng = code; const finalKeys = [key]; if (this.i18nFormat && this.i18nFormat.addLookupKeys) { this.i18nFormat.addLookupKeys(finalKeys, key, code, ns, options); } else { let pluralSuffix; if (needsPluralHandling) pluralSuffix = this.pluralResolver.getSuffix(code, options.count, options); const zeroSuffix = `${this.options.pluralSeparator}zero`; const ordinalPrefix = `${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`; if (needsPluralHandling) { finalKeys.push(key + pluralSuffix); if (options.ordinal && pluralSuffix.indexOf(ordinalPrefix) === 0) { finalKeys.push(key + pluralSuffix.replace(ordinalPrefix, this.options.pluralSeparator)); } if (needsZeroSuffixLookup) { finalKeys.push(key + zeroSuffix); } } if (needsContextHandling) { const contextKey = `${key}${this.options.contextSeparator}${options.context}`; finalKeys.push(contextKey); if (needsPluralHandling) { finalKeys.push(contextKey + pluralSuffix); if (options.ordinal && pluralSuffix.indexOf(ordinalPrefix) === 0) { finalKeys.push(contextKey + pluralSuffix.replace(ordinalPrefix, this.options.pluralSeparator)); } if (needsZeroSuffixLookup) { finalKeys.push(contextKey + zeroSuffix); } } } } let possibleKey; while (possibleKey = finalKeys.pop()) { if (!this.isValidLookup(found)) { exactUsedKey = possibleKey; found = this.getResource(code, ns, possibleKey, options); } } }); }); }); return { res: found, usedKey, exactUsedKey, usedLng, usedNS }; } isValidLookup(res) { return res !== void 0 && !(!this.options.returnNull && res === null) && !(!this.options.returnEmptyString && res === ""); } getResource(code, ns, key) { let options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {}; if (this.i18nFormat && this.i18nFormat.getResource) return this.i18nFormat.getResource(code, ns, key, options); return this.resourceStore.getResource(code, ns, key, options); } getUsedParamsDetails() { let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; const optionsKeys = ["defaultValue", "ordinal", "context", "replace", "lng", "lngs", "fallbackLng", "ns", "keySeparator", "nsSeparator", "returnObjects", "returnDetails", "joinArrays", "postProcess", "interpolation"]; const useOptionsReplaceForData = options.replace && !isString(options.replace); let data = useOptionsReplaceForData ? options.replace : options; if (useOptionsReplaceForData && typeof options.count !== "undefined") { data.count = options.count; } if (this.options.interpolation.defaultVariables) { data = { ...this.options.interpolation.defaultVariables, ...data }; } if (!useOptionsReplaceForData) { data = { ...data }; for (const key of optionsKeys) { delete data[key]; } } return data; } static hasDefaultValue(options) { const prefix = "defaultValue"; for (const option in options) { if (Object.prototype.hasOwnProperty.call(options, option) && prefix === option.substring(0, prefix.length) && void 0 !== options[option]) { return true; } } return false; } }; var capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1); var LanguageUtil = class { constructor(options) { this.options = options; this.supportedLngs = this.options.supportedLngs || false; this.logger = baseLogger.create("languageUtils"); } getScriptPartFromCode(code) { code = getCleanedCode(code); if (!code || code.indexOf("-") < 0) return null; const p = code.split("-"); if (p.length === 2) return null; p.pop(); if (p[p.length - 1].toLowerCase() === "x") return null; return this.formatLanguageCode(p.join("-")); } getLanguagePartFromCode(code) { code = getCleanedCode(code); if (!code || code.indexOf("-") < 0) return code; const p = code.split("-"); return this.formatLanguageCode(p[0]); } formatLanguageCode(code) { if (isString(code) && code.indexOf("-") > -1) { if (typeof Intl !== "undefined" && typeof Intl.getCanonicalLocales !== "undefined") { try { let formattedCode = Intl.getCanonicalLocales(code)[0]; if (formattedCode && this.options.lowerCaseLng) { formattedCode = formattedCode.toLowerCase(); } if (formattedCode) return formattedCode; } catch (e) { } } const specialCases = ["hans", "hant", "latn", "cyrl", "cans", "mong", "arab"]; let p = code.split("-"); if (this.options.lowerCaseLng) { p = p.map((part) => part.toLowerCase()); } else if (p.length === 2) { p[0] = p[0].toLowerCase(); p[1] = p[1].toUpperCase(); if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase()); } else if (p.length === 3) { p[0] = p[0].toLowerCase(); if (p[1].length === 2) p[1] = p[1].toUpperCase(); if (p[0] !== "sgn" && p[2].length === 2) p[2] = p[2].toUpperCase(); if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase()); if (specialCases.indexOf(p[2].toLowerCase()) > -1) p[2] = capitalize(p[2].toLowerCase()); } return p.join("-"); } return this.options.cleanCode || this.options.lowerCaseLng ? code.toLowerCase() : code; } isSupportedCode(code) { if (this.options.load === "languageOnly" || this.options.nonExplicitSupportedLngs) { code = this.getLanguagePartFromCode(code); } return !this.supportedLngs || !this.supportedLngs.length || this.supportedLngs.indexOf(code) > -1; } getBestMatchFromCodes(codes) { if (!codes) return null; let found; codes.forEach((code) => { if (found) return; const cleanedLng = this.formatLanguageCode(code); if (!this.options.supportedLngs || this.isSupportedCode(cleanedLng)) found = cleanedLng; }); if (!found && this.options.supportedLngs) { codes.forEach((code) => { if (found) return; const lngOnly = this.getLanguagePartFromCode(code); if (this.isSupportedCode(lngOnly)) return found = lngOnly; found = this.options.supportedLngs.find((supportedLng) => { if (supportedLng === lngOnly) return supportedLng; if (supportedLng.indexOf("-") < 0 && lngOnly.indexOf("-") < 0) return; if (supportedLng.indexOf("-") > 0 && lngOnly.indexOf("-") < 0 && supportedLng.substring(0, supportedLng.indexOf("-")) === lngOnly) return supportedLng; if (supportedLng.indexOf(lngOnly) === 0 && lngOnly.length > 1) return supportedLng; }); }); } if (!found) found = this.getFallbackCodes(this.options.fallbackLng)[0]; return found; } getFallbackCodes(fallbacks, code) { if (!fallbacks) return []; if (typeof fallbacks === "function") fallbacks = fallbacks(code); if (isString(fallbacks)) fallbacks = [fallbacks]; if (Array.isArray(fallbacks)) return fallbacks; if (!code) return fallbacks.default || []; let found = fallbacks[code]; if (!found) found = fallbacks[this.getScriptPartFromCode(code)]; if (!found) found = fallbacks[this.formatLanguageCode(code)]; if (!found) found = fallbacks[this.getLanguagePartFromCode(code)]; if (!found) found = fallbacks.default; return found || []; } toResolveHierarchy(code, fallbackCode) { const fallbackCodes = this.getFallbackCodes(fallbackCode || this.options.fallbackLng || [], code); const codes = []; const addCode = (c) => { if (!c) return; if (this.isSupportedCode(c)) { codes.push(c); } else { this.logger.warn(`rejecting language code not found in supportedLngs: ${c}`); } }; if (isString(code) && (code.indexOf("-") > -1 || code.indexOf("_") > -1)) { if (this.options.load !== "languageOnly") addCode(this.formatLanguageCode(code)); if (this.options.load !== "languageOnly" && this.options.load !== "currentOnly") addCode(this.getScriptPartFromCode(code)); if (this.options.load !== "currentOnly") addCode(this.getLanguagePartFromCode(code)); } else if (isString(code)) { addCode(this.formatLanguageCode(code)); } fallbackCodes.forEach((fc) => { if (codes.indexOf(fc) < 0) addCode(this.formatLanguageCode(fc)); }); return codes; } }; var sets = [{ lngs: ["ach", "ak", "am", "arn", "br", "fil", "gun", "ln", "mfe", "mg", "mi", "oc", "pt", "pt-BR", "tg", "tl", "ti", "tr", "uz", "wa"], nr: [1, 2], fc: 1 }, { lngs: ["af", "an", "ast", "az", "bg", "bn", "ca", "da", "de", "dev", "el", "en", "eo", "es", "et", "eu", "fi", "fo", "fur", "fy", "gl", "gu", "ha", "hi", "hu", "hy", "ia", "it", "kk", "kn", "ku", "lb", "mai", "ml", "mn", "mr", "nah", "nap", "nb", "ne", "nl", "nn", "no", "nso", "pa", "pap", "pms", "ps", "pt-PT", "rm", "sco", "se", "si", "so", "son", "sq", "sv", "sw", "ta", "te", "tk", "ur", "yo"], nr: [1, 2], fc: 2 }, { lngs: ["ay", "bo", "cgg", "fa", "ht", "id", "ja", "jbo", "ka", "km", "ko", "ky", "lo", "ms", "sah", "su", "th", "tt", "ug", "vi", "wo", "zh"], nr: [1], fc: 3 }, { lngs: ["be", "bs", "cnr", "dz", "hr", "ru", "sr", "uk"], nr: [1, 2, 5], fc: 4 }, { lngs: ["ar"], nr: [0, 1, 2, 3, 11, 100], fc: 5 }, { lngs: ["cs", "sk"], nr: [1, 2, 5], fc: 6 }, { lngs: ["csb", "pl"], nr: [1, 2, 5], fc: 7 }, { lngs: ["cy"], nr: [1, 2, 3, 8], fc: 8 }, { lngs: ["fr"], nr: [1, 2], fc: 9 }, { lngs: ["ga"], nr: [1, 2, 3, 7, 11], fc: 10 }, { lngs: ["gd"], nr: [1, 2, 3, 20], fc: 11 }, { lngs: ["is"], nr: [1, 2], fc: 12 }, { lngs: ["jv"], nr: [0, 1], fc: 13 }, { lngs: ["kw"], nr: [1, 2, 3, 4], fc: 14 }, { lngs: ["lt"], nr: [1, 2, 10], fc: 15 }, { lngs: ["lv"], nr: [1, 2, 0], fc: 16 }, { lngs: ["mk"], nr: [1, 2], fc: 17 }, { lngs: ["mnk"], nr: [0, 1, 2], fc: 18 }, { lngs: ["mt"], nr: [1, 2, 11, 20], fc: 19 }, { lngs: ["or"], nr: [2, 1], fc: 2 }, { lngs: ["ro"], nr: [1, 2, 20], fc: 20 }, { lngs: ["sl"], nr: [5, 1, 2, 3], fc: 21 }, { lngs: ["he", "iw"], nr: [1, 2, 20, 21], fc: 22 }]; var _rulesPluralsTypes = { 1: (n) => Number(n > 1), 2: (n) => Number(n != 1), 3: (n) => 0, 4: (n) => Number(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2), 5: (n) => Number(n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5), 6: (n) => Number(n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2), 7: (n) => Number(n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2), 8: (n) => Number(n == 1 ? 0 : n == 2 ? 1 : n != 8 && n != 11 ? 2 : 3), 9: (n) => Number(n >= 2), 10: (n) => Number(n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4), 11: (n) => Number(n == 1 || n == 11 ? 0 : n == 2 || n == 12 ? 1 : n > 2 && n < 20 ? 2 : 3), 12: (n) => Number(n % 10 != 1 || n % 100 == 11), 13: (n) => Number(n !== 0), 14: (n) => Number(n == 1 ? 0 : n == 2 ? 1 : n == 3 ? 2 : 3), 15: (n) => Number(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2), 16: (n) => Number(n % 10 == 1 && n % 100 != 11 ? 0 : n !== 0 ? 1 : 2), 17: (n) => Number(n == 1 || n % 10 == 1 && n % 100 != 11 ? 0 : 1), 18: (n) => Number(n == 0 ? 0 : n == 1 ? 1 : 2), 19: (n) => Number(n == 1 ? 0 : n == 0 || n % 100 > 1 && n % 100 < 11 ? 1 : n % 100 > 10 && n % 100 < 20 ? 2 : 3), 20: (n) => Number(n == 1 ? 0 : n == 0 || n % 100 > 0 && n % 100 < 20 ? 1 : 2), 21: (n) => Number(n % 100 == 1 ? 1 : n % 100 == 2 ? 2 : n % 100 == 3 || n % 100 == 4 ? 3 : 0), 22: (n) => Number(n == 1 ? 0 : n == 2 ? 1 : (n < 0 || n > 10) && n % 10 == 0 ? 2 : 3) }; var nonIntlVersions = ["v1", "v2", "v3"]; var intlVersions = ["v4"]; var suffixesOrder = { zero: 0, one: 1, two: 2, few: 3, many: 4, other: 5 }; var createRules = () => { const rules = {}; sets.forEach((set) => { set.lngs.forEach((l) => { rules[l] = { numbers: set.nr, plurals: _rulesPluralsTypes[set.fc] }; }); }); return rules; }; var PluralResolver = class { constructor(languageUtils) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; this.languageUtils = languageUtils; this.options = options; this.logger = baseLogger.create("pluralResolver"); if ((!this.options.compatibilityJSON || intlVersions.includes(this.options.compatibilityJSON)) && (typeof Intl === "undefined" || !Intl.PluralRules)) { this.options.compatibilityJSON = "v3"; this.logger.error("Your environment seems not to be Intl API compatible, use an Intl.PluralRules polyfill. Will fallback to the compatibilityJSON v3 format handling."); } this.rules = createRules(); this.pluralRulesCache = {}; } addRule(lng, obj) { this.rules[lng] = obj; } clearCache() { this.pluralRulesCache = {}; } getRule(code) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; if (this.shouldUseIntlApi()) { const cleanedCode = getCleanedCode(code === "dev" ? "en" : code); const type = options.ordinal ? "ordinal" : "cardinal"; const cacheKey = JSON.stringify({ cleanedCode, type }); if (cacheKey in this.pluralRulesCache) { return this.pluralRulesCache[cacheKey]; } let rule; try { rule = new Intl.PluralRules(cleanedCode, { type }); } catch (err) { if (!code.match(/-|_/)) return; const lngPart = this.languageUtils.getLanguagePartFromCode(code); rule = this.getRule(lngPart, options); } this.pluralRulesCache[cacheKey] = rule; return rule; } return this.rules[code] || this.rules[this.languageUtils.getLanguagePartFromCode(code)]; } needsPlural(code) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; const rule = this.getRule(code, options); if (this.shouldUseIntlApi()) { return rule && rule.resolvedOptions().pluralCategories.length > 1; } return rule && rule.numbers.length > 1; } getPluralFormsOfKey(code, key) { let options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}; return this.getSuffixes(code, options).map((suffix) => `${key}${suffix}`); } getSuffixes(code) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; const rule = this.getRule(code, options); if (!rule) { return []; } if (this.shouldUseIntlApi()) { return rule.resolvedOptions().pluralCategories.sort((pluralCategory1, pluralCategory2) => suffixesOrder[pluralCategory1] - suffixesOrder[pluralCategory2]).map((pluralCategory) => `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ""}${pluralCategory}`); } return rule.numbers.map((number) => this.getSuffix(code, number, options)); } getSuffix(code, count) { let options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}; const rule = this.getRule(code, options); if (rule) { if (this.shouldUseIntlApi()) { return `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ""}${rule.select(count)}`; } return this.getSuffixRetroCompatible(rule, count); } this.logger.warn(`no plural rule found for: ${code}`); return ""; } getSuffixRetroCompatible(rule, count) { const idx = rule.noAbs ? rule.plurals(count) : rule.plurals(Math.abs(count)); let suffix = rule.numbers[idx]; if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) { if (suffix === 2) { suffix = "plural"; } else if (suffix === 1) { suffix = ""; } } const returnSuffix = () => this.options.prepend && suffix.toString() ? this.options.prepend + suffix.toString() : suffix.toString(); if (this.options.compatibilityJSON === "v1") { if (suffix === 1) return ""; if (typeof suffix === "number") return `_plural_${suffix.toString()}`; return returnSuffix(); } else if (this.options.compatibilityJSON === "v2") { return returnSuffix(); } else if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) { return returnSuffix(); } return this.options.prepend && idx.toString() ? this.options.prepend + idx.toString() : idx.toString(); } shouldUseIntlApi() { return !nonIntlVersions.includes(this.options.compatibilityJSON); } }; var deepFindWithDefaults = function(data, defaultData, key) { let keySeparator = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : "."; let ignoreJSONStructure = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : true; let path = getPathWithDefaults(data, defaultData, key); if (!path && ignoreJSONStructure && isString(key)) { path = deepFind(data, key, keySeparator); if (path === void 0) path = deepFind(defaultData, key, keySeparator); } return path; }; var regexSafe = (val) => val.replace(/\$/g, "$$$$"); var Interpolator = class { constructor() { let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; this.logger = baseLogger.create("interpolator"); this.options = options; this.format = options.interpolation && options.interpolation.format || ((value) => value); this.init(options); } init() { let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; if (!options.interpolation) options.interpolation = { escapeValue: true }; const { escape: escape$1, escapeValue, useRawValueToEscape, prefix, prefixEscaped, suffix, suffixEscaped, formatSeparator, unescapeSuffix, unescapePrefix, nestingPrefix, nestingPrefixEscaped, nestingSuffix, nestingSuffixEscaped, nestingOptionsSeparator, maxReplaces, alwaysFormat } = options.interpolation; this.escape = escape$1 !== void 0 ? escape$1 : escape; this.escapeValue = escapeValue !== void 0 ? escapeValue : true; this.useRawValueToEscape = useRawValueToEscape !== void 0 ? useRawValueToEscape : false; this.prefix = prefix ? regexEscape(prefix) : prefixEscaped || "{{"; this.suffix = suffix ? regexEscape(suffix) : suffixEscaped || "}}"; this.formatSeparator = formatSeparator || ","; this.unescapePrefix = unescapeSuffix ? "" : unescapePrefix || "-"; this.unescapeSuffix = this.unescapePrefix ? "" : unescapeSuffix || ""; this.nestingPrefix = nestingPrefix ? regexEscape(nestingPrefix) : nestingPrefixEscaped || regexEscape("$t("); this.nestingSuffix = nestingSuffix ? regexEscape(nestingSuffix) : nestingSuffixEscaped || regexEscape(")"); this.nestingOptionsSeparator = nestingOptionsSeparator || ","; this.maxReplaces = maxReplaces || 1e3; this.alwaysFormat = alwaysFormat !== void 0 ? alwaysFormat : false; this.resetRegExp(); } reset() { if (this.options) this.init(this.options); } resetRegExp() { const getOrResetRegExp = (existingRegExp, pattern) => { if (existingRegExp && existingRegExp.source === pattern) { existingRegExp.lastIndex = 0; return existingRegExp; } return new RegExp(pattern, "g"); }; this.regexp = getOrResetRegExp(this.regexp, `${this.prefix}(.+?)${this.suffix}`); this.regexpUnescape = getOrResetRegExp(this.regexpUnescape, `${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`); this.nestingRegexp = getOrResetRegExp(this.nestingRegexp, `${this.nestingPrefix}(.+?)${this.nestingSuffix}`); } interpolate(str, data, lng, options) { let match; let value; let replaces; const defaultData = this.options && this.options.interpolation && this.options.interpolation.defaultVariables || {}; const handleFormat = (key) => { if (key.indexOf(this.formatSeparator) < 0) { const path = deepFindWithDefaults(data, defaultData, key, this.options.keySeparator, this.options.ignoreJSONStructure); return this.alwaysFormat ? this.format(path, void 0, lng, { ...options, ...data, interpolationkey: key }) : path; } const p = key.split(this.formatSeparator); const k = p.shift().trim(); const f = p.join(this.formatSeparator).trim(); return this.format(deepFindWithDefaults(data, defaultData, k, this.options.keySeparator, this.options.ignoreJSONStructure), f, lng, { ...options, ...data, interpolationkey: k }); }; this.resetRegExp(); const missingInterpolationHandler = options && options.missingInterpolationHandler || this.options.missingInterpolationHandler; const skipOnVariables = options && options.interpolation && options.interpolation.skipOnVariables !== void 0 ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables; const todos = [{ regex: this.regexpUnescape, safeValue: (val) => regexSafe(val) }, { regex: this.regexp, safeValue: (val) => this.escapeValue ? regexSafe(this.escape(val)) : regexSafe(val) }]; todos.forEach((todo) => { replaces = 0; while (match = todo.regex.exec(str)) { const matchedVar = match[1].trim(); value = handleFormat(matchedVar); if (value === void 0) { if (typeof missingInterpolationHandler === "function") { const temp = missingInterpolationHandler(str, match, options); value = isString(temp) ? temp : ""; } else if (options && Object.prototype.hasOwnProperty.call(options, matchedVar)) { value = ""; } else if (skipOnVariables) { value = match[0]; continue; } else { this.logger.warn(`missed to pass in variable ${matchedVar} for interpolating ${str}`); value = ""; } } else if (!isString(value) && !this.useRawValueToEscape) { value = makeString(value); } const safeValue = todo.safeValue(value); str = str.replace(match[0], safeValue); if (skipOnVariables) { todo.regex.lastIndex += value.length; todo.regex.lastIndex -= match[0].length; } else { todo.regex.lastIndex = 0; } replaces++; if (replaces >= this.maxReplaces) { break; } } }); return str; } nest(str, fc) { let options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}; let match; let value; let clonedOptions; const handleHasOptions = (key, inheritedOptions) => { const sep = this.nestingOptionsSeparator; if (key.indexOf(sep) < 0) return key; const c = key.split(new RegExp(`${sep}[ ]*{`)); let optionsString = `{${c[1]}`; key = c[0]; optionsString = this.interpolate(optionsString, clonedOptions); const matchedSingleQuotes = optionsString.match(/'/g); const matchedDoubleQuotes = optionsString.match(/"/g); if (matchedSingleQuotes && matchedSingleQuotes.length % 2 === 0 && !matchedDoubleQuotes || matchedDoubleQuotes.length % 2 !== 0) { optionsString = optionsString.replace(/'/g, '"'); } try { clonedOptions = JSON.parse(optionsString); if (inheritedOptions) clonedOptions = { ...inheritedOptions, ...clonedOptions }; } catch (e) { this.logger.warn(`failed parsing options string in nesting for key ${key}`, e); return `${key}${sep}${optionsString}`; } if (clonedOptions.defaultValue && clonedOptions.defaultValue.indexOf(this.prefix) > -1) delete clonedOptions.defaultValue; return key; }; while (match = this.nestingRegexp.exec(str)) { let formatters = []; clonedOptions = { ...options }; clonedOptions = clonedOptions.replace && !isString(clonedOptions.replace) ? clonedOptions.replace : clonedOptions; clonedOptions.applyPostProcessor = false; delete clonedOptions.defaultValue; let doReduce = false; if (match[0].indexOf(this.formatSeparator) !== -1 && !/{.*}/.test(match[1])) { const r = match[1].split(this.formatSeparator).map((elem) => elem.trim()); match[1] = r.shift(); formatters = r; doReduce = true; } value = fc(handleHasOptions.call(this, match[1].trim(), clonedOptions), clonedOptions); if (value && match[0] === str && !isString(value)) return value; if (!isString(value)) value = makeString(value); if (!value) { this.logger.warn(`missed to resolve ${match[1]} for nesting ${str}`); value = ""; } if (doReduce) { value = formatters.reduce((v, f) => this.format(v, f, options.lng, { ...options, interpolationkey: match[1].trim() }), value.trim()); } str = str.replace(match[0], value); this.regexp.lastIndex = 0; } return str; } }; var parseFormatStr = (formatStr) => { let formatName = formatStr.toLowerCase().trim(); const formatOptions = {}; if (formatStr.indexOf("(") > -1) { const p = formatStr.split("("); formatName = p[0].toLowerCase().trim(); const optStr = p[1].substring(0, p[1].length - 1); if (formatName === "currency" && optStr.indexOf(":") < 0) { if (!formatOptions.currency) formatOptions.currency = optStr.trim(); } else if (formatName === "relativetime" && optStr.indexOf(":") < 0) { if (!formatOptions.range) formatOptions.range = optStr.trim(); } else { const opts = optStr.split(";"); opts.forEach((opt) => { if (opt) { const [key, ...rest] = opt.split(":"); const val = rest.join(":").trim().replace(/^'+|'+$/g, ""); const trimmedKey = key.trim(); if (!formatOptions[trimmedKey]) formatOptions[trimmedKey] = val; if (val === "false") formatOptions[trimmedKey] = false; if (val === "true") formatOptions[trimmedKey] = true; if (!isNaN(val)) formatOptions[trimmedKey] = parseInt(val, 10); } }); } } return { formatName, formatOptions }; }; var createCachedFormatter = (fn) => { const cache = {}; return (val, lng, options) => { let optForCache = options; if (options && options.interpolationkey && options.formatParams && options.formatParams[options.interpolationkey] && options[options.interpolationkey]) { optForCache = { ...optForCache, [options.interpolationkey]: void 0 }; } const key = lng + JSON.stringify(optForCache); let formatter = cache[key]; if (!formatter) { formatter = fn(getCleanedCode(lng), options); cache[key] = formatter; } return formatter(val); }; }; var Formatter = class { constructor() { let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; this.logger = baseLogger.create("formatter"); this.options = options; this.formats = { number: createCachedFormatter((lng, opt) => { const formatter = new Intl.NumberFormat(lng, { ...opt }); return (val) => formatter.format(val); }), currency: createCachedFormatter((lng, opt) => { const formatter = new Intl.NumberFormat(lng, { ...opt, style: "currency" }); return (val) => formatter.format(val); }), datetime: createCachedFormatter((lng, opt) => { const formatter = new Intl.DateTimeFormat(lng, { ...opt }); return (val) => formatter.format(val); }), relativetime: createCachedFormatter((lng, opt) => { const formatter = new Intl.RelativeTimeFormat(lng, { ...opt }); return (val) => formatter.format(val, opt.range || "day"); }), list: createCachedFormatter((lng, opt) => { const formatter = new Intl.ListFormat(lng, { ...opt }); return (val) => formatter.format(val); }) }; this.init(options); } init(services) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : { interpolation: {} }; this.formatSeparator = options.interpolation.formatSeparator || ","; } add(name, fc) { this.formats[name.toLowerCase().trim()] = fc; } addCached(name, fc) { this.formats[name.toLowerCase().trim()] = createCachedFormatter(fc); } format(value, format, lng) { let options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {}; const formats = format.split(this.formatSeparator); if (formats.length > 1 && formats[0].indexOf("(") > 1 && formats[0].indexOf(")") < 0 && formats.find((f) => f.indexOf(")") > -1)) { const lastIndex = formats.findIndex((f) => f.indexOf(")") > -1); formats[0] = [formats[0], ...formats.splice(1, lastIndex)].join(this.formatSeparator); } const result = formats.reduce((mem, f) => { const { formatName, formatOptions } = parseFormatStr(f); if (this.formats[formatName]) { let formatted = mem; try { const valOptions = options && options.formatParams && options.formatParams[options.interpolationkey] || {}; const l = valOptions.locale || valOptions.lng || options.locale || options.lng || lng; formatted = this.formats[formatName](mem, l, { ...formatOptions, ...options, ...valOptions }); } catch (error) { this.logger.warn(error); } return formatted; } else { this.logger.warn(`there was no format function for ${formatName}`); } return mem; }, value); return result; } }; var removePending = (q, name) => { if (q.pending[name] !== void 0) { delete q.pending[name]; q.pendingCount--; } }; var Connector = class extends EventEmitter { constructor(backend, store, services) { let options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {}; super(); this.backend = backend; this.store = store; this.services = services; this.languageUtils = services.languageUtils; this.options = options; this.logger = baseLogger.create("backendConnector"); this.waitingReads = []; this.maxParallelReads = options.maxParallelReads || 10; this.readingCalls = 0; this.maxRetries = options.maxRetries >= 0 ? options.maxRetries : 5; this.retryTimeout = options.retryTimeout >= 1 ? options.retryTimeout : 350; this.state = {}; this.queue = []; if (this.backend && this.backend.init) { this.backend.init(services, options.backend, options); } } queueLoad(languages, namespaces, options, callback) { const toLoad = {}; const pending = {}; const toLoadLanguages = {}; const toLoadNamespaces = {}; languages.forEach((lng) => { let hasAllNamespaces = true; namespaces.forEach((ns) => { const name = `${lng}|${ns}`; if (!options.reload && this.store.hasResourceBundle(lng, ns)) { this.state[name] = 2; } else if (this.state[name] < 0) ; else if (this.state[name] === 1) { if (pending[name] === void 0) pending[name] = true; } else { this.state[name] = 1; hasAllNamespaces = false; if (pending[name] === void 0) pending[name] = true; if (toLoad[name] === void 0) toLoad[name] = true; if (toLoadNamespaces[ns] === void 0) toLoadNamespaces[ns] = true; } }); if (!hasAllNamespaces) toLoadLanguages[lng] = true; }); if (Object.keys(toLoad).length || Object.keys(pending).length) { this.queue.push({ pending, pendingCount: Object.keys(pending).length, loaded: {}, errors: [], callback }); } return { toLoad: Object.keys(toLoad), pending: Object.keys(pending), toLoadLanguages: Object.keys(toLoadLanguages), toLoadNamespaces: Object.keys(toLoadNamespaces) }; } loaded(name, err, data) { const s = name.split("|"); const lng = s[0]; const ns = s[1]; if (err) this.emit("failedLoading", lng, ns, err); if (!err && data) { this.store.addResourceBundle(lng, ns, data, void 0, void 0, { skipCopy: true }); } this.state[name] = err ? -1 : 2; if (err && data) this.state[name] = 0; const loaded = {}; this.queue.forEach((q) => { pushPath(q.loaded, [lng], ns); removePending(q, name); if (err) q.errors.push(err); if (q.pendingCount === 0 && !q.done) { Object.keys(q.loaded).forEach((l) => { if (!loaded[l]) loaded[l] = {}; const loadedKeys = q.loaded[l]; if (loadedKeys.length) { loadedKeys.forEach((n) => { if (loaded[l][n] === void 0) loaded[l][n] = true; }); } }); q.done = true; if (q.errors.length) { q.callback(q.errors); } else { q.callback(); } } }); this.emit("loaded", loaded); this.queue = this.queue.filter((q) => !q.done); } read(lng, ns, fcName) { let tried = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : 0; let wait = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : this.retryTimeout; let callback = arguments.length > 5 ? arguments[5] : void 0; if (!lng.length) return callback(null, {}); if (this.readingCalls >= this.maxParallelReads) { this.waitingReads.push({ lng, ns, fcName, tried, wait, callback }); return; } this.readingCalls++; const resolver = (err, data) => { this.readingCalls--; if (this.waitingReads.length > 0) { const next = this.waitingReads.shift(); this.read(next.lng, next.ns, next.fcName, next.tried, next.wait, next.callback); } if (err && data && tried < this.maxRetries) { setTimeout(() => { this.read.call(this, lng, ns, fcName, tried + 1, wait * 2, callback); }, wait); return; } callback(err, data); }; const fc = this.backend[fcName].bind(this.backend); if (fc.length === 2) { try { const r = fc(lng, ns); if (r && typeof r.then === "function") { r.then((data) => resolver(null, data)).catch(resolver); } else { resolver(null, r); } } catch (err) { resolver(err); } return; } return fc(lng, ns, resolver); } prepareLoading(languages, namespaces) { let options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}; let callback = arguments.length > 3 ? arguments[3] : void 0; if (!this.backend) { this.logger.warn("No backend was added via i18next.use. Will not load resources."); return callback && callback(); } if (isString(languages)) languages = this.languageUtils.toResolveHierarchy(languages); if (isString(namespaces)) namespaces = [namespaces]; const toLoad = this.queueLoad(languages, namespaces, options, callback); if (!toLoad.toLoad.length) { if (!toLoad.pending.length) callback(); return null; } toLoad.toLoad.forEach((name) => { this.loadOne(name); }); } load(languages, namespaces, callback) { this.prepareLoading(languages, namespaces, {}, callback); } reload(languages, namespaces, callback) { this.prepareLoading(languages, namespaces, { reload: true }, callback); } loadOne(name) { let prefix = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : ""; const s = name.split("|"); const lng = s[0]; const ns = s[1]; this.read(lng, ns, "read", void 0, void 0, (err, data) => { if (err) this.logger.warn(`${prefix}loading namespace ${ns} for language ${lng} failed`, err); if (!err && data) this.logger.log(`${prefix}loaded namespace ${ns} for language ${lng}`, data); this.loaded(name, err, data); }); } saveMissing(languages, namespace, key, fallbackValue, isUpdate) { let options = arguments.length > 5 && arguments[5] !== void 0 ? arguments[5] : {}; let clb = arguments.length > 6 && arguments[6] !== void 0 ? arguments[6] : () => { }; if (this.services.utils && this.services.utils.hasLoadedNamespace && !this.services.utils.hasLoadedNamespace(namespace)) { this.logger.warn(`did not save key "${key}" as the namespace "${namespace}" was not yet loaded`, "This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!"); return; } if (key === void 0 || key === null || key === "") return; if (this.backend && this.backend.create) { const opts = { ...options, isUpdate }; const fc = this.backend.create.bind(this.backend); if (fc.length < 6) { try { let r; if (fc.length === 5) { r = fc(languages, namespace, key, fallbackValue, opts); } else { r = fc(languages, namespace, key, fallbackValue); } if (r && typeof r.then === "function") { r.then((data) => clb(null, data)).catch(clb); } else { clb(null, r); } } catch (err) { clb(err); } } else { fc(languages, namespace, key, fallbackValue, clb, opts); } } if (!languages || !languages[0]) return; this.store.addResource(languages[0], namespace, key, fallbackValue); } }; var get = () => ({ debug: false, initImmediate: true, ns: ["translation"], defaultNS: ["translation"], fallbackLng: ["dev"], fallbackNS: false, supportedLngs: false, nonExplicitSupportedLngs: false, load: "all", preload: false, simplifyPluralSuffix: true, keySeparator: ".", nsSeparator: ":", pluralSeparator: "_", contextSeparator: "_", partialBundledLanguages: false, saveMissing: false, updateMissing: false, saveMissingTo: "fallback", saveMissingPlurals: true, missingKeyHandler: false, missingInterpolationHandler: false, postProcess: false, postProcessPassResolved: false, returnNull: false, returnEmptyString: true, returnObjects: false, joinArrays: false, returnedObjectHandler: false, parseMissingKeyHandler: false, appendNamespaceToMissingKey: false, appendNamespaceToCIMode: false, overloadTranslationOptionHandler: (args) => { let ret = {}; if (typeof args[1] === "object") ret = args[1]; if (isString(args[1])) ret.defaultValue = args[1]; if (isString(args[2])) ret.tDescription = args[2]; if (typeof args[2] === "object" || typeof args[3] === "object") { const options = args[3] || args[2]; Object.keys(options).forEach((key) => { ret[key] = options[key]; }); } return ret; }, interpolation: { escapeValue: true, format: (value) => value, prefix: "{{", suffix: "}}", formatSeparator: ",", unescapePrefix: "-", nestingPrefix: "$t(", nestingSuffix: ")", nestingOptionsSeparator: ",", maxReplaces: 1e3, skipOnVariables: true } }); var transformOptions = (options) => { if (isString(options.ns)) options.ns = [options.ns]; if (isString(options.fallbackLng)) options.fallbackLng = [options.fallbackLng]; if (isString(options.fallbackNS)) options.fallbackNS = [options.fallbackNS]; if (options.supportedLngs && options.supportedLngs.indexOf("cimode") < 0) { options.supportedLngs = options.supportedLngs.concat(["cimode"]); } return options; }; var noop = () => { }; var bindMemberFunctions = (inst) => { const mems = Object.getOwnPropertyNames(Object.getPrototypeOf(inst)); mems.forEach((mem) => { if (typeof inst[mem] === "function") { inst[mem] = inst[mem].bind(inst); } }); }; var I18n = class extends EventEmitter { constructor() { let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; let callback = arguments.length > 1 ? arguments[1] : void 0; super(); this.options = transformOptions(options); this.services = {}; this.logger = baseLogger; this.modules = { external: [] }; bindMemberFunctions(this); if (callback && !this.isInitialized && !options.isClone) { if (!this.options.initImmediate) { this.init(options, callback); return this; } setTimeout(() => { this.init(options, callback); }, 0); } } init() { var _this = this; let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; let callback = arguments.length > 1 ? arguments[1] : void 0; this.isInitializing = true; if (typeof options === "function") { callback = options; options = {}; } if (!options.defaultNS && options.defaultNS !== false && options.ns) { if (isString(options.ns)) { options.defaultNS = options.ns; } else if (options.ns.indexOf("translation") < 0) { options.defaultNS = options.ns[0]; } } const defOpts = get(); this.options = { ...defOpts, ...this.options, ...transformOptions(options) }; if (this.options.compatibilityAPI !== "v1") { this.options.interpolation = { ...defOpts.interpolation, ...this.options.interpolation }; } if (options.keySeparator !== void 0) { this.options.userDefinedKeySeparator = options.keySeparator; } if (options.nsSeparator !== void 0) { this.options.userDefinedNsSeparator = options.nsSeparator; } const createClassOnDemand = (ClassOrObject) => { if (!ClassOrObject) return null; if (typeof ClassOrObject === "function") return new ClassOrObject(); return ClassOrObject; }; if (!this.options.isClone) { if (this.modules.logger) { baseLogger.init(createClassOnDemand(this.modules.logger), this.options); } else { baseLogger.init(null, this.options); } let formatter; if (this.modules.formatter) { formatter = this.modules.formatter; } else if (typeof Intl !== "undefined") { formatter = Formatter; } const lu = new LanguageUtil(this.options); this.store = new ResourceStore(this.options.resources, this.options); const s = this.services; s.logger = baseLogger; s.resourceStore = this.store; s.languageUtils = lu; s.pluralResolver = new PluralResolver(lu, { prepend: this.options.pluralSeparator, compatibilityJSON: this.options.compatibilityJSON, simplifyPluralSuffix: this.options.simplifyPluralSuffix }); if (formatter && (!this.options.interpolation.format || this.options.interpolation.format === defOpts.interpolation.format)) { s.formatter = createClassOnDemand(formatter); s.formatter.init(s, this.options); this.options.interpolation.format = s.formatter.format.bind(s.formatter); } s.interpolator = new Interpolator(this.options); s.utils = { hasLoadedNamespace: this.hasLoadedNamespace.bind(this) }; s.backendConnector = new Connector(createClassOnDemand(this.modules.backend), s.resourceStore, s, this.options); s.backendConnector.on("*", function(event) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } _this.emit(event, ...args); }); if (this.modules.languageDetector) { s.languageDetector = createClassOnDemand(this.modules.languageDetector); if (s.languageDetector.init) s.languageDetector.init(s, this.options.detection, this.options); } if (this.modules.i18nFormat) { s.i18nFormat = createClassOnDemand(this.modules.i18nFormat); if (s.i18nFormat.init) s.i18nFormat.init(this); } this.translator = new Translator(this.services, this.options); this.translator.on("*", function(event) { for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } _this.emit(event, ...args); }); this.modules.external.forEach((m) => { if (m.init) m.init(this); }); } this.format = this.options.interpolation.format; if (!callback) callback = noop; if (this.options.fallbackLng && !this.services.languageDetector && !this.options.lng) { const codes = this.services.languageUtils.getFallbackCodes(this.options.fallbackLng); if (codes.length > 0 && codes[0] !== "dev") this.options.lng = codes[0]; } if (!this.services.languageDetector && !this.options.lng) { this.logger.warn("init: no languageDetector is used and no lng is defined"); } const storeApi = ["getResource", "hasResourceBundle", "getResourceBundle", "getDataByLanguage"]; storeApi.forEach((fcName) => { this[fcName] = function() { return _this.store[fcName](...arguments); }; }); const storeApiChained = ["addResource", "addResources", "addResourceBundle", "removeResourceBundle"]; storeApiChained.forEach((fcName) => { this[fcName] = function() { _this.store[fcName](...arguments); return _this; }; }); const deferred = defer(); const load = () => { const finish = (err, t2) => { this.isInitializing = false; if (this.isInitialized && !this.initializedStoreOnce) this.logger.warn("init: i18next is already initialized. You should call init just once!"); this.isInitialized = true; if (!this.options.isClone) this.logger.log("initialized", this.options); this.emit("initialized", this.options); deferred.resolve(t2); callback(err, t2); }; if (this.languages && this.options.compatibilityAPI !== "v1" && !this.isInitialized) return finish(null, this.t.bind(this)); this.changeLanguage(this.options.lng, finish); }; if (this.options.resources || !this.options.initImmediate) { load(); } else { setTimeout(load, 0); } return deferred; } loadResources(language) { let callback = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop; let usedCallback = callback; const usedLng = isString(language) ? language : this.language; if (typeof language === "function") usedCallback = language; if (!this.options.resources || this.options.partialBundledLanguages) { if (usedLng && usedLng.toLowerCase() === "cimode" && (!this.options.preload || this.options.preload.length === 0)) return usedCallback(); const toLoad = []; const append = (lng) => { if (!lng) return; if (lng === "cimode") return; const lngs = this.services.languageUtils.toResolveHierarchy(lng); lngs.forEach((l) => { if (l === "cimode") return; if (toLoad.indexOf(l) < 0) toLoad.push(l); }); }; if (!usedLng) { const fallbacks = this.services.languageUtils.getFallbackCodes(this.options.fallbackLng); fallbacks.forEach((l) => append(l)); } else { append(usedLng); } if (this.options.preload) { this.options.preload.forEach((l) => append(l)); } this.services.backendConnector.load(toLoad, this.options.ns, (e) => { if (!e && !this.resolvedLanguage && this.language) this.setResolvedLanguage(this.language); usedCallback(e); }); } else { usedCallback(null); } } reloadResources(lngs, ns, callback) { const deferred = defer(); if (typeof lngs === "function") { callback = lngs; lngs = void 0; } if (typeof ns === "function") { callback = ns; ns = void 0; } if (!lngs) lngs = this.languages; if (!ns) ns = this.options.ns; if (!callback) callback = noop; this.services.backendConnector.reload(lngs, ns, (err) => { deferred.resolve(); callback(err); }); return deferred; } use(module2) { if (!module2) throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()"); if (!module2.type) throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()"); if (module2.type === "backend") { this.modules.backend = module2; } if (module2.type === "logger" || module2.log && module2.warn && module2.error) { this.modules.logger = module2; } if (module2.type === "languageDetector") { this.modules.languageDetector = module2; } if (module2.type === "i18nFormat") { this.modules.i18nFormat = module2; } if (module2.type === "postProcessor") { postProcessor.addPostProcessor(module2); } if (module2.type === "formatter") { this.modules.formatter = module2; } if (module2.type === "3rdParty") { this.modules.external.push(module2); } return this; } setResolvedLanguage(l) { if (!l || !this.languages) return; if (["cimode", "dev"].indexOf(l) > -1) return; for (let li = 0; li < this.languages.length; li++) { const lngInLngs = this.languages[li]; if (["cimode", "dev"].indexOf(lngInLngs) > -1) continue; if (this.store.hasLanguageSomeTranslations(lngInLngs)) { this.resolvedLanguage = lngInLngs; break; } } } changeLanguage(lng, callback) { var _this2 = this; this.isLanguageChangingTo = lng; const deferred = defer(); this.emit("languageChanging", lng); const setLngProps = (l) => { this.language = l; this.languages = this.services.languageUtils.toResolveHierarchy(l); this.resolvedLanguage = void 0; this.setResolvedLanguage(l); }; const done = (err, l) => { if (l) { setLngProps(l); this.translator.changeLanguage(l); this.isLanguageChangingTo = void 0; this.emit("languageChanged", l); this.logger.log("languageChanged", l); } else { this.isLanguageChangingTo = void 0; } deferred.resolve(function() { return _this2.t(...arguments); }); if (callback) callback(err, function() { return _this2.t(...arguments); }); }; const setLng = (lngs) => { if (!lng && !lngs && this.services.languageDetector) lngs = []; const l = isString(lngs) ? lngs : this.services.languageUtils.getBestMatchFromCodes(lngs); if (l) { if (!this.language) { setLngProps(l); } if (!this.translator.language) this.translator.changeLanguage(l); if (this.services.languageDetector && this.services.languageDetector.cacheUserLanguage) this.services.languageDetector.cacheUserLanguage(l); } this.loadResources(l, (err) => { done(err, l); }); }; if (!lng && this.services.languageDetector && !this.services.languageDetector.async) { setLng(this.services.languageDetector.detect()); } else if (!lng && this.services.languageDetector && this.services.languageDetector.async) { if (this.services.languageDetector.detect.length === 0) { this.services.languageDetector.detect().then(setLng); } else { this.services.languageDetector.detect(setLng); } } else { setLng(lng); } return deferred; } getFixedT(lng, ns, keyPrefix) { var _this3 = this; const fixedT = function(key, opts) { let options; if (typeof opts !== "object") { for (var _len3 = arguments.length, rest = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) { rest[_key3 - 2] = arguments[_key3]; } options = _this3.options.overloadTranslationOptionHandler([key, opts].concat(rest)); } else { options = { ...opts }; } options.lng = options.lng || fixedT.lng; options.lngs = options.lngs || fixedT.lngs; options.ns = options.ns || fixedT.ns; if (options.keyPrefix !== "") options.keyPrefix = options.keyPrefix || keyPrefix || fixedT.keyPrefix; const keySeparator = _this3.options.keySeparator || "."; let resultKey; if (options.keyPrefix && Array.isArray(key)) { resultKey = key.map((k) => `${options.keyPrefix}${keySeparator}${k}`); } else { resultKey = options.keyPrefix ? `${options.keyPrefix}${keySeparator}${key}` : key; } return _this3.t(resultKey, options); }; if (isString(lng)) { fixedT.lng = lng; } else { fixedT.lngs = lng; } fixedT.ns = ns; fixedT.keyPrefix = keyPrefix; return fixedT; } t() { return this.translator && this.translator.translate(...arguments); } exists() { return this.translator && this.translator.exists(...arguments); } setDefaultNamespace(ns) { this.options.defaultNS = ns; } hasLoadedNamespace(ns) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; if (!this.isInitialized) { this.logger.warn("hasLoadedNamespace: i18next was not initialized", this.languages); return false; } if (!this.languages || !this.languages.length) { this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty", this.languages); return false; } const lng = options.lng || this.resolvedLanguage || this.languages[0]; const fallbackLng = this.options ? this.options.fallbackLng : false; const lastLng = this.languages[this.languages.length - 1]; if (lng.toLowerCase() === "cimode") return true; const loadNotPending = (l, n) => { const loadState = this.services.backendConnector.state[`${l}|${n}`]; return loadState === -1 || loadState === 0 || loadState === 2; }; if (options.precheck) { const preResult = options.precheck(this, loadNotPending); if (preResult !== void 0) return preResult; } if (this.hasResourceBundle(lng, ns)) return true; if (!this.services.backendConnector.backend || this.options.resources && !this.options.partialBundledLanguages) return true; if (loadNotPending(lng, ns) && (!fallbackLng || loadNotPending(lastLng, ns))) return true; return false; } loadNamespaces(ns, callback) { const deferred = defer(); if (!this.options.ns) { if (callback) callback(); return Promise.resolve(); } if (isString(ns)) ns = [ns]; ns.forEach((n) => { if (this.options.ns.indexOf(n) < 0) this.options.ns.push(n); }); this.loadResources((err) => { deferred.resolve(); if (callback) callback(err); }); return deferred; } loadLanguages(lngs, callback) { const deferred = defer(); if (isString(lngs)) lngs = [lngs]; const preloaded = this.options.preload || []; const newLngs = lngs.filter((lng) => preloaded.indexOf(lng) < 0 && this.services.languageUtils.isSupportedCode(lng)); if (!newLngs.length) { if (callback) callback(); return Promise.resolve(); } this.options.preload = preloaded.concat(newLngs); this.loadResources((err) => { deferred.resolve(); if (callback) callback(err); }); return deferred; } dir(lng) { if (!lng) lng = this.resolvedLanguage || (this.languages && this.languages.length > 0 ? this.languages[0] : this.language); if (!lng) return "rtl"; const rtlLngs = ["ar", "shu", "sqr", "ssh", "xaa", "yhd", "yud", "aao", "abh", "abv", "acm", "acq", "acw", "acx", "acy", "adf", "ads", "aeb", "aec", "afb", "ajp", "apc", "apd", "arb", "arq", "ars", "ary", "arz", "auz", "avl", "ayh", "ayl", "ayn", "ayp", "bbz", "pga", "he", "iw", "ps", "pbt", "pbu", "pst", "prp", "prd", "ug", "ur", "ydd", "yds", "yih", "ji", "yi", "hbo", "men", "xmn", "fa", "jpr", "peo", "pes", "prs", "dv", "sam", "ckb"]; const languageUtils = this.services && this.services.languageUtils || new LanguageUtil(get()); return rtlLngs.indexOf(languageUtils.getLanguagePartFromCode(lng)) > -1 || lng.toLowerCase().indexOf("-arab") > 1 ? "rtl" : "ltr"; } static createInstance() { let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; let callback = arguments.length > 1 ? arguments[1] : void 0; return new I18n(options, callback); } cloneInstance() { let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; let callback = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop; const forkResourceStore = options.forkResourceStore; if (forkResourceStore) delete options.forkResourceStore; const mergedOptions = { ...this.options, ...options, ...{ isClone: true } }; const clone = new I18n(mergedOptions); if (options.debug !== void 0 || options.prefix !== void 0) { clone.logger = clone.logger.clone(options); } const membersToCopy = ["store", "services", "language"]; membersToCopy.forEach((m) => { clone[m] = this[m]; }); clone.services = { ...this.services }; clone.services.utils = { hasLoadedNamespace: clone.hasLoadedNamespace.bind(clone) }; if (forkResourceStore) { clone.store = new ResourceStore(this.store.data, mergedOptions); clone.services.resourceStore = clone.store; } clone.translator = new Translator(clone.services, mergedOptions); clone.translator.on("*", function(event) { for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { args[_key4 - 1] = arguments[_key4]; } clone.emit(event, ...args); }); clone.init(mergedOptions, callback); clone.translator.options = mergedOptions; clone.translator.backendConnector.services.utils = { hasLoadedNamespace: clone.hasLoadedNamespace.bind(clone) }; return clone; } toJSON() { return { options: this.options, store: this.store, language: this.language, languages: this.languages, resolvedLanguage: this.resolvedLanguage }; } }; var instance = I18n.createInstance(); instance.createInstance = I18n.createInstance; var createInstance = instance.createInstance; var dir = instance.dir; var init = instance.init; var loadResources = instance.loadResources; var reloadResources = instance.reloadResources; var use = instance.use; var changeLanguage = instance.changeLanguage; var getFixedT = instance.getFixedT; var t = instance.t; var exists = instance.exists; var setDefaultNamespace = instance.setDefaultNamespace; var hasLoadedNamespace = instance.hasLoadedNamespace; var loadNamespaces = instance.loadNamespaces; var loadLanguages = instance.loadLanguages; // src/lib/translations/en.json var en_exports = {}; __export(en_exports, { default: () => en_default, translation: () => translation }); var translation = { errors: { source: { title: 'Source SMILES: "{{source}}"' }, dataview: { title: "Dataview plugin is missing or disabled." }, rdkit: { title: "RDKit failed to parse the SMILES." } }, menus: { copy: { title: "Copy", success: "Copied", error: "Copy failed" } }, settings: { scale: { name: "Scale", description: "Adjust the global molecule scale. If set to zero, the image widths will be unified. Otherwise, the structures will share the same bond length, but the image widths will vary.", tooltip: "Restore default" }, "unify-bond-length": { name: "Maximum width", description: "Crop structure images that are too large in a multiline block." }, "unify-image-width": { name: "Image width", description: "Adjust the width of the molecule images. Only valid when 'scale' is set to zero." }, theme: { light: { name: "Light theme", description: "Active when Obsidian is under light mode." }, dark: { name: "Dark theme", description: "Active when Obsidian is under dark mode." } }, preview: { title: "Live Preview", sample: { name: "Sample SMILES strings", description: "Input SMILES strings to see the styled structures." } }, advanced: { title: "Advanced", core: { name: "Core configuration", description: "Select render core." }, "compact-drawing": { name: "Compact drawing", description: "Linearize simple structures and functional groups." }, "terminal-carbons": { name: "Show terminal carbons", description: "Explictly draw terminal carbons like methyl or methylene." }, "explicit-hydrogen": { name: "Explicit hydrogen", description: "Enable to show explicit hydrogen." } }, copy: { title: "Copy", scale: { name: "Scale factor", description: "Scaling the copied image. Larger scale will yield images with better resolution." }, transparent: { name: "Transparent", description: "Enable to yield a copied image with transparent background." }, theme: { name: "Target theme", description: "Set the default theme of copied images.", default: "Adapt to Obsidian" } }, dataview: { title: "Dataview", enable: { name: "Parse Dataview", description: "In smiles block, recognize and get return values from Dataview queries and DataviewJS lines as rendering source according to Dataview settings." } }, inline: { title: "Inline SMILES", enable: { name: "Enable inline SMILES", description: "Render SMILES code lines." }, prefix: { name: "Inline SMILES Prefix", description: "The prefix to inline SMILES." } }, modals: { "core-fallback": { title: "Switch core failed", confirm: "Confirm", message: "Failed to switch to the target package, and will fallback to the default Smiles Drawer core." } } } }; var en_default = { translation }; // src/lib/translations/zh-CN.json var zh_CN_exports = {}; __export(zh_CN_exports, { default: () => zh_CN_default, translation: () => translation2 }); var translation2 = { errors: { source: { title: "\u539F\u59CB SMILES \uFF1A {{source}}" }, dataview: { title: "Dataview \u63D2\u4EF6\u672A\u542F\u7528\u3002" }, rdkit: { title: "RDKit \u65E0\u6CD5\u8BC6\u522B\u8BE5 SMILES \u5B57\u7B26\u4E32" } }, menus: { copy: { title: "\u590D\u5236", success: "\u590D\u5236\u6210\u529F", error: "\u590D\u5236\u5931\u8D25" } }, settings: { scale: { name: "\u56FE\u50CF\u6BD4\u4F8B", description: "\u8C03\u6574\u5168\u5C40\u56FE\u50CF\u6BD4\u4F8B\u3002\u8BE5\u503C\u4E3A 0 \u65F6\uFF0C\u6240\u6709\u56FE\u50CF\u5C06\u5177\u6709\u76F8\u540C\u7684\u5927\u5C0F\uFF1B\u82E5\u975E 0\uFF0C\u6240\u6709\u7ED3\u6784\u5C06\u5177\u6709\u76F8\u540C\u7684\u952E\u957F\uFF0C\u56FE\u50CF\u5C3A\u5BF8\u5C06\u9002\u5E94\u7ED3\u6784\u5F0F\u7684\u5927\u5C0F\u3002", tooltip: "\u6062\u590D\u9ED8\u8BA4" }, "unify-bond-length": { name: "\u6700\u5927\u5BBD\u5EA6", description: "\u4EE3\u7801\u5757\u4E2D\u6709\u591A\u884C SMILES \u5B57\u7B26\u4E32\u65F6\uFF0C\u7ED3\u6784\u56FE\u50CF\u7684\u6700\u5927\u5BBD\u5EA6\u3002" }, "unify-image-width": { name: "\u56FE\u50CF\u5BBD\u5EA6", description: "\u8C03\u6574\u5168\u5C40\u56FE\u50CF\u5BBD\u5EA6\u3002\u4EC5\u5728'\u56FE\u50CF\u6BD4\u4F8B'\u503C\u4E3A 0 \u65F6\u751F\u6548\u3002" }, theme: { light: { name: "\u6D45\u8272\u4E3B\u9898", description: "\u8C03\u6574\u7ED3\u6784\u5F0F\u914D\u8272\u65B9\u6848\uFF0C\u5728\u6D45\u8272\u6A21\u5F0F\u4E0B\u751F\u6548\u3002" }, dark: { name: "\u6DF1\u8272\u4E3B\u9898", description: "\u8C03\u6574\u7ED3\u6784\u5F0F\u914D\u8272\u65B9\u6848\uFF0C\u5728\u6DF1\u8272\u6A21\u5F0F\u4E0B\u751F\u6548\u3002" } }, preview: { title: "\u5B9E\u65F6\u9884\u89C8", sample: { name: "\u793A\u4F8B SMILES \u5B57\u7B26\u4E32", description: "\u8F93\u5165 SMILES \u5B57\u7B26\u4E32\u4EE5\u9884\u89C8\u6E32\u67D3\u6548\u679C\u3002" } }, advanced: { title: "\u9AD8\u7EA7", core: { name: "\u6E32\u67D3\u5668", description: "\u9009\u62E9\u6E32\u67D3\u5668\u3002" }, "compact-drawing": { name: "\u7D27\u51D1\u7ED8\u5236", description: "\u7B80\u5316\u7FA7\u57FA\u3001\u785D\u57FA\u3001\u78FA\u9178\u57FA\u7B49\u7B80\u5355\u5B98\u80FD\u56E2\u7684\u7ED8\u5236\u3002" }, "terminal-carbons": { name: "\u7AEF\u57FA\u78B3", description: "\u663E\u5F0F\u7ED8\u51FA\u672B\u7AEF\u7532\u57FA\u3001\u4E9A\u7532\u57FA\u7B49\u7AEF\u57FA\u78B3\u3002" }, "explicit-hydrogen": { name: "\u663E\u5F0F\u6C22", description: "\u542F\u7528\u4EE5\u7ED8\u5236\u663E\u5F0F\u6C22\u539F\u5B50\u3002" } }, copy: { title: "\u590D\u5236\u9009\u9879", scale: { name: "\u7F29\u653E\u6BD4\u4F8B", description: "\u8C03\u6574\u590D\u5236\u6240\u5F97\u56FE\u50CF\u7684\u7F29\u653E\u6BD4\u4F8B\uFF0C\u66F4\u5927\u7684\u6BD4\u4F8B\u5BF9\u5E94\u66F4\u9AD8\u7684\u5206\u8FA8\u7387\u3002" }, transparent: { name: "\u900F\u660E\u80CC\u666F", description: "\u542F\u7528\u4EE5\u900F\u660E\u5316\u590D\u5236\u6240\u5F97\u56FE\u50CF\u3002" }, theme: { name: "\u989C\u8272\u4E3B\u9898", description: "\u8BBE\u7F6E\u590D\u5236\u6240\u5F97\u56FE\u50CF\u7684\u989C\u8272\u4E3B\u9898", default: "\u8DDF\u968F Obsidian" } }, dataview: { title: "Dataview \u96C6\u6210", enable: { name: "\u89E3\u6790 Dataview", description: "\u6839\u636E Dataview \u63D2\u4EF6\u8BBE\u7F6E\uFF0C\u8BC6\u522B\u5E76\u6267\u884C smiles \u4EE3\u7801\u5757\u4E2D\u7684 Dataview \u67E5\u8BE2\u5F0F (Queries) \u548C DataviewJS \u4EE3\u7801\uFF0C\u4F9D\u67E5\u8BE2\u7ED3\u679C\u6E32\u67D3\u7ED3\u6784\u3002" } }, inline: { title: "\u884C\u5185 SMILES \u6E32\u67D3", enable: { name: "\u542F\u7528\u884C\u5185 SMILES", description: "\u6E32\u67D3\u884C\u5185\u4EE3\u7801\u5F62\u5F0F\u7684 SMILES \u5B57\u7B26\u4E32\u3002" }, prefix: { name: "\u524D\u7F00", description: "\u884C\u5185 SMILES \u7684\u524D\u7F00\u3002" } } }, modals: { "core-fallback": { title: "\u5207\u6362\u6E32\u67D3\u5668\u5931\u8D25", confirm: "\u786E\u8BA4", message: "\u5207\u6362\u76EE\u6807\u6E32\u67D3\u5668\u5931\u8D25\uFF0C\u5C06\u56DE\u9000\u4E3A\u9ED8\u8BA4\u6E32\u67D3\u5668 Smiles Drawer\u3002" } } }; var zh_CN_default = { translation: translation2 }; // src/lib/i18n.ts var import_obsidian = require("obsidian"); instance.init({ lng: import_obsidian.moment.locale(), fallbackLng: { "zh-TW": ["zh-CN", "en"], default: ["en"] }, resources: { en: en_exports, "zh-CN": zh_CN_exports } }); var i18n = instance; // src/lib/core/smilesDrawerCore.ts var SmilesDrawerCore = class { constructor(settings) { this.draw = async (source, theme = getCurrentTheme(this.settings)) => { var _a, _b, _c, _d, _e, _f, _g, _h; const svg = createSvg("svg"); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); svg.setAttribute("data-smiles", source); const errorDiv = createDiv(); const errorCb = (error) => { errorDiv.createDiv("error-source").setText(i18n.t("errors.source.title", { source })); errorDiv.createEl("br"); const info = errorDiv.createEl("details"); info.createEl("summary").setText(error.name); info.createEl("div").setText(error.message); errorDiv.style.wordBreak = `break-word`; errorDiv.style.userSelect = `text`; }; this.core.draw(source, svg, theme, () => { }, errorCb); if (this.settings.commonOptions.scale == 0) svg.style.width = `${((_b = (_a = this.settings) == null ? void 0 : _a.commonOptions.unifiedWidth) != null ? _b : 300).toString()}px`; else if (parseFloat(svg.style.width) > ((_d = (_c = this.settings.commonOptions) == null ? void 0 : _c.width) != null ? _d : 300)) { const r = parseFloat(svg.style.width) / parseFloat(svg.style.height); svg.style.width = `${((_f = (_e = this.settings.commonOptions) == null ? void 0 : _e.width) != null ? _f : 300).toString()}px`; svg.style.height = `${(((_h = (_g = this.settings.commonOptions) == null ? void 0 : _g.width) != null ? _h : 300) / r).toString()}px`; } if (errorDiv.innerHTML) return errorDiv; return svg; }; this.settings = settings; this.core = new import_smiles_drawer.default.SmiDrawer( { ...DEFAULT_SD_OPTIONS.moleculeOptions, ...this.settings.smilesDrawerOptions.moleculeOptions }, { ...DEFAULT_SD_OPTIONS.reactionOptions, ...this.settings.smilesDrawerOptions.reactionOptions } ); } }; // src/lib/core/rdkitOptions.ts var DEFAULT_RDKIT_OPTIONS = { explicitMethyl: false, fixedBondLength: -1, fixedScale: -1, atomColourPalette: convertToRDKitTheme("rdkit-default"), includeRadicals: true, clearBackground: false, addStereoAnnotation: false, annotationColour: [0.63, 0.12, 0.94], // chiral RS annotations singleColourWedgeBonds: true, // a company option with `symbolColour` addChiralHs: true // test case: C1C[C@H]2CCCC[C@H]2CC1 // wedgeBonds: true, // wavyBonds: true, // kekulize: false, }; // src/lib/core/rdkitCore.ts var import_obsidian2 = require("obsidian"); // src/global/app.ts var AppStore = class { constructor() { this._app = null; } init(app) { this._app = app; } get app() { if (!this._app) { throw new Error("App store not initialized. Call AppStore.init() first."); } return this._app; } clear() { this._app = null; } }; var appStore = new AppStore(); var setApp = (app) => appStore.init(app); var getApp = () => appStore.app; var clearApp = () => appStore.clear(); // src/lib/core/rdkitCore.ts var RDKitCore = class { /** * @private */ constructor(settings, core) { this.draw = async (source, theme = getCurrentTheme(this.settings)) => { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m; let svgstr = ""; if (source.includes(">")) { const rxn = this.core.get_rxn(source); if (!rxn) return this.logError(source); svgstr = await this.drawReaction(rxn); } else { const mol = this.core.get_mol(source, JSON.stringify({})); if (!mol) return this.logError(source); if (this.settings.commonOptions.compactDrawing) mol.condense_abbreviations(); svgstr = await this.drawMolecule(mol, theme); } const container2 = createDiv(); container2.innerHTML = svgstr; const svg = container2.find("svg"); svg.setAttribute("data-smiles", source); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); const w = svg.width.baseVal.value; const h = svg.height.baseVal.value; const r = w / h; const scale = (_a = this.settings.commonOptions.scale) != null ? _a : 1; if (this.settings.commonOptions.scale == 0) { svg.style.width = `${((_c = (_b = this.settings) == null ? void 0 : _b.commonOptions.unifiedWidth) != null ? _c : 300).toString()}px`; svg.style.height = `${(((_e = (_d = this.settings.commonOptions) == null ? void 0 : _d.unifiedWidth) != null ? _e : 300) / r).toString()}px`; } else if (w * scale > ((_g = (_f = this.settings.commonOptions) == null ? void 0 : _f.width) != null ? _g : 300)) { svg.style.width = `${((_i = (_h = this.settings.commonOptions) == null ? void 0 : _h.width) != null ? _i : 300).toString()}px`; svg.style.height = `${(((_k = (_j = this.settings.commonOptions) == null ? void 0 : _j.width) != null ? _k : 300) / r).toString()}px`; } else if (w * scale <= ((_m = (_l = this.settings.commonOptions) == null ? void 0 : _l.width) != null ? _m : 300)) { svg.style.width = `${(w * scale).toString()}px`; svg.style.height = `${(h * scale).toString()}px`; } return svg; }; this.drawReaction = async (rxn) => { const svgstr = rxn.get_svg_with_highlights( JSON.stringify({ width: -1, height: -1 }) ); return svgstr; }; this.drawMolecule = async (mol, theme) => { const palette = convertToRDKitTheme(theme); const svgstr = mol.get_svg_with_highlights( JSON.stringify({ ...DEFAULT_RDKIT_OPTIONS, ...this.settings.rdkitOptions, legend: mol.has_prop("_Name") ? mol.get_prop("_Name") : "", atomColourPalette: palette, queryColour: palette["6"], highlightColour: palette["6"], drawMolsSameScale: true, width: -1, height: -1, addChiralHs: this.settings.commonOptions.explicitHydrogens, //https://rdkit.org/docs/cppapi/MolDraw2D_8h_source.html#l00547 symbolColour: palette["-1"], // the color used for reaction arrows and single-color wedged bonds legendColour: palette["-1"], annotationColour: palette["-1"], variableAttachmentColour: palette["-1"] }) ); return svgstr; }; this.logError = (source) => { const div = createDiv(); div.createDiv("error-source").setText(i18n.t("errors.source.title", { source })); div.createEl("br"); div.createDiv().setText(i18n.t("errors.rdkit.title")); div.style.wordBreak = `break-word`; div.style.userSelect = `text`; return div; }; this.settings = settings; this.core = core; } static async init(settings) { if (!window.RDKit) { try { window.RDKit = await loadRDKit(); } catch (e) { try { window.RDKit = await loadRDKitUnpkg(); } catch (e2) { throw Error( "Initializing rdkit failed: Can't fetch resources from unpkg." ); } } } return new RDKitCore(settings, window.RDKit); } }; var loadRDKit = async () => { const app = getApp(); const assetPath = (0, import_obsidian2.normalizePath)( [app.vault.configDir, "plugins", "chem", "rdkit"].join("/") ); if (!await app.vault.adapter.exists(assetPath)) { await app.vault.adapter.mkdir(assetPath); } const jsPath = [assetPath, "RDKit_minimal.js"].join("/"); await checkOrDownload("RDKit_minimal.js"); const wasmPath = [assetPath, "RDKit_minimal.wasm"].join("/"); await checkOrDownload("RDKit_minimal.wasm"); const rdkitBundler = document.body.createEl("script"); rdkitBundler.type = "text/javascript"; rdkitBundler.id = "chem-rdkit-bundler"; rdkitBundler.innerHTML = await app.vault.adapter.read(jsPath); const getWasmURL = async () => URL.createObjectURL( new Blob([await app.vault.adapter.readBinary(wasmPath)], { type: "application/wasm" }) ); const url = await getWasmURL(); const RDKit = await window.initRDKitModule({ locateFile: () => url }); URL.revokeObjectURL(url); return RDKit; }; var loadRDKitUnpkg = async () => { const rdkitBundler = document.body.createEl("script"); new import_obsidian2.Notice("Fetching RDKit.js from unpkg..."); rdkitBundler.innerHTML = await (0, import_obsidian2.requestUrl)( "https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.js" ).text; const RDKit = await window.initRDKitModule({ locateFile: () => "https://unpkg.com/@rdkit/rdkit/dist/RDKit_minimal.wasm" }); new import_obsidian2.Notice("RDKit.js has been successfully loaded."); return RDKit; }; var fetchAsset = async (target, localPath) => { var _a, _b; const app = getApp(); const assetInfo = await (0, import_obsidian2.requestUrl)( `https://api.github.com/repos/acylation/obsidian-chem/releases/tags/${(_b = (_a = app.plugins.getPlugin("chem")) == null ? void 0 : _a.manifest.version) != null ? _b : "0.4.0"}` ).json; const asset = assetInfo.assets.find((a) => a.name == target); if (asset === void 0) throw Error("Could not find the online asset!"); const data = await (0, import_obsidian2.requestUrl)({ url: asset.url, headers: { Accept: "application/octet-stream" } }).arrayBuffer; await app.vault.adapter.writeBinary(localPath, data); }; var checkOrDownload = async (target) => { const app = getApp(); const assetPath = (0, import_obsidian2.normalizePath)( [app.vault.configDir, "plugins", "chem", "rdkit", target].join("/") ); if (!await app.vault.adapter.exists(assetPath)) { new import_obsidian2.Notice(`Chem: Downloading ${target}!`, 5e3); try { await fetchAsset(target, assetPath); new import_obsidian2.Notice(`Chem: Resource ${target} successfully downloaded! \u2714\uFE0F`, 5e3); } catch (error) { new import_obsidian2.Notice(`Chem: Failed to fetch ${target}: ${error} \u274C`); throw Error(`Failed to fetch resource ${target} from GitHub release.`); } } }; // src/global/chemCore.ts var gRenderCore; var setCore = async (settings, onFallback) => { if (!gRenderCore || settings.core !== gRenderCore.id) { if (settings.core === "smiles-drawer") { gRenderCore = new SmilesDrawerCore(settings); updateCoreSettings(settings); } else if (settings.core === "rdkit") { try { gRenderCore = await RDKitCore.init(settings); updateCoreSettings(settings); } catch (error) { onFallback(error); } } else { onFallback(`invalid chem core id. ${settings.core}`); } } }; var setFallbackCore = async (settings) => { gRenderCore = new SmilesDrawerCore(settings); updateCoreSettings(settings); }; var updateCoreSettings = (settings) => { gRenderCore.settings = settings; }; var clearCore = () => { const rdkitBundler = document.getElementById("chem-rdkit-bundler"); if (rdkitBundler) document.body.removeChild(rdkitBundler); delete window.RDKit; delete window.initRDKitModule; delete window.SmilesDrawer; delete window.SmiDrawer; }; // src/global/blocks.ts var gBlocks; var setBlocks = () => { gBlocks = new Array(); }; var addBlock = (block) => { gBlocks.push(block); }; var removeBlock = (block) => { if (gBlocks.indexOf(block) != -1) gBlocks.splice(gBlocks.indexOf(block), 1); }; var refreshBlocks = () => { if (!gBlocks) return; gBlocks.forEach((block) => { block.render(); }); }; var clearBlocks = () => { if (!gBlocks) return; gBlocks.forEach((block) => { removeBlock(block); }); }; // src/global/dataview.ts var import_obsidian_dataview = __toESM(require_lib()); var import_obsidian_dataview2 = __toESM(require_lib()); var gDataview; var getDataview = () => { const app = getApp(); if ((0, import_obsidian_dataview.isPluginEnabled)(app)) { gDataview = app.plugins.getPlugin("dataview"); } else { throw new Error(i18n.t("errors.dataview.title")); } }; var clearDataview = () => { gDataview = null; }; // src/settings/SettingTab.ts var import_obsidian4 = require("obsidian"); // src/settings/LivePreview.ts var LivePreview = class { constructor(el, argSettings) { this.el = el; this.argSettings = argSettings; this.render = () => { var _a, _b; this.lightCard.empty(); const lightWidth = this.renderCell( this.settings.sample1, this.lightCard, this.settings.lightTheme ); this.darkCard.empty(); const darkWidth = this.renderCell( this.settings.sample2, this.darkCard, this.settings.darkTheme ); if (this.settings.commonOptions.scale == 0) this.container.style.gridTemplateColumns = `repeat(auto-fill, minmax(${((_b = (_a = this.settings) == null ? void 0 : _a.commonOptions.unifiedWidth) != null ? _b : 300).toString()}px, 1fr)`; else this.container.style.gridTemplateColumns = `repeat(auto-fill, minmax(${(lightWidth > darkWidth ? lightWidth : darkWidth).toString()}px, 1fr)`; }; this.updateSettings = (newSettings) => { this.settings = newSettings; }; this.renderCell = async (source, target, theme) => { const svg = await gRenderCore.draw(source, theme); target.appendChild(svg); return parseFloat(svg.style.width); }; this.container = this.el.createEl("div"); this.container.style.display = `flex`; this.container.style.flexWrap = `wrap`; this.container.style.justifyContent = `center`; this.lightCard = this.container.createEl("div", { cls: "chemcard theme-light" }); this.darkCard = this.container.createEl("div", { cls: "chemcard theme-dark" }); this.settings = this.argSettings; } }; // src/lib/core/coreFallbackModal.ts var import_obsidian3 = require("obsidian"); var CoreFallbackModal = class extends import_obsidian3.Modal { constructor(app, text, onConfrim) { super(app); this.msg = text; this.onConfirm = onConfrim; } onOpen() { const { contentEl } = this; contentEl.createEl("h3", { text: i18n.t("modals.core-fallback.title") }); contentEl.createEl("div", { text: i18n.t("modals.core-fallback.message") }); contentEl.createEl("br"); contentEl.createEl("div", { text: this.msg }); const div = contentEl.createDiv(); div.style.display = "flex"; div.style.flex = "1 1 auto"; div.style.justifyContent = "flex-end"; div.style.padding = "3px"; new import_obsidian3.ButtonComponent(div).setCta().setButtonText(i18n.t("modals.core-fallback.confirm")).onClick(() => { this.close(); }); } onClose() { this.onConfirm(); const { contentEl } = this; contentEl.empty(); } }; // src/settings/SettingTab.ts var ChemSettingTab = class extends import_obsidian4.PluginSettingTab { constructor({ app, plugin }) { super(app, plugin); this.updateCore = () => updateCoreSettings(this.plugin.settings); this.plugin = plugin; } display() { var _a, _b; const { containerEl } = this; containerEl.empty(); const scaleSetting = new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.scale.name")).setDesc(i18n.t("settings.scale.description")).addExtraButton((button) => { button.setIcon("rotate-ccw").setTooltip(i18n.t("settings.scale.tooltip")).onClick(async () => { this.plugin.settings.smilesDrawerOptions.moleculeOptions.scale = 1; this.plugin.settings.commonOptions.scale = 1; scaleSlider.setValue(1); await this.plugin.saveSettings(); this.updateCore(); onSettingsChange(); unifyBondLength(); }); }); const scaleSlider = new import_obsidian4.SliderComponent(scaleSetting.controlEl).setLimits(0, 2, 0.01).setValue((_a = this.plugin.settings.commonOptions.scale) != null ? _a : 1).setDynamicTooltip().onChange(async (value) => { this.plugin.settings.smilesDrawerOptions.moleculeOptions.scale = value; this.plugin.settings.commonOptions.scale = value; await this.plugin.saveSettings(); this.updateCore(); onSettingsChange(); value === 0 ? unifyImageWidth() : unifyBondLength(); }); const widthSettings = new import_obsidian4.Setting(containerEl); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.theme.light.name")).setDesc(i18n.t("settings.theme.light.description")).addDropdown( (dropdown) => dropdown.addOptions(themeList).setValue(this.plugin.settings.lightTheme).onChange(async (value) => { this.plugin.settings.lightTheme = value; await this.plugin.saveSettings(); onSettingsChange(); }) ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.theme.dark.name")).setDesc(i18n.t("settings.theme.dark.description")).addDropdown( (dropdown) => dropdown.addOptions(themeList).setValue(this.plugin.settings.darkTheme).onChange(async (value) => { this.plugin.settings.darkTheme = value; await this.plugin.saveSettings(); onSettingsChange(); }) ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.preview.title")).setHeading(); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.preview.sample.name")).setDesc(i18n.t("settings.preview.sample.description")).addText( (text) => text.setPlaceholder(DEFAULT_SETTINGS.sample1).setValue(this.plugin.settings.sample1).onChange(async (value) => { if (value == "") { value = DEFAULT_SETTINGS.sample1; } this.plugin.settings.sample1 = value; await this.plugin.saveSettings(); onSettingsChange(); }) ).addText( (text) => text.setPlaceholder(DEFAULT_SETTINGS.sample2).setValue(this.plugin.settings.sample2).onChange(async (value) => { if (value == "") { value = DEFAULT_SETTINGS.sample2; } this.plugin.settings.sample2 = value; await this.plugin.saveSettings(); onSettingsChange(); }) ); const preview = new LivePreview(containerEl, this.plugin.settings); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.advanced.title")).setHeading(); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.advanced.core.name")).setDesc(i18n.t("settings.advanced.core.description")).addDropdown( (dropdown) => { var _a2; return dropdown.addOptions({ rdkit: "RDKit.js", "smiles-drawer": "Smiles Drawer" }).setValue((_a2 = this.plugin.settings.core) != null ? _a2 : false).onChange(async (value) => { this.plugin.settings.core = value; await this.plugin.saveSettings(); await setCore(this.plugin.settings, async (error) => { new CoreFallbackModal(this.app, error, async () => { dropdown.setValue("smiles-drawer"); this.plugin.settings.core = "smiles-drawer"; await this.plugin.saveSettings(); setFallbackCore(this.plugin.settings); onSettingsChange(); }).open(); }); onSettingsChange(); }); } ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.advanced.compact-drawing.name")).setDesc(i18n.t("settings.advanced.compact-drawing.description")).addToggle( (toggle) => { var _a2; return toggle.setValue( (_a2 = this.plugin.settings.smilesDrawerOptions.moleculeOptions.compactDrawing) != null ? _a2 : false ).onChange(async (value) => { this.plugin.settings.smilesDrawerOptions.moleculeOptions.compactDrawing = value; this.plugin.settings.commonOptions.compactDrawing = value; await this.plugin.saveSettings(); this.updateCore(); onSettingsChange(); }); } ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.advanced.terminal-carbons.name")).setDesc(i18n.t("settings.advanced.terminal-carbons.description")).addToggle( (toggle) => { var _a2; return toggle.setValue( (_a2 = this.plugin.settings.smilesDrawerOptions.moleculeOptions.terminalCarbons) != null ? _a2 : false ).onChange(async (value) => { this.plugin.settings.smilesDrawerOptions.moleculeOptions.terminalCarbons = value; this.plugin.settings.rdkitOptions.explicitMethyl = value; this.plugin.settings.commonOptions.explicitMethyl = value; await this.plugin.saveSettings(); this.updateCore(); onSettingsChange(); }); } ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.advanced.explicit-hydrogen.name")).setDesc(i18n.t("settings.advanced.explicit-hydrogen.description")).addToggle( (toggle) => { var _a2; return toggle.setValue( (_a2 = this.plugin.settings.smilesDrawerOptions.moleculeOptions.explicitHydrogens) != null ? _a2 : false ).onChange(async (value) => { this.plugin.settings.smilesDrawerOptions.moleculeOptions.explicitHydrogens = value; this.plugin.settings.commonOptions.explicitHydrogens = value; await this.plugin.saveSettings(); this.updateCore(); onSettingsChange(); }); } ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.copy.title")).setHeading(); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.copy.scale.name")).setDesc(i18n.t("settings.copy.scale.description")).addText( (text) => text.setValue(this.plugin.settings.copy.scale.toString()).onChange(async (value) => { this.plugin.settings.copy.scale = parseFloat(value); await this.plugin.saveSettings(); onSettingsChange(); }) ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.copy.transparent.name")).setDesc(i18n.t("settings.copy.transparent.description")).addToggle( (toggle) => toggle.setValue(this.plugin.settings.copy.transparent).onChange(async (value) => { this.plugin.settings.copy.transparent = value; await this.plugin.saveSettings(); onSettingsChange(); }) ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.copy.theme.name")).setDesc(i18n.t("settings.copy.theme.description")).addDropdown( (dropdown) => dropdown.addOptions({ default: i18n.t("settings.copy.theme.default"), ...themeList }).setValue(this.plugin.settings.copy.theme).onChange(async (value) => { this.plugin.settings.copy.theme = value; await this.plugin.saveSettings(); onSettingsChange(); }) ); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.dataview.title")).setHeading(); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.dataview.enable.name")).setDesc(i18n.t("settings.dataview.enable.description")).addToggle((toggle) => { toggle.setValue(this.plugin.settings.dataview).onChange(async (value) => { this.plugin.settings.dataview = value; if (value) { getDataview(); } else { clearDataview(); } await this.plugin.saveSettings(); onSettingsChange(); }); }); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.inline.title")).setHeading(); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.inline.enable.name")).setDesc(i18n.t("settings.inline.enable.description")).addToggle((toggle) => { toggle.setValue(this.plugin.settings.inlineSmiles).onChange(async (value) => { this.plugin.settings.inlineSmiles = value; await this.plugin.saveSettings(); onSettingsChange(); }); }); new import_obsidian4.Setting(containerEl).setName(i18n.t("settings.inline.prefix.name")).setDesc(i18n.t("settings.inline.prefix.description")).addText((text) => { text.setValue(this.plugin.settings.inlineSmilesPrefix).onChange(async (value) => { this.plugin.settings.inlineSmilesPrefix = value; await this.plugin.saveSettings(); onSettingsChange(); }); }); const onSettingsChange = () => { preview.updateSettings(this.plugin.settings); preview.render(); }; const unifyBondLength = () => { widthSettings.controlEl.empty(); widthSettings.setName(i18n.t("settings.unify-bond-length.name")).setDesc(i18n.t("settings.unify-bond-length.description")).addText( (text) => { var _a2, _b2; return text.setValue( (_b2 = (_a2 = this.plugin.settings.commonOptions.width) == null ? void 0 : _a2.toString()) != null ? _b2 : "300" ).onChange(async (value) => { if (value == "") { value = "300"; } this.plugin.settings.commonOptions.width = parseInt(value); this.plugin.settings.smilesDrawerOptions.moleculeOptions.width = parseInt(value); await this.plugin.saveSettings(); this.updateCore(); onSettingsChange(); }); } ); }; const unifyImageWidth = () => { widthSettings.controlEl.empty(); widthSettings.setName(i18n.t("settings.unify-image-width.name")).setDesc(i18n.t("settings.unify-image-width.description")).addText((text) => { var _a2, _b2; text.setValue( (_b2 = (_a2 = this.plugin.settings.commonOptions.unifiedWidth) == null ? void 0 : _a2.toString()) != null ? _b2 : "300" ).setPlaceholder("300").onChange(async (value) => { if (value == "") { value = "300"; } this.plugin.settings.commonOptions.unifiedWidth = parseInt(value); await this.plugin.saveSettings(); onSettingsChange(); }); }); }; preview.render(); if (((_b = this.plugin.settings.smilesDrawerOptions.moleculeOptions.scale) != null ? _b : 1) == 0) unifyImageWidth(); else unifyBondLength(); } hide() { refreshBlocks(); } }; // src/SmilesBlock.ts var import_obsidian5 = require("obsidian"); var SmilesBlock = class extends import_obsidian5.MarkdownRenderChild { constructor(el, markdownSource, context, settings) { super(el); this.el = el; this.markdownSource = markdownSource; this.context = context; this.settings = settings; this.preprocess = async (source) => { const dvEl = this.el.createDiv(); this.el.appendChild(dvEl); const api = gDataview.localApi(this.context.sourcePath, this, dvEl); const isDQL = (source2) => { const prefix = gDataview.settings.inlineQueryPrefix; return prefix.length > 0 && source2.startsWith(prefix); }; const isDataviewJs = (source2) => { const prefix = gDataview.settings.inlineJsQueryPrefix; return prefix.length > 0 && source2.startsWith(prefix); }; const evaluateDQL = (row) => { const prefix = gDataview.settings.inlineQueryPrefix; const result = api.evaluate(row.substring(prefix.length).trim()); return result.successful ? result.value : row; }; const executeJs = async (row) => { var _a, _b; const prefix = gDataview.settings.inlineJsQueryPrefix; const code = row.substring(prefix.length).trim(); const func = new Function( "api", `return eval(\`const dv = api; const dataview = api; ${code}\`)` //TODO: await & async exec ); return (_b = (_a = func(api)) == null ? void 0 : _a.toString()) != null ? _b : "DataviewJS parsing error"; }; if (gDataview.settings.enableInlineDataview && isDQL(source)) { return evaluateDQL(source); } else if (gDataview.settings.enableDataviewJs && gDataview.settings.enableInlineDataviewJs && isDataviewJs(source)) { return await executeJs(source); } dvEl.remove(); return source; }; this.renderCell = async (source, target, theme) => { var _a, _b; const svg = await gRenderCore.draw(source, theme); target.appendChild(svg); if (this.settings.commonOptions.scale == 0) svg.style.width = `${(_b = (_a = this.settings.commonOptions.unifiedWidth) == null ? void 0 : _a.toString()) != null ? _b : 300}px`; return svg; }; this.handleContextMenu = (event) => { const targetEl = event.target; const closestSVG = targetEl.tagName === "svg" ? targetEl : targetEl.closest("svg"); if (!closestSVG) return; const menu = new import_obsidian5.Menu(); menu.addItem((item) => { item.setTitle(i18n.t("menus.copy.title")).setIcon("copy").onClick(() => { var _a, _b; const svg = closestSVG.outerHTML; const source = (_b = (_a = closestSVG.attributes.getNamedItem("data-smiles")) == null ? void 0 : _a.value) != null ? _b : ""; const rect = closestSVG.getBoundingClientRect(); this.onCopy(svg, source, rect.width, rect.height); }); }); menu.showAtMouseEvent(event); }; addBlock(this); } // TODO: rendering animation async render() { var _a, _b, _c; this.el.empty(); const oRows = this.markdownSource.split("\n").filter((row) => row.length > 0); let rows = oRows; if (this.settings.dataview && (0, import_obsidian_dataview2.isPluginEnabled)(getApp())) { if (!gDataview) getDataview(); rows = await Promise.all( oRows.map(async (row) => await this.preprocess(row)) ); } rows = rows.map((row) => row.trim()); if (rows.length == 1) { const cell = this.el.createDiv({ cls: "chem-cell" }); this.renderCell(rows[0], cell); } else { const table = this.el.createDiv({ cls: "chem-table" }); const maxWidth = (_a = this.settings.commonOptions.width) != null ? _a : 300; rows.forEach(async (row) => { const cell = table.createDiv({ cls: "chem-cell" }); const svgcell = await this.renderCell(row, cell); if (parseFloat(svgcell.style.width) > maxWidth) { const r = parseFloat(svgcell.style.width) / parseFloat(svgcell.style.height); svgcell.style.width = `${maxWidth.toString()}px`; svgcell.style.height = `${(maxWidth / r).toString()}px`; } }); table.style.gridTemplateColumns = `repeat(auto-fill, minmax(${(_c = (_b = this.settings.commonOptions.width) == null ? void 0 : _b.toString()) != null ? _c : "300"}px, 1fr)`; } } async onload() { this.theme = getCurrentTheme(this.settings); this.render(); this.registerDomEvent(this.el, "contextmenu", this.handleContextMenu); } // Credits to Thom Kiesewetter @ Stack Overflow https://stackoverflow.com/a/58142441 // Credits to image-toolkit plugin by Sissilab @ GitHub async onCopy(svgString, source, w, h) { let copyTheme = this.settings.copy.theme; if (copyTheme === "default") copyTheme = this.theme; else if (copyTheme !== this.theme) { const div = this.containerEl.createDiv(); div.style.display = "none"; const svg = await this.renderCell(source, div, copyTheme); svgString = svg.outerHTML; svg.remove(); } const svgUrl = URL.createObjectURL( new Blob([svgString], { type: "image/svg+xml" }) ); const image = new Image(); image.src = svgUrl; image.onload = () => { const canvas = document.createElement("canvas"); const f = this.settings.copy.scale; canvas.width = f * w; canvas.height = f * h; const ctx = canvas.getContext("2d"); if (!ctx) { new import_obsidian5.Notice(i18n.t("menus.copy.error")); URL.revokeObjectURL(svgUrl); return; } ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = "high"; if (!this.settings.copy.transparent) { copyTheme.contains("rdkit") ? ctx.fillStyle = getSDBGColor(copyTheme) : ctx.fillStyle = convertToSDTheme(copyTheme).BACKGROUND; ctx.fillRect(0, 0, canvas.width, canvas.height); } ctx.scale(f, f); ctx.drawImage(image, 0, 0, w, h); try { canvas.toBlob(async (blob) => { await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]).then( () => new import_obsidian5.Notice(i18n.t("menus.copy.success")), () => new import_obsidian5.Notice(i18n.t("menus.copy.error")) ); }); } catch (e) { new import_obsidian5.Notice(i18n.t("menus.copy.error")); } URL.revokeObjectURL(svgUrl); }; image.onerror = () => { new import_obsidian5.Notice(i18n.t("menus.copy.error")); URL.revokeObjectURL(svgUrl); }; } onunload() { removeBlock(this); } }; // src/SmilesInline.ts var import_view = require("@codemirror/view"); var import_language = require("@codemirror/language"); var import_obsidian6 = require("obsidian"); function selectionAndRangeOverlap(selection, rangeFrom, rangeTo) { for (const range of selection.ranges) { if (range.from <= rangeTo && range.to >= rangeFrom) { return true; } } return false; } var InlineWidget = class extends import_view.WidgetType { constructor(source, el, view) { super(); this.source = source; this.el = el; this.view = view; } eq(other) { return other.source === this.source ? true : false; } toDOM() { return this.el; } /* Make queries only editable when shift is pressed (or navigated inside with the keyboard * or the mouse is placed at the end, but that is always possible regardless of this method). * Mostly useful for links, and makes results selectable. * If the widgets should always be expandable, make this always return false. */ ignoreEvent(event) { if (event.type === "mousedown") { const currentPos = this.view.posAtCoords({ x: event.x, y: event.y }); if (event.shiftKey) { if (currentPos) { const { editor } = this.view.state.field(import_obsidian6.editorInfoField); if (editor) { editor.setCursor(editor.offsetToPos(currentPos)); } } return false; } } return true; } }; function inlinePlugin(settings) { const renderCell = async (source, target, theme) => { var _a, _b; const svg = await gRenderCore.draw(source, theme); target.appendChild(svg); if (settings.commonOptions.scale == 0) svg.style.width = `${(_b = (_a = settings.commonOptions.unifiedWidth) == null ? void 0 : _a.toString()) != null ? _b : 300}px`; return svg; }; return import_view.ViewPlugin.fromClass( class { constructor(view) { var _a; this.component = new import_obsidian6.Component(); this.component.load(); this.decorations = (_a = this.inlineRender(view)) != null ? _a : import_view.Decoration.none; } update(update) { var _a; if (!update.state.field(import_obsidian6.editorLivePreviewField)) { this.decorations = import_view.Decoration.none; return; } if (update.docChanged) { this.decorations = this.decorations.map(update.changes); this.updateTree(update.view); } else if (update.selectionSet) { this.updateTree(update.view); } else if (update.viewportChanged) { this.decorations = (_a = this.inlineRender(update.view)) != null ? _a : import_view.Decoration.none; } } updateTree(view) { for (const { from, to } of view.visibleRanges) { (0, import_language.syntaxTree)(view.state).iterate({ from, to, enter: ({ node }) => { const { render, isQuery } = this.renderNode(view, node); if (!render && isQuery) { this.removeDeco(node); return; } else if (!render) { return; } else if (render) { this.addDeco(node, view); } } }); } } removeDeco(node) { this.decorations.between( node.from - 1, node.to + 1, (from, to, value) => { this.decorations = this.decorations.update({ filterFrom: from, filterTo: to, filter: (from2, to2, value2) => false }); } ); } addDeco(node, view) { var _a; const from = node.from - 1; const to = node.to + 1; let exists2 = false; this.decorations.between(from, to, (from2, to2, value) => { exists2 = true; }); if (!exists2) { const currentFile = view.state.field(import_obsidian6.editorInfoField).file; if (!currentFile) return; const newDeco = (_a = this.renderWidget(node, view)) == null ? void 0 : _a.value; if (newDeco) { this.decorations = this.decorations.update({ add: [{ from, to, value: newDeco }] }); } } } // checks whether a node should get rendered/unrendered renderNode(view, node) { const type = node.type; const tokenProps = type.prop(import_language.tokenClassNodeProp); const props = new Set(tokenProps == null ? void 0 : tokenProps.split(" ")); if (props.has("inline-code") && !props.has("formatting")) { const start = node.from; const end = node.to; const selection = view.state.selection; if (selectionAndRangeOverlap(selection, start - 1, end + 1)) { if (this.isInlineSmiles(view, start, end)) { return { render: false, isQuery: true }; } else { return { render: false, isQuery: false }; } } else if (this.isInlineSmiles(view, start, end)) { return { render: true, isQuery: true }; } } return { render: false, isQuery: false }; } isInlineSmiles(view, start, end) { if (settings.inlineSmilesPrefix.length > 0) { const text = view.state.doc.sliceString(start, end); return text.startsWith(settings.inlineSmilesPrefix); } else return false; } inlineRender(view) { const currentFile = view.state.field(import_obsidian6.editorInfoField).file; if (!currentFile) return; const widgets = []; for (const { from, to } of view.visibleRanges) { (0, import_language.syntaxTree)(view.state).iterate({ from, to, enter: ({ node }) => { if (!this.renderNode(view, node).render) return; const widget = this.renderWidget(node, view); if (widget) { widgets.push(widget); } } }); } return import_view.Decoration.set(widgets, true); } renderWidget(node, view) { const start = node.from; const end = node.to; if (view.state.doc.sliceString(end, end + 1) === "\n") { return; } const text = view.state.doc.sliceString(start, end); const el = createSpan({ cls: ["smiles", "chem-cell-inline", "chem-cell"] }); let code = ""; if (settings.inlineSmiles && text.startsWith(settings.inlineSmilesPrefix)) { code = text.substring(settings.inlineSmilesPrefix.length).trim(); renderCell(code, el.createDiv(), getCurrentTheme(settings)); } return import_view.Decoration.replace({ widget: new InlineWidget(code, el, view), inclusive: false, block: false }).range(start - 1, end + 1); } destroy() { this.component.unload(); } }, { decorations: (v) => v.decorations } ); } // src/lib/themes/themeObserver.ts var themeObserver = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { var _a, _b, _c, _d; const target = mutation.target; if ( // dark -> dark & light -> light ((_a = mutation.oldValue) == null ? void 0 : _a.contains("theme-dark")) && !((_b = mutation.oldValue) == null ? void 0 : _b.contains("theme-light")) && // key line, avoid calling twice target.classList.value.contains("theme-light") ) { refreshBlocks(); } else if ( // light -> empty -> dark ((_c = mutation.oldValue) == null ? void 0 : _c.contains("theme-light")) && // key line, avoid calling twice !((_d = mutation.oldValue) == null ? void 0 : _d.contains("theme-dark")) && target.classList.value.contains("theme-dark") ) { refreshBlocks(); } }); }); var setObserver = () => { themeObserver.observe(document.body, { attributes: true, attributeOldValue: true, attributeFilter: ["class"] }); }; var detachObserver = () => { themeObserver.disconnect(); }; // src/main.ts var ChemPlugin = class extends import_obsidian7.Plugin { constructor() { super(...arguments); this.smilesProcessor = (source, el, ctx) => { ctx.addChild(new SmilesBlock(el, source, ctx, this.settings)); }; //TODO: fix extra br in inline code this.inlineSmilesProcessor = (el, ctx) => { const inlineCodes = el.findAll("code"); inlineCodes.forEach((code) => { const text = code.innerText; if (text.startsWith(this.settings.inlineSmilesPrefix)) { const source = text.substring(this.settings.inlineSmilesPrefix.length).trim(); const container2 = el.createDiv(); code.replaceWith(container2); ctx.addChild(new SmilesBlock(container2, source, ctx, this.settings)); } }); }; } async onload() { setApp(this.app); await this.loadSettings(); setBlocks(); setObserver(); setCore(this.settings, (error) => { new CoreFallbackModal(this.app, error, async () => { this.settings.core = "smiles-drawer"; await this.saveSettings(); await setFallbackCore(this.settings); }).open(); }); this.addSettingTab(new ChemSettingTab({ app: this.app, plugin: this })); this.registerMarkdownCodeBlockProcessor("smiles", this.smilesProcessor); this.registerMarkdownPostProcessor(this.inlineSmilesProcessor); this.registerEditorExtension(inlinePlugin(this.settings)); this.app.workspace.updateOptions(); if (this.settings.dataview) getDataview(); } async onunload() { detachObserver(); clearBlocks(); clearCore(); clearDataview(); clearApp(); } async loadSettings() { this.settings = migrateSettings(await this.loadData()); } async saveSettings() { await this.saveData(this.settings); } }; /*! Bundled license information: chroma-js/dist/chroma.cjs: (** * chroma.js - JavaScript library for color conversions * * Copyright (c) 2011-2024, Gregor Aisch * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name Gregor Aisch may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ------------------------------------------------------- * * chroma.js includes colors from colorbrewer2.org, which are released under * the following license: * * Copyright (c) 2002 Cynthia Brewer, Mark Harrower, * and The Pennsylvania State University. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the License. * * ------------------------------------------------------ * * Named colors are taken from X11 Color Names. * http://www.w3.org/TR/css3-color/#svg-color * * @preserve *) */ /* nosourcemap */