varri-js 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/vaRRI.js"],
4
+ "sourcesContent": ["/**\n * vaRRI.js \u2014 Browser-only JavaScript port of the vaRRI RNA-RNA interaction \n * visualiser.\n *\n * This library translates the Python vaRRI source into pure browser JavaScript,\n * removing all command-line dependencies (RNAfold, RNAplfold, Playwright).\n * It relies on Fornac (https://github.com/ViennaRNA/fornac) and D3.js which\n * must be loaded before this script.\n *\n * @module vaRRI\n */\n\n(function (global) {\n 'use strict';\n\n // -----------------------------------------------------------------------\n // Constants\n // -----------------------------------------------------------------------\n\n /** Number of invisible gap nodes Fornac inserts between two molecules. */\n const GAP = 3;\n\n /** Metadata type for invisible linear-RRI span constraints. */\n const LINEAR_RRI_LINK_TYPE = 'rri_linear';\n\n /** Metadata type for invisible intramolecular span constraints. */\n const LINEAR_STRUCTURE_LINK_TYPE = 'structure_linear';\n\n /** Fractional per-tick correction used to guide index labels outside a helix rail. */\n const LINEAR_HELIX_LABEL_BIAS_GAIN = 0.2;\n\n /** Maximum label correction per tick, relative to its ordinary link length. */\n const LINEAR_HELIX_LABEL_BIAS_MAX_STEP = 0.05;\n\n /** Small positive half-plane target used only while a label is on the wrong side. */\n const LINEAR_HELIX_LABEL_BIAS_TARGET = 0.1;\n\n /** Active requestAnimationFrame ID for the background-highlight animation loop (null when idle). */\n let _animFrameId = null;\n\n /** Active timeout ID for delayed post-processing after a render (null when idle). */\n let _renderTimeoutId = null;\n\n /** Resolver for the render promise that is currently waiting for post-processing. */\n let _pendingRenderResolve = null;\n\n /** Live Fornac container used by the current render, if any. */\n let _activeContainer = null;\n\n /**\n * Colours used by vaRRI rendering functions initialized to defaults.\n * \n *\n * Change these at runtime with {@link setColors}; the new values take\n * effect on the next call to any rendering function.\n */\n const COLORS = {\n /** Fill colour for nucleotide circles of sequence 1 in strand-colouring mode. */\n sequence1: 'lightblue',\n /** Fill colour for nucleotide circles of sequence 2 in strand-colouring mode. */\n sequence2: '#F4BB44',\n /** Default fill colour for sequence-1 accessibility/profile overlays. */\n seq1profileColor: 'purple',\n /** Default fill colour for sequence-2 accessibility/profile overlays. */\n seq2profileColor: 'red',\n /** Default fill colour for point mutation overlays. */\n mutationColor: 'Darkgreen',\n /** Stroke colour used for intermolecular nucleotide and index-label highlighting. */\n intermolecularHighlight: 'red',\n /** Fill/stroke colour used for background (region / basepair-stack) highlighting. */\n backgroundHighlight: 'red',\n /** Stroke colour used for subsequence-highlighting polylines and circles. */\n subsequenceHighlight: 'purple',\n /** Stroke colour used for basepair links. */\n basepair: 'red',\n };\n\n /** In-memory registries for user-defined annotations. */\n const SUBSEQUENCE_REGISTRY = { items: [], nextId: 1, label: 'Highlight' };\n const REGION_REGISTRY = { items: [], nextId: 1, label: 'Region highlight' };\n const MUTATION_REGISTRY = { items: [], nextId: 1, label: 'Mutation' };\n\n // Short aliases keep rendering code focused on domain objects.\n const SUBSEQUENCE_HIGHLIGHTS = SUBSEQUENCE_REGISTRY.items;\n const REGION_HIGHLIGHTS = REGION_REGISTRY.items;\n const POINT_MUTATIONS = MUTATION_REGISTRY.items;\n\n function clearRegistry(registry) {\n registry.items.length = 0;\n registry.nextId = 1;\n }\n\n function getRegistryItem(registry, id) {\n const item = registry.items.find(candidate => candidate.id === id);\n if (!item) throw new Error(registry.label + ' with id ' + id + ' not found.');\n return item;\n }\n\n function listRegistryItems(registry, cloneItem) {\n return registry.items.map(cloneItem);\n }\n\n function registerRegistryItem(registry, item, cloneItem) {\n item.id = registry.nextId++;\n registry.items.push(item);\n return cloneItem(item);\n }\n\n function removeRegistryItem(registry, id) {\n const index = registry.items.findIndex(item => item.id === id);\n if (index === -1) return false;\n registry.items.splice(index, 1);\n return true;\n }\n\n /**\n * Return a deep-enough clone of a highlight object for external consumers.\n *\n * @param {Object} highlight\n * @returns {Object}\n */\n function cloneSubsequenceHighlight(highlight) {\n return {\n id: highlight.id,\n sequence: highlight.sequence,\n range: highlight.range.map(([start, end]) => [start, end]),\n color: highlight.color,\n alpha: highlight.alpha,\n rangeText: highlight.rangeText,\n };\n }\n\n /**\n * Validate and normalize a sequence selector for subsequence highlighting.\n *\n * @param {string|number} sequence\n * @returns {'1'|'2'}\n */\n function normaliseHighlightSequence(sequence) {\n const seq = String(sequence);\n if (seq !== '1' && seq !== '2') {\n throw new Error('Highlight sequence must be \"1\" or \"2\".');\n }\n return seq;\n }\n\n /**\n * Normalize and validate a highlight range input.\n *\n * @param {string|Array<[number, number]>} rangeInput\n * @param {{id:string, offset:number, length:number}=} context\n * @returns {{range:Array<[number, number]>, rangeText:string}}\n */\n function normaliseHighlightRanges(rangeInput, context) {\n if (typeof rangeInput === 'string') {\n const range = parseSubsequences(\n rangeInput,\n context ? context.offset : undefined,\n context ? context.length : undefined,\n context ? context.id : undefined\n );\n if (!range || range.length === 0) {\n throw new Error('Highlight range must not be empty.');\n }\n return { range, rangeText: rangeInput.trim() };\n }\n\n if (!Array.isArray(rangeInput) || rangeInput.length === 0) {\n throw new Error('Highlight range must not be empty.');\n }\n\n const range = rangeInput.map((pair, idx) => {\n if (!Array.isArray(pair) || pair.length !== 2) {\n throw new Error(`${context.id ? context.id+\": \": \"\"} Invalid subsequence range ${pair} at index ${idx}. Expected [start, end].`);\n }\n const start = Number(pair[0]);\n const end = Number(pair[1]);\n if (!Number.isInteger(start) || !Number.isInteger(end)) {\n throw new Error(`${context.id ? context.id+\": \": \"\"}Invalid subsequence range at index ${idx}. Range bounds must be integers.`);\n }\n if (start === 0 || end === 0) {\n throw new Error(`${context.id ? context.id+\": \": \"\"}Invalid subsequence range at index ${idx}. Index 0 is not valid.`);\n }\n if (start > end) {\n throw new Error(`${context.id ? context.id+\": \": \"\"}Invalid subsequence range at index ${idx}. Start index must be <= end index.`);\n }\n return [start, end];\n });\n\n if (context) {\n parseSubsequences(\n range.map(([start, end]) => `${start}-${end}`).join(','),\n context.offset,\n context.length,\n context.id\n );\n }\n\n return {\n range,\n rangeText: range.map(([start, end]) => `${start}-${end}`).join(','),\n };\n }\n\n /**\n * Build a normalized subsequence-highlight object from user input.\n *\n * @param {{sequence:string|number, range:string|Array<[number, number]>, color?:string, alpha?:number, id?:number}} input\n * @param {{'1'?:{offset:number, length:number}, '2'?:{offset:number, length:number}}=} sequenceContext\n * @returns {{id:number, sequence:'1'|'2', range:Array<[number, number]>, color:string, rangeText:string}}\n */\n function createSubsequenceHighlight(input, sequenceContext = {}) {\n\n const sequence = normaliseHighlightSequence(input.sequence);\n const context = sequenceContext[sequence];\n const normalizedRange = normaliseHighlightRanges(input.range, context);\n const color = (input.color || '').trim() || COLORS.subsequenceHighlight;\n const alpha = input.alpha !== undefined ? Number(input.alpha) : 0.3;\n\n return {\n id: Number.isInteger(input.id) ? input.id : 0,\n sequence,\n range: normalizedRange.range,\n color,\n alpha,\n rangeText: normalizedRange.rangeText,\n };\n }\n\n /**\n * Register a new subsequence highlight object.\n *\n * @param {{sequence:string|number, range:string|Array<[number, number]>, color?:string, alpha?:number}} input\n * @param {{'1'?:{offset:number, length:number}, '2'?:{offset:number, length:number}}=} sequenceContext\n * @returns {Object}\n */\n function registerSubsequenceHighlight(input, sequenceContext = {}) {\n return registerRegistryItem(\n SUBSEQUENCE_REGISTRY,\n createSubsequenceHighlight(input, sequenceContext),\n cloneSubsequenceHighlight\n );\n }\n\n /**\n * Update an existing subsequence highlight object.\n *\n * @param {number} id\n * @param {{sequence?:string|number, range?:string|Array<[number, number]>, color?:string}} patch\n * @param {{'1'?:{offset:number, length:number}, '2'?:{offset:number, length:number}}=} sequenceContext\n * @returns {Object}\n */\n function updateSubsequenceHighlight(id, patch, sequenceContext = {}) {\n const target = getRegistryItem(SUBSEQUENCE_REGISTRY, id);\n\n const normalized = createSubsequenceHighlight({\n id,\n sequence: patch.sequence !== undefined ? patch.sequence : target.sequence,\n range: patch.range !== undefined ? patch.range : target.range,\n color: patch.color !== undefined ? patch.color : target.color,\n alpha: patch.alpha !== undefined ? patch.alpha : target.alpha,\n }, sequenceContext);\n\n Object.assign(target, normalized);\n\n return cloneSubsequenceHighlight(target);\n }\n\n /**\n * Remove a subsequence highlight object by id.\n *\n * @param {number} id\n * @returns {boolean}\n */\n function removeSubsequenceHighlight(id) {\n return removeRegistryItem(SUBSEQUENCE_REGISTRY, id);\n }\n\n /**\n * Remove all registered subsequence highlights.\n */\n function clearSubsequenceHighlights() {\n clearRegistry(SUBSEQUENCE_REGISTRY);\n }\n\n /**\n * Read registered subsequence highlights.\n *\n * @returns {Array<Object>}\n */\n function getSubsequenceHighlights() {\n return listRegistryItems(SUBSEQUENCE_REGISTRY, cloneSubsequenceHighlight);\n }\n\n /**\n * Return a deep-enough clone of a region-highlight object for external consumers.\n *\n * @param {Object} highlight\n * @returns {Object}\n */\n function cloneRegionHighlight(highlight) {\n return {\n id: highlight.id,\n sequence1Range: [highlight.sequence1Range[0], highlight.sequence1Range[1]],\n sequence2Range: [highlight.sequence2Range[0], highlight.sequence2Range[1]],\n color: highlight.color,\n alpha: highlight.alpha,\n rangeText: highlight.rangeText,\n generated: !!highlight.generated,\n };\n }\n\n /**\n * Normalize a range input for region highlighting.\n *\n * @param {string|Array<number|[number, number]>} rangeInput\n * @param {{id: string, offset:number, length:number}=} context\n * @returns {{range:[number, number], rangeText:string}}\n */\n function normaliseRegionRange(rangeInput, context = {}) {\n if (typeof rangeInput === 'string') {\n const ranges = parseSubsequences(rangeInput, context.offset, context.length, context.id);\n if (!ranges || ranges.length === 0) {\n throw new Error('Region range must not be empty.');\n }\n if (ranges.length > 1) {\n throw new Error('Region highlighting supports a single range per sequence.');\n }\n const [start, end] = ranges[0];\n return { range: [start, end], rangeText: rangeInput.trim() };\n }\n\n if (!Array.isArray(rangeInput) || rangeInput.length === 0) {\n throw new Error('Region range must not be empty.');\n }\n\n const pair = rangeInput;\n if (!Array.isArray(pair) || pair.length !== 2) {\n throw new Error('Invalid region range. Expected [start, end].');\n }\n\n const start = Number(pair[0]);\n const end = Number(pair[1]);\n if (!Number.isInteger(start) || !Number.isInteger(end)) {\n throw new Error('Invalid region range. Range bounds must be integers.');\n }\n if (start === 0 || end === 0) {\n throw new Error('Invalid region range. Index 0 is not valid.');\n }\n if (start > end) {\n throw new Error('Invalid region range. Start index must be <= end index.');\n }\n\n if (context) {\n parseSubsequences(`${start}-${end}`, context.offset, context.length, context.id);\n }\n\n return {\n range: [start, end],\n rangeText: `${start}-${end}`,\n };\n }\n\n /**\n * Build a normalized region-highlight object from user input.\n *\n * @param {{sequence1Range:string|[number, number], sequence2Range:string|[number, number], color?:string, alpha?:number, generated?:boolean, id?:number}} input\n * @param {{'1'?:{offset:number, length:number}, '2'?:{offset:number, length:number}}=} sequenceContext\n * @returns {{id:number, sequence1Range:[number, number], sequence2Range:[number, number], color:string, rangeText:string, generated:boolean}}\n */\n function createRegionHighlight(input, sequenceContext = {}) {\n const context1 = sequenceContext['1'];\n const context2 = sequenceContext['2'];\n const seq1Range = normaliseRegionRange(input.sequence1Range, context1);\n const seq2Range = normaliseRegionRange(input.sequence2Range, context2);\n const color = (input.color || '').trim() || COLORS.backgroundHighlight;\n const alpha = input.alpha !== undefined ? Number(input.alpha) : 0.2;\n\n return {\n id: Number.isInteger(input.id) ? input.id : 0,\n sequence1Range: seq1Range.range,\n sequence2Range: seq2Range.range,\n color,\n alpha,\n rangeText: `${seq1Range.rangeText}&${seq2Range.rangeText}`,\n generated: !!input.generated,\n };\n }\n\n /**\n * Register a new region highlight object.\n *\n * @param {{sequence1Range:string|[number, number], sequence2Range:string|[number, number], color?:string, alpha?:number, generated?:boolean}} input\n * @param {{'1'?:{offset:number, length:number}, '2'?:{offset:number, length:number}}=} sequenceContext\n * @returns {Object}\n */\n function registerRegionHighlight(input, sequenceContext = {}) {\n return registerRegistryItem(\n REGION_REGISTRY,\n createRegionHighlight(input, sequenceContext),\n cloneRegionHighlight\n );\n }\n\n /**\n * Update an existing region highlight object.\n *\n * @param {number} id\n * @param {{sequence1Range?:string|[number, number], sequence2Range?:string|[number, number], color?:string, generated?:boolean}} patch\n * @param {{'1'?:{offset:number, length:number}, '2'?:{offset:number, length:number}}=} sequenceContext\n * @returns {Object}\n */\n function updateRegionHighlight(id, patch, sequenceContext = {}) {\n const target = getRegistryItem(REGION_REGISTRY, id);\n\n const normalized = createRegionHighlight({\n id,\n sequence1Range: patch.sequence1Range !== undefined ? patch.sequence1Range : target.sequence1Range,\n sequence2Range: patch.sequence2Range !== undefined ? patch.sequence2Range : target.sequence2Range,\n color: patch.color !== undefined ? patch.color : target.color,\n alpha: patch.alpha !== undefined ? patch.alpha : target.alpha,\n generated: patch.generated !== undefined ? patch.generated : target.generated,\n }, sequenceContext);\n\n Object.assign(target, normalized);\n\n return cloneRegionHighlight(target);\n }\n\n /**\n * Remove a region highlight object by id.\n *\n * @param {number} id\n * @returns {boolean}\n */\n function removeRegionHighlight(id) {\n return removeRegistryItem(REGION_REGISTRY, id);\n }\n\n /**\n * Remove all registered region highlights.\n */\n function clearRegionHighlights() {\n clearRegistry(REGION_REGISTRY);\n }\n\n /**\n * Read registered region highlights.\n *\n * @returns {Array<Object>}\n */\n function getRegionHighlights() {\n return listRegistryItems(REGION_REGISTRY, cloneRegionHighlight);\n }\n\n /**\n * Return a deep-enough clone of a point-mutation object for external consumers.\n *\n * @param {Object} mutation\n * @returns {Object}\n */\n function clonePointMutation(mutation) {\n return {\n id: mutation.id,\n sequence: mutation.sequence,\n position: mutation.position,\n replacement: mutation.replacement,\n reference: mutation.reference,\n nodeId: mutation.nodeId,\n color: mutation.color,\n labelText: mutation.labelText,\n };\n }\n\n /**\n * Validate and normalize a mutation sequence selector.\n *\n * @param {string|number} sequence\n * @returns {'1'|'2'}\n */\n function normaliseMutationSequence(sequence) {\n const seq = String(sequence);\n if (seq !== '1' && seq !== '2') {\n throw new Error('Mutation sequence must be \"1\" or \"2\".');\n }\n return seq;\n }\n\n /**\n * Build a map of valid sequence positions to their bases.\n *\n * @param {{offset:number, sequence:string}|undefined} context\n * @returns {Object.<number, string>}\n */\n function buildSequencePositionMap(context) {\n const map = {};\n if (!context || !Number.isInteger(context.offset) || typeof context.sequence !== 'string') {\n return map;\n }\n\n getSequenceIndices('s', context.offset, context.sequence.length).forEach(([, position], index) => {\n map[position] = context.sequence[index];\n });\n return map;\n }\n\n /**\n * Normalize a mutation position and validate it against the current sequence context.\n *\n * @param {number|string} positionInput\n * @param {{offset:number, sequence:string}|undefined} context\n * @returns {number}\n */\n function normaliseMutationPosition(positionInput, context) {\n if (positionInput === undefined || positionInput === null || positionInput === '') {\n throw new Error('Mutation position must not be empty.');\n }\n const position = validateOffset(String(positionInput));\n\n if (context) {\n const sequencePositionMap = buildSequencePositionMap(context);\n if (!(position in sequencePositionMap)) {\n throw new Error('Mutation position must be a valid sequence index.');\n }\n }\n\n return position;\n }\n\n /**\n * Validate a point-mutation replacement base.\n *\n * @param {string} replacement\n * @returns {string}\n */\n function normaliseMutationReplacement(replacement) {\n const newLetter = String(replacement || '').trim();\n if (newLetter.length !== 1) {\n throw new Error('Mutation replacement must be a single letter.');\n }\n return newLetter;\n }\n\n /**\n * Build a normalized point-mutation object from user input.\n *\n * @param {{sequence:string|number, position:number|string, replacement:string, color?:string, id?:number}} input\n * @param {{'1'?:{offset:number, sequence:string}, '2'?:{offset:number, sequence:string}}=} sequenceContext\n * @returns {{id:number, sequence:'1'|'2', position:number, replacement:string, reference:string, nodeId:number, color:string, labelText:string}}\n */\n function createPointMutation(input, sequenceContext = {}) {\n const sequence = normaliseMutationSequence(input.sequence);\n const context = sequenceContext[sequence];\n const position = normaliseMutationPosition(input.position, context);\n const replacement = normaliseMutationReplacement(input.replacement);\n const color = (input.color || '').trim() || COLORS.intermolecularHighlight;\n\n const referenceMap = context ? buildSequencePositionMap(context) : {};\n const reference = referenceMap[position] || '';\n\n return {\n id: Number.isInteger(input.id) ? input.id : 0,\n sequence,\n position,\n replacement,\n reference,\n nodeId: 0,\n color,\n labelText: `${reference || '?'}${position}${replacement}`,\n };\n }\n\n /**\n * Register a new point mutation.\n *\n * @param {{sequence:string|number, position:number|string, replacement:string, color?:string}} input\n * @param {{'1'?:{offset:number, sequence:string}, '2'?:{offset:number, sequence:string}}=} sequenceContext\n * @returns {Object}\n */\n function registerPointMutation(input, sequenceContext = {}) {\n return registerRegistryItem(\n MUTATION_REGISTRY,\n createPointMutation(input, sequenceContext),\n clonePointMutation\n );\n }\n\n /**\n * Update an existing point mutation.\n *\n * @param {number} id\n * @param {{sequence?:string|number, position?:number|string, replacement?:string, color?:string}} patch\n * @param {{'1'?:{offset:number, sequence:string}, '2'?:{offset:number, sequence:string}}=} sequenceContext\n * @returns {Object}\n */\n function updatePointMutation(id, patch, sequenceContext = {}) {\n const target = getRegistryItem(MUTATION_REGISTRY, id);\n\n const normalized = createPointMutation({\n id,\n sequence: patch.sequence !== undefined ? patch.sequence : target.sequence,\n position: patch.position !== undefined ? patch.position : target.position,\n replacement: patch.replacement !== undefined ? patch.replacement : target.replacement,\n color: patch.color !== undefined ? patch.color : target.color,\n }, sequenceContext);\n\n Object.assign(target, normalized);\n\n return clonePointMutation(target);\n }\n\n /**\n * Remove a point mutation by id.\n *\n * @param {number} id\n * @returns {boolean}\n */\n function removePointMutation(id) {\n return removeRegistryItem(MUTATION_REGISTRY, id);\n }\n\n /**\n * Remove all registered point mutations.\n */\n function clearPointMutations() {\n clearRegistry(MUTATION_REGISTRY);\n }\n\n /**\n * Read registered point mutations.\n *\n * @returns {Array<Object>}\n */\n function getPointMutations() {\n return listRegistryItems(MUTATION_REGISTRY, clonePointMutation);\n }\n\n /**\n * Find the node ID that corresponds to a given sequence position.\n *\n * @param {Object} v\n * @param {'1'|'2'} sequence\n * @param {number} position\n * @returns {number}\n */\n function getNodeIdForSequencePosition(v, sequence, position) {\n for (const [nodeId, [seqName, seqPosition]] of Object.entries(getIndexDictionary(v))) {\n if (seqName === `s${sequence}` && seqPosition === position) {\n return parseInt(nodeId, 10);\n }\n }\n return 0;\n }\n\n /**\n * Override one or more default rendering colours.\n *\n * Only the keys present in `overrides` are changed; all others retain\n * their current values. The new colours take effect on the next call to\n * any rendering function.\n *\n * Valid keys: `sequence1`, `sequence2`, `seq1profileColor`, `seq2profileColor`,\n * `mutationColor`, `intermolecularHighlight`, `backgroundHighlight`, `subsequenceHighlight`, `basepair`.\n *\n * @param {Partial<typeof COLORS>} overrides Key \u2192 CSS-colour-string map.\n */\n function setColors(overrides) {\n Object.assign(COLORS, overrides);\n }\n\n /**\n * Return a shallow copy of the current colour settings.\n *\n * @returns {typeof COLORS}\n */\n function getColors() {\n return { ...COLORS };\n }\n\n // -----------------------------------------------------------------------\n // Utilities (ported from utils.py)\n // -----------------------------------------------------------------------\n\n /**\n * Identify intermolecular basepair positions in a structure string.\n *\n * Analyses a dot-bracket structure and returns positions involved in\n * intermolecular basepairs. Unmatched opening or closing brackets are\n * considered intermolecular.\n *\n * Supports `()`, `[]`, `{}`, `<>` bracket types independently.\n *\n * @param {string} struc Structure string in dot-bracket notation.\n * @param {number} [shift=0] Offset added to every returned index.\n * @returns {Array<[number, string]>} Sorted list of [1-based index, bracket] pairs.\n */\n function listIntermolNodes(struc, shift = 0) {\n const interBasepairs = [];\n const openBasepairs = { '(': [], '<': [], '[': [], '{': [] };\n const bracketPairs = [['(', ')'], ['[', ']'], ['{', '}'], ['<', '>']];\n\n for (let i = 0; i < struc.length; i++) {\n const char = struc[i];\n const index = i + 1; // 1-based\n for (const [open, close] of bracketPairs) {\n if (char === open) {\n openBasepairs[open].push([index + shift, char]);\n break;\n }\n if (char === close) {\n if (openBasepairs[open].length > 0) {\n openBasepairs[open].pop();\n } else {\n interBasepairs.push([index + shift, char]);\n }\n break;\n }\n }\n }\n\n for (const pairs of Object.values(openBasepairs)) {\n interBasepairs.push(...pairs);\n }\n\n interBasepairs.sort((a, b) => a[0] - b[0]);\n return interBasepairs;\n }\n\n // -----------------------------------------------------------------------\n // Input validation (ported from input_validation.py)\n // -----------------------------------------------------------------------\n\n /**\n * Split a string at the first `&` character.\n *\n * Always returns exactly two strings; the second is empty when `&` is absent.\n *\n * @param {string} str\n * @returns {[string, string]}\n */\n function splitAtAmpersand(str) {\n const idx = str.indexOf('&');\n if (idx === -1) return [str, ''];\n return [str.slice(0, idx), str.slice(idx + 1)];\n }\n\n /**\n * Validate a structure string for correctly-paired brackets.\n *\n * Ensures `()`, `<>`, `[]`, `{}` are properly opened and closed.\n *\n * @param {string} structure Dot-bracket structure, may contain `&`.\n * @throws {Error} When bracket counts do not balance.\n */\n function checkStructureInputSimple(structure) {\n const basepairs = { '(': 0, '<': 0, '[': 0, '{': 0 };\n const closingBp = { ')': '(', '>': '<', ']': '[', '}': '{' };\n\n for (const char of structure) {\n if (char in basepairs) {\n basepairs[char]++;\n } else if (char in closingBp) {\n const open = closingBp[char];\n basepairs[open]--;\n if (basepairs[open] < 0) {\n throw new Error(\n `The number of brackets does not line up. Too many closing ${char} brackets:\\n${structure}`\n );\n }\n }\n }\n\n for (const [bp, count] of Object.entries(basepairs)) {\n if (count > 0) {\n throw new Error(\n `The number of brackets does not line up. Too many opening ${bp} brackets:\\n${structure}`\n );\n }\n }\n }\n\n /**\n * Find base-pair indices in a dot-bracket structure string.\n *\n * @param {string} structure\n * @returns {Array<[number, number]>} List of [open, close] index pairs (0-based).\n */\n function findBasePairs(structure) {\n const basepairList = [];\n const openBasepairs = { '(': [], '<': [], '[': [], '{': [] };\n const closingBp = { ')': '(', '>': '<', ']': '[', '}': '{' };\n\n for (let i = 0; i < structure.length; i++) {\n const char = structure[i];\n if (char in openBasepairs) {\n openBasepairs[char].push(i);\n } else if (char in closingBp) {\n const open = closingBp[char];\n if (openBasepairs[open].length > 0) {\n const openIdx = openBasepairs[open].pop();\n basepairList.push([openIdx, i]);\n }\n }\n }\n return basepairList;\n }\n\n /**\n * Validate a sequence string \u2014 must consist of IUPAC nucleotide characters,\n * optionally separated by a single `&`.\n *\n * @param {string} sequence\n * @returns {string} The validated sequence.\n * @throws {Error}\n */\n function validateSequenceInput(sequence) {\n if (sequence === '') throw new Error('No sequence given');\n if (/^([aAcCgGtTuUrRyYsSwWkKmMbBdDhHvVnN]+&)?[aAcCgGtTuUrRyYsSwWkKmMbBdDhHvVnN]+$/.test(sequence)) {\n return sequence;\n }\n // find first invalid character for better error message\n const invalidChars = sequence.replace(/[aAcCgGtTuUrRyYsSwWkKmMbBdDhHvVnN&]/g, '');\n throw new Error(`The given sequence input has invalid none-IUPAC characters: ${invalidChars}`);\n }\n\n /**\n * Validate cropping input. Must be an integer string, and disallowed for\n * @param {string} cropping (integer string to be validated)\n * @param {string} structure Validated structure string, used to check for unpaired-only structures)\n * @returns the validated cropping string\n * @throws {Error} When cropping is not a valid integer or when cropping is disallowed for unpaired-only structures.\n */\n function validateCroppingInput(structure, cropping) {\n // check if cropping is not set, return default value\n if (!cropping) return '-1'; // default value\n\n // check if cropping is a valid integer string\n if (!/^-?\\d+$/.test(cropping)) {\n throw new Error(`The given cropping input is not an integer: ${cropping}`);\n }\n \n // negative cropping is indicating no cropping, return -1\n if (parseInt(cropping, 10) < 0) return -1; \n\n // check if structure is only composed of dots (unpaired) and if so, disallow cropping\n if( structure ) {\n if (!structure.match(/[^.&]/)) {\n throw new Error('Cropping is not allowed for structures with only unpaired nucleotides.');\n }\n if (structure.includes('&')) {\n // check structure of the first molecule (before &) if present\n const [struc1, struc2] = splitAtAmpersand(structure);\n if (!struc1.match(/[^.]/) || !struc2.match(/[^.]/)) {\n throw new Error('Cropping is not allowed for structures with only unpaired nucleotides in either molecule.');\n }\n }\n }\n return cropping;\n }\n\n\n\n /**\n * Validate a structure string in dot-bracket notation.\n *\n * @param {string} structure\n * @param {string} sequence Used to check length parity when `&` is present.\n * @returns {string} The validated structure.\n * @throws {Error}\n */\n function validateStructureInput(structure, sequence) {\n if (structure === '') throw new Error('No structure given');\n\n if (structure.includes('&')) {\n const [struc1, struc2] = splitAtAmpersand(structure);\n const [seq1, seq2] = splitAtAmpersand(sequence);\n for (const [idx, struc, seq] of [[1, struc1, seq1], [2, struc2, seq2]]) {\n if (struc.length !== seq.length) {\n throw new Error(\n `Structure length (${struc.length}) and Sequence length (${seq.length}) ` +\n `of molecule ${idx} do not match`\n );\n }\n }\n } else {\n if (structure.length !== sequence.length) {\n throw new Error(\n `Structure length (${structure.length}) and Sequence length (${sequence.length}) do not match`\n );\n }\n }\n\n if (/^([\\.()<>\\[\\]{}]+&)?[\\.()<>\\[\\]{}]+$/.test(structure)) {\n checkStructureInputSimple(structure);\n return structure;\n }\n throw new Error(`The given structure input is not valid: ${structure}`);\n }\n\n /**\n * Validate an offset value.\n *\n * @param {string} offsetStr String representation of the offset.\n * @returns {number}\n * @throws {Error}\n */\n function validateOffset(offsetStr) {\n if (offsetStr === '0') throw new Error('Index 0 is not valid; use a value of -1 or less, or 1 or greater');\n if (/^-?\\d+$/.test(offsetStr)) return parseInt(offsetStr, 10);\n throw new Error(`The given index input is not valid: ${offsetStr}`);\n }\n\n /**\n * Validate the highlighting option.\n *\n * @param {string} highlighting\n * @returns {string}\n * @throws {Error}\n */\n function validateHighlighting(highlighting) {\n const valid = ['nothing', 'basepairs', 'region'];\n if (valid.includes(highlighting)) return highlighting;\n throw new Error(\n `The given highlighting input (${highlighting}) is not accepted [nothing, basepairs, region]`\n );\n }\n\n /**\n * Validate the backgroundhighlighting option.\n *\n * @param {string} bgHighlighting\n * @returns {string}\n * @throws {Error}\n */\n function validateBackgroundhighlighting(bgHighlighting) {\n const valid = ['nothing', 'basepairs', 'region'];\n if (valid.includes(bgHighlighting)) return bgHighlighting;\n throw new Error(\n `The given backgroundhighlighting input (${bgHighlighting}) is not accepted [nothing, basepairs, region]`\n );\n }\n\n /**\n * Split structure string and apply the Fornac `&...` fix.\n *\n * Fornac incorrectly cuts the first 2 nodes of the second sequence when\n * the separator is exactly `&`. Inserting `&...` compensates for this.\n *\n * @param {string} structure Raw structure (may contain `&`).\n * @returns {{structure1: string, structure2: string, structure: string, structure_dict: Object}}\n */\n function formatStructure(structure) {\n const [first, second] = splitAtAmpersand(structure);\n\n // Fix: Fornac incorrectly cuts the first 2 nodes of the second sequence\n // when the separator is exactly `&`. Inserting 3 gap dots compensates.\n // Build strings explicitly from the already-split parts to avoid\n // partial-replacement ambiguity on the `&` character.\n const fixedStructure = second !== '' ? first + '&...' + second : first;\n const bareStructure = second !== '' ? first + '...' + second : first;\n\n const structureDict = {};\n for (let i = 0; i < bareStructure.length; i++) {\n structureDict[String(i + 1)] = bareStructure[i];\n }\n\n return { structure1: first, structure2: second, structure: fixedStructure, structure_dict: structureDict };\n }\n\n /**\n * Split sequence string and apply the Fornac `&...` fix.\n *\n * @param {string} sequence Raw sequence (may contain `&`).\n * @returns {{sequence1: string, sequence2: string, sequence: string, sequence_dict: Object}}\n */\n function formatSequence(sequence) {\n const [first, second] = splitAtAmpersand(sequence);\n\n // Same Fornac fix as formatStructure \u2014 build from split parts explicitly.\n const fixedSequence = second !== '' ? first + '&...' + second : first;\n const bareSequence = second !== '' ? first + '...' + second : first;\n\n const sequenceDict = {};\n for (let i = 0; i < bareSequence.length; i++) {\n sequenceDict[String(i + 1)] = bareSequence[i];\n }\n\n return { sequence1: first, sequence2: second, sequence: fixedSequence, sequence_dict: sequenceDict };\n }\n\n /**\n * Determine how many molecules are given (`\"1\"` or `\"2\"`).\n *\n * @param {{sequence2: string}} validated\n * @returns {\"1\"|\"2\"}\n */\n function getMolecules(validated) {\n return validated.sequence2 !== '' ? '2' : '1';\n }\n\n /**\n * Generate indexed sequence positions with RNA-style numbering (skipping 0).\n *\n * @param {string} seqId Sequence identifier, e.g. `\"s1\"`.\n * @param {number} offset Starting index.\n * @param {number} length Length of the sequence.\n * @returns {Array<[string, number]>} Array of [seqId, index] pairs.\n */\n function getSequenceIndices(seqId, offset, length) {\n const indices = [];\n for (let i = offset; i < offset + length; i++) {\n indices.push([seqId, i]);\n }\n // RNA-style: skip 0\n const zeroIdx = indices.findIndex(([, n]) => n === 0);\n if (zeroIdx !== -1) {\n indices.splice(zeroIdx, 1);\n const [seq, lastNum] = indices[indices.length - 1];\n indices.push([seq, lastNum + 1]);\n }\n return indices;\n }\n\n /**\n * Build a mapping from Fornac node ID (1-based) to [sequenceId, position].\n *\n * @param {{offset1: number, offset2: number, sequence1: string, sequence2: string}} v\n * @returns {Object.<number, [string, number]>}\n */\n function getIndexDictionary(v) {\n const { offset1, offset2, sequence1, sequence2 } = v;\n const gapList = Array.from({ length: GAP }, () => ['e', 0]);\n\n const indices = [\n ...getSequenceIndices('s1', offset1, sequence1.length),\n ...gapList,\n ...getSequenceIndices('s2', offset2, sequence2.length),\n ];\n\n const dict = {};\n indices.forEach(([seq, num], i) => {\n dict[i + 1] = [seq, num];\n });\n return dict;\n }\n\n /**\n * Crop leading and trailing unpaired nucleotides from sequences and structures.\n * \n * @param {string} rawSeq \n * @param {string} validStruc \n * @param {integer} offset1 \n * @param {integer} offset2 \n * @param {integer} cropping \n * @returns Object with updated rawSeq, validStruc, offset1, offset2\n */\n function applyCropping(rawSeq, validStruc, offset1, offset2, cropping) {\n\n // check if cropping is not set or is negative, return original values\n if( !cropping || cropping < 0 ) {\n return { rawSeq, validStruc, offset1, offset2 };\n }\n\n let seq = rawSeq.split('&');\n let str = validStruc.split('&');\n let off = [offset1, offset2];\n \n for (let i = 0; i < seq.length; i++) {\n // leading cropping\n let unpairedLeading = str[i].match(/^\\.+/);\n if (unpairedLeading && unpairedLeading[0].length > cropping) {\n seq[i] = seq[i].slice(unpairedLeading[0].length - cropping);\n str[i] = str[i].slice(unpairedLeading[0].length - cropping);\n const offOld = off[i];\n off[i] += unpairedLeading[0].length - cropping;\n if (off[i] >= 0 && offOld < 0) { off[i] += 1; } // skip 0\n }\n // trailing cropping\n let trailing = str[i].match(/\\.+$/);\n if (trailing && trailing[0].length > cropping) {\n seq[i] = seq[i].slice(0, seq[i].length - (trailing[0].length - cropping));\n str[i] = str[i].slice(0, str[i].length - (trailing[0].length - cropping));\n }\n }\n \n // return updated values\n return { rawSeq: seq.join(\"&\"), validStruc: str.join(\"&\"), offset1: off[0], offset2: off[1] };\n }\n\n /**\n * Validate all inputs and return a `validated` parameter object ready for rendering.\n *\n * @param {Object} args Raw input parameters.\n * @param {string} args.structure Dot-bracket structure, one or two molecules separated by `&`.\n * @param {string} args.sequence RNA sequence, one or two molecules separated by `&`.\n * @param {string} [args.cropping=\"-1\"] Cropping value (integer string).\n * @param {string} [args.startIndex1=\"1\"] Start index for sequence 1.\n * @param {string} [args.startIndex2=\"1\"] Start index for sequence 2.\n * @param {string} [args.labelInterval=\"10\"] Interval for index label display.\n * @param {string} [args.coloring=\"strand\"] Coloring option: `\"strand\"` or `\"loop\"`.\n * @param {string} [args.highlighting=\"region\"] Highlighting option: `\"nothing\"`, `\"basepairs\"`, `\"region\"`.\n * @param {string} [args.backgroundhighlighting=\"basepairs\"] Background-highlighting option.\n * @param {boolean} [args.distinctBpTypes=true] Whether to display G-U basepairs as dashed lines.\n * @param {Array<{sequence:string|number, range:string|Array<[number, number]>, color?:string}>} [args.subsequenceHighlights=[]]\n * Generic subsequence-highlight definitions.\n * @returns {Object} Validated parameter dictionary.\n * @throws {Error} On invalid input.\n */\n function validate(args) {\n const v = {};\n\n // Sequence\n const rawSeq = (args.sequence || '').trim();\n validateSequenceInput(rawSeq);\n \n // Structure\n const rawStruc = (args.structure || '').trim();\n const validStruc = validateStructureInput(rawStruc, rawSeq);\n \n // Offsets\n v.offset1 = validateOffset(String(args.startIndex1 || '1'));\n v.offset2 = validateOffset(String(args.startIndex2 || '1'));\n \n // Cropping\n const cropping = validateCroppingInput(validStruc, String(args.cropping || '-1'));\n\n // update sequences, structures and offsets based on cropping\n const cropped = applyCropping(rawSeq, validStruc, v.offset1, v.offset2, cropping);\n\n // update offset information\n v.offset1 = cropped.offset1;\n v.offset2 = cropped.offset2;\n\n // create the formatted sequence and structure objects\n const seqFmt = formatSequence(cropped.rawSeq);\n Object.assign(v, seqFmt);\n const strucFmt = formatStructure(cropped.validStruc);\n Object.assign(v, strucFmt);\n\n // Molecules\n v.molecules = getMolecules(v);\n\n // Options\n v.coloring = args.coloring || 'strand';\n v.highlighting = validateHighlighting(args.highlighting || 'region');\n v.backgroundhighlighting = validateBackgroundhighlighting(\n args.backgroundhighlighting || 'basepairs'\n );\n v.distinctBpTypes = args.distinctBpTypes !== false; // default true\n v.labelInterval = parseInt(String(args.labelInterval || '10'), 10) || 10;\n\n // Subsequence highlights\n const sequenceContext = {\n '1': { offset: v.offset1, length: v.sequence1.length, sequence: v.sequence1 },\n '2': { offset: v.offset2, length: v.sequence2.length, sequence: v.sequence2 },\n };\n\n if (Array.isArray(args.subsequenceHighlights)) {\n v.subsequenceHighlights = args.subsequenceHighlights.map(h =>\n createSubsequenceHighlight(h, sequenceContext)\n );\n } else {\n v.subsequenceHighlights = [];\n }\n\n if (Array.isArray(args.regionHighlights)) {\n v.regionHighlights = args.regionHighlights.map(highlight =>\n createRegionHighlight(highlight, sequenceContext)\n );\n } else {\n v.regionHighlights = [];\n }\n\n if (Array.isArray(args.pointMutations)) {\n v.pointMutations = args.pointMutations.map(mutation =>\n createPointMutation(mutation, sequenceContext)\n );\n\n const seenMutationPositions = new Set();\n v.pointMutations.forEach(mutation => {\n const key = `${mutation.sequence}:${mutation.position}`;\n if (seenMutationPositions.has(key)) {\n throw new Error(`Duplicate point mutation at ${key}.`);\n }\n seenMutationPositions.add(key);\n\n mutation.nodeId = getNodeIdForSequencePosition(v, mutation.sequence, mutation.position);\n if (!mutation.nodeId) {\n throw new Error(`Mutation position ${mutation.position} is not visible in the current rendering.`);\n }\n mutation.labelText = `${mutation.reference || '?'}${mutation.position}${mutation.replacement}`;\n });\n } else {\n v.pointMutations = [];\n }\n\n return v;\n }\n\n /**\n * Parse a comma-separated list of `\"start-end\"` range strings.\n *\n * @param {string|null|undefined} input\n * @param {number} [startIndex]\n * @param {number} [sequenceLength]\n * @param {string|null|undefined} [sequenceId]\n * @returns {Array<[number,number]>|null}\n */\n function parseSubsequences(input, startIndex, sequenceLength, sequenceId) {\n if (!input || input.trim() === '') return null;\n let validIndices = null;\n if (Number.isInteger(startIndex) && Number.isInteger(sequenceLength) && sequenceLength >= 0) {\n validIndices = new Set(\n getSequenceIndices('s', startIndex, sequenceLength).map(([, index]) => index)\n );\n }\n const ranges = input.split(',').map(s => s.trim()).filter(Boolean);\n return ranges.map(r => {\n const match = r.match(/^(-?\\d+)-(-?\\d+)$/);\n if (!match) {\n throw new Error(`${sequenceId ? sequenceId+\": \" : \"\"}Invalid subsequence range: \"${r}\". Expected \"start-end\".`);\n }\n const start = parseInt(match[1], 10);\n const end = parseInt(match[2], 10);\n\n if (start === 0 || end === 0) {\n throw new Error(`${sequenceId ? sequenceId+\": \" : \"\"}Invalid subsequence range: \"${r}\". Index 0 is not valid.`);\n }\n if (start > end) {\n throw new Error(`${sequenceId ? sequenceId+\": \" : \"\"}Invalid subsequence range: \"${r}\". Start index must be <= end index.`);\n }\n if (validIndices && (!validIndices.has(start) || !validIndices.has(end))) {\n throw new Error(\n `${sequenceId ? sequenceId+\": \" : \"\"}Invalid subsequence range: \"${r}\". Range endpoints must be valid sequence indices.`\n );\n }\n\n return [start, end];\n });\n }\n\n // -----------------------------------------------------------------------\n // DOM modification helpers (ported from modifications.py)\n // -----------------------------------------------------------------------\n\n /**\n * Set an attribute on all elements that match `[targetAttr=\"targetValue\"]`.\n *\n * @param {string} targetAttr\n * @param {string} targetValue\n * @param {string} setAttr\n * @param {string} setValue\n */\n function setAttributeForElements(targetAttr, targetValue, setAttr, setValue) {\n document.querySelectorAll(`[${targetAttr}=\"${targetValue}\"]`).forEach(el => {\n el.setAttribute(setAttr, setValue);\n });\n }\n\n /**\n * Generate a color list for two sequences.\n *\n * Each nucleotide in `seq1` maps to {@link COLORS.sequence1};\n * each nucleotide in `seq2` maps to {@link COLORS.sequence2}.\n *\n * @param {string} seq1\n * @param {string} seq2\n * @returns {string[]}\n */\n function sequenceColoring(seq1, seq2) {\n return [\n ...Array.from(seq1, () => COLORS.sequence1),\n ...Array.from(seq2, () => COLORS.sequence2),\n ];\n }\n\n /**\n * Apply strand-based coloring to all nucleotide circles in the Fornac plot.\n *\n * @param {{sequence1: string, sequence2: string}} v\n */\n function changeBackgroundColor(v) {\n const coloring = sequenceColoring(v.sequence1, v.sequence2);\n if (coloring.length === 0) return;\n const nodes = document.querySelectorAll('[r=\"5\"]');\n nodes.forEach((node, index) => {\n node.setAttribute('style', `fill: ${coloring[index]};`);\n });\n }\n\n /**\n * Assign `start` and `end` attributes to every `<line>` link element.\n *\n * Fornac stores link identity in a tooltip text child; this function\n * parses it and promotes the IDs to proper attributes.\n */\n function setLinksId() {\n document.querySelectorAll('line').forEach(line => {\n const textContent = line.children[0] && line.children[0].textContent;\n if (!textContent) return;\n const parts = textContent.split(':')[1];\n if (!parts) return;\n const ids = parts.split('-').filter(x => !isNaN(parseInt(x, 10)) && x !== '');\n if (ids.length >= 2) {\n line.setAttribute('start', ids[0].trim());\n line.setAttribute('end', ids[1].trim());\n }\n });\n }\n\n /**\n * Assign sequential `label_gnum` / `label_num` IDs to label elements.\n */\n function setLabelsId() {\n document.querySelectorAll('g[num=\"n-1\"]').forEach((label, index) => {\n label.setAttribute('label_gnum', String(index + 1));\n if (label.firstChild) {\n label.firstChild.setAttribute('label_num', String(index + 1));\n }\n });\n }\n\n /**\n * Update node tooltip text to display correct sequence and index labels.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function updateNodeToolTips(v) {\n const indexDict = getIndexDictionary(v);\n for (const [key, [seq, num]] of Object.entries(indexDict)) {\n document.querySelectorAll(`circle[node_num=\"${key}\"]`).forEach(node => {\n if (node.firstChild) {\n node.firstChild.innerHTML = `${seq}[${num}]`;\n }\n });\n }\n }\n\n /**\n * Validate whether a label marker should be placed at the given position.\n *\n * Prevents two adjacent markers from being displayed simultaneously.\n *\n * @param {number} pos\n * @param {Object.<number, number>} indexing\n * @param {number} number\n * @returns {number} The number to place, or 0 to suppress.\n */\n function validateLabelPos(pos, indexing, number) {\n for (const neighbor of [pos - 1, pos + 1]) {\n if (neighbor in indexing && indexing[neighbor] !== 0) {\n return 0;\n }\n }\n return number;\n }\n\n /**\n * Apply the intermolecular-highlight stroke style to the label at the given index.\n *\n * @param {number} targetIndex\n */\n function highlightLabel(targetIndex) {\n document.querySelectorAll(`[label_num=\"${targetIndex}\"]`).forEach(label => {\n label.setAttribute('style', `stroke: ${COLORS.intermolecularHighlight};stroke-width: 0.8;`);\n });\n }\n\n /**\n * Set or update the SVG title used as a hover tooltip for a label.\n *\n * @param {SVGElement} label\n * @param {string} text\n */\n function setLabelTooltip(label, text) {\n const parent = label.parentElement;\n if (!parent) return;\n\n const existingTitleOnLabel = label.querySelector('title');\n if (existingTitleOnLabel) existingTitleOnLabel.remove();\n\n let title = parent.querySelector('title');\n if (!title) {\n title = document.createElementNS('http://www.w3.org/2000/svg', 'title');\n parent.insertBefore(title, parent.firstChild);\n }\n title.textContent = text;\n }\n\n /**\n * Remove label group elements at the given index.\n *\n * @param {number} index\n */\n function removeLabel(index) {\n document.querySelectorAll(`[label_gnum=\"${index}\"]`).forEach(node => node.remove());\n }\n\n /**\n * Remove label-link line elements at the given index.\n *\n * @param {number} index\n */\n function removeLabelLink(index) {\n document.querySelectorAll(`line[start=\"${index}\"]`).forEach(line => {\n if (line.getAttribute('link_type') === 'label_link') {\n line.remove();\n }\n });\n }\n\n /** Return every combined nucleotide position and its selected index label. */\n function getIndexLabelValues(v) {\n const { structure1, structure2, sequence1, labelInterval, molecules, sequence_dict } = v;\n const length1 = sequence1.length;\n const lengthTotal = Object.keys(sequence_dict).length;\n const indexDict = getIndexDictionary(v);\n const indexLabels = {};\n for (const key of Object.keys(indexDict)) {\n indexLabels[parseInt(key, 10)] = 0;\n }\n\n // Priority 1 \u2014 sequence boundaries\n for (const pos of [1, length1, length1 + GAP + 1, lengthTotal]) {\n if (!(pos in indexDict)) break;\n const [, number] = indexDict[pos];\n indexLabels[pos] = validateLabelPos(pos, indexLabels, number);\n }\n\n // Priority 2 \u2014 intermolecular basepair region boundaries\n if (molecules === '2') {\n const basepairRegion = getIntermolBasepairRegion(structure1, structure2);\n for (const region of basepairRegion) {\n for (const pos of region) {\n if (!(pos in indexDict)) continue;\n const [, number] = indexDict[pos];\n indexLabels[pos] = validateLabelPos(pos, indexLabels, number);\n }\n }\n }\n\n // Priority 3 \u2014 every labelInterval\n for (const [posStr, [, number]] of Object.entries(indexDict)) {\n const pos = parseInt(posStr, 10);\n if (number % labelInterval === 0 || number === 1) {\n indexLabels[pos] = validateLabelPos(pos, indexLabels, number);\n }\n }\n\n return indexLabels;\n }\n\n /**\n * Set index labels on the Fornac plot using a priority system.\n *\n * Priority order (highest \u2192 lowest):\n * 1. Start/end of each sequence.\n * 2. Start/end of intermolecular basepair region.\n * 3. Every `labelInterval`-th position.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function setIndexLabels(v) {\n const indexLabels = getIndexLabelValues(v);\n const mutationByNodeId = {};\n (Array.isArray(v.pointMutations) ? v.pointMutations : []).forEach(mutation => {\n if (mutation.nodeId) mutationByNodeId[mutation.nodeId] = mutation;\n });\n\n if (v.molecules === '2') {\n getIntermolBasepairRegion(v.structure1, v.structure2)\n .flat()\n .forEach(highlightLabel);\n }\n\n // Apply labels\n const labelValues = Object.entries(indexLabels);\n document.querySelectorAll('[label_type=\"label\"]').forEach((label, index) => {\n const [posStr, value] = labelValues[index] || [];\n const pos = posStr ? parseInt(posStr, 10) : 0;\n const mutation = pos && mutationByNodeId[pos] ? mutationByNodeId[pos] : null;\n\n if (mutation) {\n label.innerHTML = mutation.replacement;\n setLabelTooltip(label, `Mutation: ${mutation.labelText}`);\n label.setAttribute('style', `fill: ${mutation.color}; stroke: ${mutation.color}; stroke-width: 0.2; font-weight: bolder;`);\n addStyleToNodes([mutation.nodeId], `stroke: ${mutation.color}; stroke-width: 2px;`);\n return;\n }\n\n label.removeAttribute('style');\n const parent = label.parentElement;\n const existingTitle = parent?.querySelector('title');\n if (existingTitle) existingTitle.remove();\n label.innerHTML = value !== undefined ? value : '';\n });\n\n // Remove suppressed labels\n for (const [posStr, value] of Object.entries(indexLabels)) {\n const pos = parseInt(posStr, 10);\n if (value === 0 && !mutationByNodeId[pos]) {\n removeLabel(pos);\n removeLabelLink(pos);\n }\n }\n }\n\n /**\n * Update tooltip text on link elements to display correct index values.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function updateLinkTooltips(v) {\n const updatedIndices = {};\n for (const [key, [, index]] of Object.entries(getIndexDictionary(v))) {\n updatedIndices[String(key)] = String(index);\n }\n document.querySelectorAll('line').forEach(line => {\n const start = line.getAttribute('start');\n const end = line.getAttribute('end');\n if (!line.firstChild) return;\n if (line.getAttribute('link_type') === 'label_link') {\n line.firstChild.textContent = updatedIndices[start] || '';\n } else {\n line.firstChild.textContent =\n (updatedIndices[start] || '') + '-' + (updatedIndices[end] || '');\n }\n });\n }\n\n /**\n * Apply a CSS style string to an array of nodes by `node_num`.\n *\n * @param {number[]} nodeIds\n * @param {string} style\n */\n function addStyleToNodes(nodeIds, style) {\n nodeIds.forEach(nodeId => {\n document.querySelectorAll(`circle[node_num=\"${nodeId}\"]`).forEach(node => {\n node.setAttribute('style', (node.getAttribute('style') || '') + style);\n });\n });\n }\n\n /**\n * Retrieve the x,y position of a Fornac node from its `transform` attribute.\n *\n * @param {number} nodeId\n * @returns {number[]} [x, y] coordinates.\n */\n function getPositionOfNode(nodeId) {\n const pos = [];\n document.querySelectorAll(`g[num=\"n${nodeId}\"]`).forEach(node => {\n const transform = node.getAttribute('transform') || '';\n const matches = [...transform.matchAll(/-?\\d+(?:\\.\\d+)?/g)];\n matches.forEach(([val]) => pos.push(parseFloat(val)));\n });\n return pos;\n }\n\n /**\n * Resolve where new overlay elements should be inserted.\n *\n * If a vaRRI rotation layer exists, insert into that layer so newly added\n * overlays follow the current rotation.\n *\n * @returns {SVGElement|null}\n */\n function getPlotInsertRoot() {\n const plot = document.getElementsByClassName('fornac-plot')[0];\n if (!plot) return null;\n\n const rotationLayer = Array.from(plot.children).find(child =>\n child.tagName && child.tagName.toLowerCase() === 'g' &&\n child.getAttribute('data-varri-rotation-layer') === 'true'\n );\n\n return rotationLayer || plot;\n }\n\n /**\n * Create and insert an SVG element at the beginning of the Fornac plot.\n *\n * @param {string} elementType SVG tag name (e.g. `\"circle\"`, `\"polyline\"`).\n * @param {Object.<string,string>} attr Attribute key\u2192value map.\n */\n function addElement(elementType, attr) {\n const el = document.createElementNS('http://www.w3.org/2000/svg', elementType);\n for (const [key, value] of Object.entries(attr)) {\n el.setAttribute(key, value);\n }\n const insertRoot = getPlotInsertRoot();\n if (insertRoot) insertRoot.insertBefore(el, insertRoot.firstChild);\n }\n\n /**\n * Resolve the x/y coordinates of a list of Fornac node IDs.\n *\n * @param {number[]} indices Fornac node IDs to resolve.\n * @returns {Array<[number, number]>}\n */\n function getNodePointPairs(indices) {\n const points = [];\n indices.forEach(index => {\n document.querySelectorAll(`g[num=\"n${index}\"]`).forEach(node => {\n const transform = node.getAttribute('transform') || '';\n const match = [...transform.matchAll(/-?\\d+(?:\\.\\d+)?/g)];\n if (match.length >= 2) {\n points.push([parseFloat(match[0][0]), parseFloat(match[1][0])]);\n }\n });\n });\n return points;\n }\n\n /**\n * Close a polygon point list by appending the first point at the end.\n *\n * @param {Array<[number, number]>} points\n * @returns {string[]}\n */\n function closePolygonPoints(points) {\n if (!Array.isArray(points) || points.length === 0) return [];\n const pointStrings = points.map(([x, y]) => `${x},${y}`);\n if (pointStrings.length < 2) return pointStrings;\n return [...pointStrings, pointStrings[0]];\n }\n\n function insertSvgShape(tagName, pointString, style, extraAttrs) {\n const shape = document.createElementNS('http://www.w3.org/2000/svg', tagName);\n shape.setAttribute('points', pointString);\n shape.setAttribute('style', style);\n for (const [name, value] of Object.entries(extraAttrs)) {\n shape.setAttribute(name, value);\n }\n const insertRoot = getPlotInsertRoot();\n if (insertRoot) insertRoot.insertBefore(shape, insertRoot.firstChild);\n }\n\n /**\n * Draw a polyline connecting a list of Fornac node positions.\n *\n * @param {number[]} indices Fornac node IDs to connect.\n * @param {string} style CSS style string for the polyline.\n */\n function polyline(indices, style, extraAttrs = {}) {\n const points = getNodePointPairs(indices);\n const pointString = points.map(([x, y]) => `${x},${y}`).join(' ');\n\n insertSvgShape('polyline', pointString, style, extraAttrs);\n }\n\n /**\n * Draw a closed polygon connecting a list of Fornac node positions.\n *\n * @param {number[]} indices Fornac node IDs to connect.\n * @param {string} style CSS style string for the polygon.\n */\n function polygon(indices, style, extraAttrs = {}) {\n const points = getNodePointPairs(indices);\n const pointString = closePolygonPoints(points).join(' ');\n\n insertSvgShape('polygon', pointString, style, extraAttrs);\n }\n\n /**\n * Compute [start, end] ranges of intermolecular basepair regions.\n *\n * @param {string} structure1\n * @param {string} structure2\n * @returns {Array<[number, number]>}\n */\n function getIntermolBasepairRegion(structure1, structure2) {\n const basepairRegion = [];\n const offset = structure1.length + GAP;\n\n for (const [structure, shift] of [[structure1, 0], [structure2, offset]]) {\n const basepairList = listIntermolNodes(structure, shift).map(([idx]) => idx);\n if (basepairList.length === 0) return [];\n basepairRegion.push([basepairList[0], basepairList[basepairList.length - 1]]);\n }\n return basepairRegion;\n }\n\n /**\n * Highlight nodes in the intermolecular basepair region with a stroke.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function highlightRegion(v) {\n const basepairRegion = getIntermolBasepairRegion(v.structure1, v.structure2);\n const intermolNodes = [];\n for (const [start, end] of basepairRegion) {\n for (let i = start; i <= end; i++) intermolNodes.push(i);\n }\n addStyleToNodes(intermolNodes, `stroke: ${COLORS.intermolecularHighlight};`);\n }\n\n /**\n * Highlight individual intermolecular basepair nodes with a stroke.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function highlightBasepairs(v) {\n const split = v.sequence1.length + 1;\n // Highlight all nodes that are part of intermolecular basepairs of main layouting (basepair) or 2ndary layouting (pseudoknot)\n for (const type of [\"basepair\", \"pseudoknot\"]) {\n document.querySelectorAll(`[link_type=\"${type}\"]`).forEach(link => {\n const nodes = [\n parseInt(link.getAttribute('start'), 10),\n parseInt(link.getAttribute('end'), 10),\n ];\n if (!(nodes[0] < split && nodes[1] > split)) return;\n nodes.forEach(nodeNum => {\n const node = document.querySelector(`circle[node_num=\"${nodeNum}\"]`);\n if (node) {\n node.setAttribute('style', (node.getAttribute('style') || '') + `stroke: ${COLORS.intermolecularHighlight};`);\n }\n });\n });\n }\n }\n\n /**\n * Remove duplicate basepair links (keep only links where start < end).\n */\n function removeSecondLink() {\n document.querySelectorAll('[link_type=\"basepair\"]').forEach(link => {\n const start = parseInt(link.getAttribute('start'), 10);\n const end = parseInt(link.getAttribute('end'), 10);\n if (start > end) link.remove();\n });\n }\n\n /**\n * Remove a Fornac node group element by ID.\n *\n * @param {number} id\n */\n function removeNode(id) {\n document.querySelectorAll(`[num=\"n${id}\"]`).forEach(node => node.remove());\n }\n\n /**\n * Remove the directional arrow from a node.\n *\n * @param {number} id\n */\n function removeArrow(id) {\n document.querySelectorAll(`[num=\"n${id}\"]`).forEach(node => {\n if (node.firstChild) node.firstChild.remove();\n });\n }\n\n /**\n * Remove a backbone link between two nodes.\n *\n * @param {number} startId\n * @param {number} endId\n */\n function removeLink(startId, endId) {\n const targetIds = `${startId},${endId}`;\n document.querySelectorAll('[link_type=\"backbone\"]').forEach(link => {\n const ids = `${link.getAttribute('start')},${link.getAttribute('end')}`;\n if (ids === targetIds) link.remove();\n });\n }\n\n /**\n * Remove dummy gap nodes that Fornac inserts between two molecules.\n *\n * @param {string} sequence The combined sequence string (with `&` and fix dots).\n */\n function removeDummyNodes(sequence) {\n for (let index = 0; index < sequence.length; index++) {\n if (sequence[index] === '.') {\n removeLink(index, index + 1);\n removeArrow(index + 1);\n removeNode(index);\n }\n }\n }\n\n /**\n * Highlight subsequence ranges with polyline/circle overlays.\n *\n * @param {Object} v Validated parameter dictionary.\n * @param {\"1\"|\"2\"} seq Which sequence to highlight.\n * @param {Array<[number, number]>} range Parsed index range.\n * @param {string} color Highlight color.\n * @param {number} Highlight opacity.\n */\n function highlightSubsequence(v, seq, range, color, alpha) {\n const highlightDiameter = 14;\n const keyOffset = `offset${seq}`;\n\n // Map RNA index \u2192 Fornac web node id for the relevant sequence\n const indexDict = {};\n for (const [web, [mol, index]] of Object.entries(getIndexDictionary(v))) {\n if (mol === `s${seq}`) {\n indexDict[index] = parseInt(web, 10);\n }\n }\n\n const shift = seq === '2' ? v.sequence1.length + GAP : 0;\n\n for (const [start, end] of (range || [])) {\n const startIndex = v[keyOffset];\n\n if (start === end) {\n const webId = indexDict[start];\n const [x, y] = getPositionOfNode(webId);\n addElement('circle', {\n cx: String(x),\n cy: String(y),\n r: `${Math.ceil(highlightDiameter/2)}px`,\n style: `fill:${color};opacity:${alpha};`,\n 'data-varri-subseq': 'true',\n });\n continue;\n }\n\n let distance1 = start - startIndex;\n let distance2 = end - start;\n\n if (startIndex < 0 && start > 0) distance1 -= 1;\n if (start < 0 && end > 0) distance2 -= 1;\n\n const startNode = distance1 + 1 + shift;\n const endNode = distance1 + distance2 + 1 + shift;\n const indices = [];\n for (let i = startNode; i <= endNode; i++) indices.push(i);\n\n polyline(indices,\n `stroke:${color};stroke-width:14;opacity:${alpha};fill:None;` +\n 'stroke-linejoin:round;stroke-linecap:round',\n { 'data-varri-subseq': 'true' }\n );\n }\n }\n\n /**\n * Remove all generated region highlights from the active registry.\n */\n function clearGeneratedRegionHighlights() {\n getRegionHighlights().filter(highlight => highlight.generated).forEach(highlight => {\n removeRegionHighlight(highlight.id);\n });\n }\n\n /**\n * Register a generated region highlight from sequence ranges.\n *\n * @param {Object} v\n * @param {{sequence1Range:[number, number], sequence2Range:[number, number], color?:string, alpha?:number}} spec\n * @returns {Object}\n */\n function registerGeneratedRegionHighlight(v, spec) {\n const sequenceContext = {\n '1': { offset: v.offset1, length: v.sequence1 ? v.sequence1.length : 0, sequence: v.sequence1 },\n '2': { offset: v.offset2, length: v.sequence2 ? v.sequence2.length : 0, sequence: v.sequence2 },\n };\n\n return registerRegionHighlight({\n sequence1Range: spec.sequence1Range,\n sequence2Range: spec.sequence2Range,\n color: spec.color || COLORS.backgroundHighlight,\n alpha: spec.alpha,\n generated: true,\n }, sequenceContext);\n }\n\n /**\n * Derive a true sequence-position range (matching offset/skip-zero\n * numbering) for a given sequence from a list of combined node/structure\n * positions (as produced by {@link listIntermolPairs} or\n * {@link getIntermolBasepairRegion}).\n *\n * @param {Object} v\n * @param {number[]} positions Combined node positions (1-based, gap-inclusive).\n * @param {'1'|'2'} sequence\n * @returns {[number, number]|null}\n */\n function getBackgroundRangeForPositions(v, positions, sequence) {\n const indexDict = getIndexDictionary(v);\n const values = positions\n .map(position => indexDict[position])\n .filter(entry => Array.isArray(entry) && entry[0] === `s${sequence}`)\n .map(([, seqPosition]) => seqPosition)\n .filter(Number.isFinite);\n\n if (values.length === 0) return null;\n return [Math.min(...values), Math.max(...values)];\n }\n\n /**\n * Compute the generated region-highlight ranges for the \"entire\n * intermolecular region\" background-highlighting mode, expressed as true\n * sequence positions (matching offset/skip-zero numbering).\n *\n * @param {Object} v Validated parameter dictionary.\n * @returns {{sequence1Range:[number,number], sequence2Range:[number,number]}|null}\n */\n function computeBackgroundRegionRanges(v) {\n const basepairRegion = getIntermolBasepairRegion(v.structure1, v.structure2);\n if (!basepairRegion || basepairRegion.length < 2) return null;\n\n const sequence1Range = getBackgroundRangeForPositions(v, basepairRegion[0], '1');\n const sequence2Range = getBackgroundRangeForPositions(v, basepairRegion[1], '2');\n if (!sequence1Range || !sequence2Range) return null;\n\n return { sequence1Range, sequence2Range };\n }\n\n /**\n * Build the node-ID path for a region highlight's filled polygon.\n *\n * @param {Object} v\n * @param {Object} highlight\n * @returns {number[]}\n */\n function getRegionHighlightNodePath(v, highlight) {\n const nodeIds = [];\n const seq1Range = Array.isArray(highlight.sequence1Range) ? highlight.sequence1Range : [];\n const seq2Range = Array.isArray(highlight.sequence2Range) ? highlight.sequence2Range : [];\n\n for (let position = seq1Range[0]; position <= seq1Range[1]; position++) {\n const nodeId = getNodeIdForSequencePosition(v, '1', position);\n if (nodeId) nodeIds.push(nodeId);\n }\n\n for (let position = seq2Range[0]; position <= seq2Range[1]; position++) {\n const nodeId = getNodeIdForSequencePosition(v, '2', position);\n if (nodeId) nodeIds.push(nodeId);\n }\n\n return nodeIds;\n }\n\n /**\n * Apply all region highlights from the active registry.\n *\n * @param {Object} v\n */\n function applyRegionHighlights(v) {\n const registryHighlights = getRegionHighlights();\n const highlights = registryHighlights.length > 0\n ? registryHighlights\n : (Array.isArray(v.regionHighlights) ? v.regionHighlights : []);\n\n highlights.forEach(highlight => {\n const nodePath = getRegionHighlightNodePath(v, highlight);\n if (nodePath.length >= 3) {\n polygon(\n nodePath,\n `fill:${highlight.color || COLORS.backgroundHighlight};opacity:${highlight.alpha};stroke:${highlight.color || COLORS.backgroundHighlight};stroke-width:7`,\n { 'data-varri-region': 'true' }\n );\n } else if (nodePath.length === 2) {\n polyline(\n nodePath,\n `stroke:${highlight.color || COLORS.backgroundHighlight};opacity:${highlight.alpha};stroke-width:7`,\n { 'data-varri-region': 'true' }\n );\n }\n });\n }\n\n /**\n * Apply all subsequence highlights from `v.subsequenceHighlights`.\n *\n * @param {Object} v\n */\n function applySubsequenceHighlights(v) {\n const highlights = Array.isArray(v.subsequenceHighlights) ? v.subsequenceHighlights : [];\n highlights.forEach(highlight => {\n highlightSubsequence(\n v,\n highlight.sequence,\n highlight.range,\n highlight.color || COLORS.subsequenceHighlight,\n highlight.alpha\n );\n });\n }\n\n /**\n * Apply point-mutation styling to nucleotide nodes.\n *\n * @param {Object} v\n */\n function applyPointMutations(v) {\n const mutations = Array.isArray(v.pointMutations) ? v.pointMutations : [];\n mutations.forEach(mutation => {\n if (!mutation.nodeId) return;\n addStyleToNodes([mutation.nodeId], `stroke: ${mutation.color}; stroke-width: 2px;`);\n });\n }\n\n /**\n * Visualise basepairs: apply the basepair colour to all basepair links,\n * and additionally mark G-U basepairs with a dashed line style.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function styleBasepairs(v) {\n // Apply basepair colour to all basepair links using inline style so it\n // overrides the Fornac CSS rule `line.fornac-link[link_type=\"basepair\"]\n // { stroke: red; }`, which takes precedence over SVG presentation\n // attributes.\n document.querySelectorAll('[link_type=\"basepair\"]').forEach(link => {\n link.style.stroke = COLORS.basepair;\n });\n\n if (v.distinctBpTypes) {\n // Build a 1-based sequence map including gap dots\n const seq1 = v.sequence1;\n const seq2 = v.sequence2;\n const gapDots = '.'.repeat(GAP);\n const combined = seq1 + gapDots + seq2;\n const seqDict = {};\n for (let i = 0; i < combined.length; i++) {\n seqDict[String(i + 1)] = combined[i];\n }\n\n document.querySelectorAll('[link_type=\"basepair\"], [link_type=\"pseudoknot\"]').forEach(link => {\n const l1 = seqDict[link.getAttribute('start')];\n const l2 = seqDict[link.getAttribute('end')];\n const bp = [l1, l2].sort().join('-').toLowerCase();\n if (bp === 'g-u') {\n link.style.strokeLinecap = 'butt';\n link.style.strokeDasharray = '1 1';\n } else if (bp === 'c-g' || bp === 'a-u') {\n link.style.strokeLinecap = 'butt';\n link.style.strokeDasharray = '';\n } else {\n link.style.strokeLinecap = 'round';\n link.style.strokeDasharray = '0 3';\n }\n });\n } else {\n document.querySelectorAll('[link_type=\"basepair\"], [link_type=\"pseudoknot\"]').forEach(link => {\n link.style.strokeLinecap = 'butt';\n link.style.strokeDasharray = '';\n });\n }\n }\n\n /**\n * Parse basepairs from a dot-bracket-like structure dictionary.\n *\n * @param {Object.<string, string>} struc Position \u2192 bracket character map.\n * @returns {Array<[number, number]>} Sorted basepair index pairs.\n */\n function listBasepairs(struc) {\n const basepairs = [];\n const openBasepairs = { '(': [], '<': [], '[': [], '{': [] };\n const brackets = [['(', ')'], ['[', ']'], ['{', '}'], ['<', '>']];\n\n for (const [indexStr, char] of Object.entries(struc)) {\n const index = parseInt(indexStr, 10);\n for (const [open, close] of brackets) {\n if (char === open) { openBasepairs[open].push(index); break; }\n if (char === close) {\n if (openBasepairs[open].length > 0) {\n basepairs.push([openBasepairs[open].pop(), index]);\n }\n break;\n }\n }\n }\n basepairs.sort((a, b) => a[0] - b[0]);\n return basepairs;\n }\n\n /**\n * Extract intermolecular basepair pairs from the combined structure.\n *\n * @param {Object} v Validated parameter dictionary.\n * @returns {Array<[number, number]>}\n */\n function listIntermolPairs(v) {\n const struc = v.structure_dict;\n const struc1 = v.structure1;\n const struc2 = v.structure2;\n const shift = struc1.length + GAP;\n\n const intermol = {};\n for (const i of Object.keys(struc)) intermol[i] = '.';\n\n for (const [index, bracket] of [\n ...listIntermolNodes(struc1),\n ...listIntermolNodes(struc2, shift),\n ]) {\n intermol[String(index)] = bracket;\n }\n\n return listBasepairs(intermol);\n }\n\n /**\n * Normalize base-pair endpoints and return a deterministic, duplicate-free\n * list ordered by the first and then the second nucleotide.\n *\n * @param {Array<[number, number]>} basepairs\n * @returns {Array<[number, number]>}\n */\n function normaliseBasepairList(basepairs) {\n const seen = new Set();\n const pairs = [];\n\n (Array.isArray(basepairs) ? basepairs : []).forEach(pair => {\n if (!Array.isArray(pair) || pair.length < 2) return;\n const first = Number(pair[0]);\n const second = Number(pair[1]);\n if (!Number.isInteger(first) || !Number.isInteger(second) || first === second) return;\n\n const normalized = first < second ? [first, second] : [second, first];\n const key = normalized[0] + ':' + normalized[1];\n if (seen.has(key)) return;\n seen.add(key);\n pairs.push(normalized);\n });\n\n return pairs.sort((left, right) =>\n left[0] - right[0] || left[1] - right[1]\n );\n }\n\n /**\n * Find the direct nested children of every base pair.\n *\n * For a fixed outer pair, candidates are visited by increasing opening\n * endpoint. A candidate is covered by an earlier candidate exactly when\n * that earlier candidate has the larger closing endpoint. Keeping the\n * largest earlier closing endpoint therefore computes the cover relation\n * in O(n^2), without joining an outer pair to a deeper pair through an\n * intervening base-pair column.\n *\n * @param {Array<[number, number]>} basepairs\n * @returns {Array<{outer:[number,number],children:Array<[number,number]>}>}\n */\n function listDirectNestedPairChildren(basepairs) {\n const pairs = normaliseBasepairList(basepairs);\n\n return pairs.map((outer, outerIndex) => {\n const children = [];\n let largestEarlierClose = -Infinity;\n\n for (let innerIndex = outerIndex + 1; innerIndex < pairs.length; innerIndex++) {\n const inner = pairs[innerIndex];\n if (inner[0] >= outer[1]) break;\n if (inner[1] >= outer[1]) continue;\n\n if (largestEarlierClose <= inner[1]) children.push(inner);\n largestEarlierClose = Math.max(largestEarlierClose, inner[1]);\n }\n\n return { outer, children };\n });\n }\n\n function pairEquals(left, right) {\n return left[0] === right[0] && left[1] === right[1];\n }\n\n function pairsCross(left, right) {\n return (\n left[0] < right[0] && right[0] < left[1] && left[1] < right[1]\n ) || (\n right[0] < left[0] && left[0] < right[1] && right[1] < left[1]\n );\n }\n\n function createLoopBoundary(outer, inner, extra = {}) {\n const firstGap = inner[0] - outer[0] - 1;\n const secondGap = outer[1] - inner[1] - 1;\n return {\n outer: outer.slice(),\n inner: inner.slice(),\n gaps: [firstGap, secondGap],\n loopType: firstGap > 0 && secondGap > 0 ? 'interior' : 'bulge',\n ...extra,\n };\n }\n\n /**\n * Identify intermolecular base-pair columns that directly bound RRI\n * bulges or interior loops.\n *\n * A result obeys the exact antiparallel cover relation from issue #59.\n * Fully stacked columns are excluded. A candidate touched by a crossing\n * RRI pair is also excluded because bulge/interior-loop decomposition is\n * not defined for that pseudoknotted region.\n *\n * @param {Object} v Validated parameter dictionary.\n * @returns {Array<{outer:[number,number],inner:[number,number],gaps:[number,number],loopType:string}>}\n */\n function listRriLoopBoundaryPairs(v) {\n const pairs = normaliseBasepairList(listIntermolPairs(v));\n const boundaries = [];\n\n listDirectNestedPairChildren(pairs).forEach(({ outer, children }) => {\n children.forEach(inner => {\n const firstGap = inner[0] - outer[0] - 1;\n const secondGap = outer[1] - inner[1] - 1;\n if (firstGap === 0 && secondGap === 0) return;\n\n const crossesBoundary = pairs.some(pair => {\n if (pairEquals(pair, outer) || pairEquals(pair, inner)) return false;\n if (pairsCross(pair, outer) || pairsCross(pair, inner)) return true;\n const firstInside = outer[0] < pair[0] && pair[0] < inner[0];\n const secondInside = inner[1] < pair[1] && pair[1] < outer[1];\n return firstInside !== secondInside;\n });\n if (!crossesBoundary) boundaries.push(createLoopBoundary(outer, inner));\n });\n });\n\n return boundaries;\n }\n\n /**\n * Group intramolecular base pairs by strand using Fornac node numbers.\n * Synthetic inter-molecule gap nodes are deliberately excluded.\n *\n * @param {Object} v Validated parameter dictionary.\n * @returns {{\"1\":Array<[number,number]>,\"2\":Array<[number,number]>}}\n */\n function listIntramolPairsBySequence(v) {\n const sequence1End = v.sequence1.length;\n const sequence2Start = sequence1End + GAP + 1;\n const sequence2End = sequence1End + GAP + v.sequence2.length;\n const grouped = { '1': [], '2': [] };\n\n listBasepairs(v.structure_dict).forEach(pair => {\n if (pair[0] >= 1 && pair[1] <= sequence1End) {\n grouped['1'].push(pair);\n } else if (pair[0] >= sequence2Start && pair[1] <= sequence2End) {\n grouped['2'].push(pair);\n }\n });\n\n return grouped;\n }\n\n /**\n * Identify intramolecular bulges/interior loops independently per strand.\n * A true bulge/interior loop has one direct child stem; hairpins (zero),\n * multiloops (multiple), ordinary stacks, and crossing pairs are excluded.\n *\n * @param {Object} v Validated parameter dictionary.\n * @returns {Array<{sequence:\"1\"|\"2\",outer:[number,number],inner:[number,number],gaps:[number,number],loopType:string}>}\n */\n function listStructureLoopBoundaryPairs(v) {\n const boundaries = [];\n const grouped = listIntramolPairsBySequence(v);\n\n for (const sequence of ['1', '2']) {\n const pairs = normaliseBasepairList(grouped[sequence]);\n listDirectNestedPairChildren(pairs).forEach(({ outer, children }) => {\n if (children.length !== 1) return;\n const inner = children[0];\n const firstGap = inner[0] - outer[0] - 1;\n const secondGap = outer[1] - inner[1] - 1;\n if (firstGap === 0 && secondGap === 0) return;\n\n const touchesCrossing = pairs.some(pair =>\n !pairEquals(pair, outer) && !pairEquals(pair, inner) &&\n (pairsCross(pair, outer) || pairsCross(pair, inner))\n );\n if (!touchesCrossing) {\n boundaries.push(createLoopBoundary(outer, inner, { sequence }));\n }\n });\n }\n\n return boundaries;\n }\n\n function loopBoundariesToConstraintSpecs(boundaries, kind) {\n const constraints = [];\n\n boundaries.forEach((boundary, index) => {\n const loopId = boundary.sequence\n ? kind + ':' + boundary.sequence + ':' + index\n : kind + ':' + index;\n const common = {\n kind,\n loopId,\n loopType: boundary.loopType,\n };\n\n constraints.push({\n ...common,\n source: boundary.outer[0],\n target: boundary.inner[0],\n sequence: boundary.sequence || '1',\n });\n constraints.push({\n ...common,\n source: boundary.inner[1],\n target: boundary.outer[1],\n sequence: boundary.sequence || '2',\n });\n });\n\n return constraints;\n }\n\n /**\n * Build the two same-strand spring specifications for every RRI loop.\n * Rest lengths are intentionally absent here: they are measured from the\n * live Fornac coordinates when the springs are installed.\n *\n * @param {Object} v Validated parameter dictionary.\n * @returns {Array<{source:number,target:number,sequence:\"1\"|\"2\",kind:string,loopId:string,loopType:string}>}\n */\n function getLinearRriConstraintSpecs(v) {\n return loopBoundariesToConstraintSpecs(listRriLoopBoundaryPairs(v), 'rri');\n }\n\n /**\n * Build the two same-strand spring specifications for every intramolecular\n * bulge/interior loop on either sequence.\n *\n * @param {Object} v Validated parameter dictionary.\n * @returns {Array<{source:number,target:number,sequence:\"1\"|\"2\",kind:string,loopId:string,loopType:string}>}\n */\n function getLinearStructureConstraintSpecs(v) {\n return loopBoundariesToConstraintSpecs(\n listStructureLoopBoundaryPairs(v),\n 'structure'\n );\n }\n\n /** Resolve a nucleotide node by its 1-based Fornac node number. */\n function getGraphNucleotideByNumber(graph, nodeNumber) {\n if (!graph || !Array.isArray(graph.nodes)) return null;\n return graph.nodes.find(node =>\n node && node.nodeType === 'nucleotide' && node.num === nodeNumber\n ) || null;\n }\n\n function getNodeDistance(first, second) {\n const coordinates = [first?.x, first?.y, second?.x, second?.y];\n if (!coordinates.every(value =>\n typeof value === 'number' && Number.isFinite(value)\n )) return null;\n const [firstX, firstY, secondX, secondY] = coordinates;\n return Math.hypot(secondX - firstX, secondY - firstY);\n }\n\n function pairKey(pair) {\n return pair[0] + ':' + pair[1];\n }\n\n /**\n * Return the one antiparallel RRI chain that can be represented by two\n * ordered rails. Crossing RRI pairs are deliberately left to the ordinary\n * force layout because a single two-rail ordering does not exist for them.\n */\n function listRriHelixPairGroups(v) {\n const pairs = normaliseBasepairList(listIntermolPairs(v));\n if (pairs.length < 2) return [];\n\n for (let first = 0; first < pairs.length; first++) {\n for (let second = first + 1; second < pairs.length; second++) {\n if (pairsCross(pairs[first], pairs[second])) return [];\n }\n }\n for (let index = 1; index < pairs.length; index++) {\n if (pairs[index - 1][1] <= pairs[index][1]) return [];\n }\n return [{ kind: 'rri', sequence: null, pairs }];\n }\n\n /**\n * Split intramolecular base pairs into maximal single-child stem paths.\n * Paths stop at multiloops and only paths containing a bulge/interior loop\n * need an additional linear constraint; uninterrupted stacks are already\n * linear in Fornac's native layout.\n */\n function listStructureHelixPairGroups(v) {\n const groups = [];\n const grouped = listIntramolPairsBySequence(v);\n\n for (const sequence of ['1', '2']) {\n const pairs = normaliseBasepairList(grouped[sequence]);\n const crossingPairKeys = new Set();\n pairs.forEach((pair, index) => {\n pairs.slice(index + 1).forEach(other => {\n if (!pairsCross(pair, other)) return;\n crossingPairKeys.add(pairKey(pair));\n crossingPairKeys.add(pairKey(other));\n });\n });\n\n const nextPair = new Map();\n const hasIncoming = new Set();\n listDirectNestedPairChildren(pairs).forEach(({ outer, children }) => {\n if (children.length !== 1) return;\n const inner = children[0];\n if (crossingPairKeys.has(pairKey(outer)) ||\n crossingPairKeys.has(pairKey(inner))) return;\n nextPair.set(pairKey(outer), inner);\n hasIncoming.add(pairKey(inner));\n });\n\n pairs.filter(pair =>\n !crossingPairKeys.has(pairKey(pair)) &&\n !hasIncoming.has(pairKey(pair))\n ).forEach(root => {\n const path = [];\n const visited = new Set();\n let pair = root;\n while (pair && !visited.has(pairKey(pair))) {\n path.push(pair);\n visited.add(pairKey(pair));\n pair = nextPair.get(pairKey(pair));\n }\n const containsLoop = path.slice(1).some((inner, index) => {\n const outer = path[index];\n return inner[0] - outer[0] > 1 || outer[1] - inner[1] > 1;\n });\n if (path.length >= 2 && containsLoop) {\n groups.push({ kind: 'structure', sequence, pairs: path });\n }\n });\n }\n return groups;\n }\n\n /**\n * Measure the two issue-59 loop spans without adding them to Fornac's\n * render graph. Keeping constraint metadata outside graph.links makes the\n * constraints unconditionally invisible, including after container.update().\n */\n function collectLinearHelixSpanConstraints(container, specs, linkType) {\n const graph = container && container.graph;\n if (!graph || !Array.isArray(graph.nodes)) return [];\n\n const rawMultiplier = Number(container.options?.linkDistanceMultiplier);\n const multiplier = Number.isFinite(rawMultiplier) && rawMultiplier > 0\n ? rawMultiplier\n : 15;\n const byLoop = new Map();\n specs.forEach(spec => {\n if (!byLoop.has(spec.loopId)) byLoop.set(spec.loopId, []);\n byLoop.get(spec.loopId).push(spec);\n });\n\n const constraints = [];\n byLoop.forEach(loopSpecs => {\n if (loopSpecs.length !== 2) return;\n const resolved = loopSpecs.map(spec => ({\n spec,\n source: getGraphNucleotideByNumber(graph, spec.source),\n target: getGraphNucleotideByNumber(graph, spec.target),\n }));\n if (resolved.some(link => !link.source || !link.target)) return;\n\n const distances = resolved.map(link => getNodeDistance(link.source, link.target));\n if (distances.some(distance => distance === null)) return;\n const loopSpan = Math.max(...distances);\n if (!Number.isFinite(loopSpan) || loopSpan <= 0) return;\n\n resolved.forEach(({ spec, source, target }) => {\n constraints.push({\n source,\n target,\n value: loopSpan / multiplier,\n linkType,\n extraLinkType: 'constraint',\n varriLinearHelix: true,\n varriLinearHelixKind: spec.kind,\n varriLinearHelixLoop: spec.loopId,\n varriTargetDistance: loopSpan,\n });\n });\n });\n return constraints;\n }\n\n /**\n * Build a straight two-rail template from the current live geometry.\n * Loop-to-loop increments use max(d1,d2), exactly as requested in issue\n * #59; uninterrupted stack increments use Fornac's backbone rest length.\n * The template itself is centered at the origin so it can subsequently be\n * fitted to the freely translating and rotating force-layout component.\n */\n function createLinearHelixRailTemplate(container, group) {\n const graph = container && container.graph;\n if (!graph || !Array.isArray(graph.nodes) || !group?.pairs?.length) return null;\n\n const rawMultiplier = Number(container.options?.linkDistanceMultiplier);\n const multiplier = Number.isFinite(rawMultiplier) && rawMultiplier > 0\n ? rawMultiplier\n : 15;\n const pairNodes = group.pairs.map(pair => ({\n pair,\n first: getGraphNucleotideByNumber(graph, pair[0]),\n second: getGraphNucleotideByNumber(graph, pair[1]),\n }));\n if (pairNodes.some(column =>\n !column.first || !column.second ||\n column.first.fixed || column.second.fixed ||\n getNodeDistance(column.first, column.second) === null\n )) return null;\n\n const offsets = [0];\n const intervals = [];\n for (let index = 1; index < pairNodes.length; index++) {\n const previous = pairNodes[index - 1];\n const current = pairNodes[index];\n const firstGap = current.pair[0] - previous.pair[0] - 1;\n const secondGap = previous.pair[1] - current.pair[1] - 1;\n const isLoop = firstGap > 0 || secondGap > 0;\n const measured = Math.max(\n getNodeDistance(previous.first, current.first),\n getNodeDistance(previous.second, current.second)\n );\n const span = isLoop && Number.isFinite(measured) && measured > 0\n ? measured\n : multiplier;\n intervals.push({ span, isLoop, gaps: [firstGap, secondGap] });\n offsets.push(offsets[offsets.length - 1] + span);\n }\n\n const meanOffset = offsets.reduce((sum, value) => sum + value, 0) /\n offsets.length;\n const points = [];\n pairNodes.forEach((column, index) => {\n const along = offsets[index] - meanOffset;\n points.push({ node: column.first, x: along, y: -multiplier / 2 });\n points.push({ node: column.second, x: along, y: multiplier / 2 });\n });\n\n const template = {\n kind: group.kind,\n sequence: group.sequence,\n pairs: group.pairs.map(pair => pair.slice()),\n points,\n intervals,\n railGap: multiplier,\n };\n const ordinaryFit = fitLinearHelixRailTemplate(template, 'x', 'y', 1);\n const reflectedFit = fitLinearHelixRailTemplate(template, 'x', 'y', -1);\n template.reflection = reflectedFit &&\n (!ordinaryFit || reflectedFit.error < ordinaryFit.error)\n ? -1\n : 1;\n return template;\n }\n\n /**\n * Fit a translated/rotated copy of one possibly reflected rail template\n * to a requested pair of live node-coordinate fields.\n */\n function fitLinearHelixRailTemplate(template, xField, yField, reflection) {\n if (!template || !Array.isArray(template.points) || template.points.length < 4) {\n return null;\n }\n const live = template.points.map(point => point.node);\n if (live.some(node =>\n !node || ![node[xField], node[yField]].every(Number.isFinite)\n )) {\n return null;\n }\n\n const center = live.reduce((sum, node) => ({\n x: sum.x + node[xField],\n y: sum.y + node[yField],\n }), { x: 0, y: 0 });\n center.x /= live.length;\n center.y /= live.length;\n\n let dot = 0;\n let cross = 0;\n template.points.forEach(point => {\n const templateY = reflection * point.y;\n const liveX = point.node[xField] - center.x;\n const liveY = point.node[yField] - center.y;\n dot += point.x * liveX + templateY * liveY;\n cross += point.x * liveY - templateY * liveX;\n });\n const angle = Math.atan2(cross, dot);\n const cosine = Math.cos(angle);\n const sine = Math.sin(angle);\n let error = 0;\n const targets = template.points.map(point => {\n const templateY = reflection * point.y;\n const target = {\n x: center.x + cosine * point.x - sine * templateY,\n y: center.y + sine * point.x + cosine * templateY,\n };\n const deltaX = point.node[xField] - target.x;\n const deltaY = point.node[yField] - target.y;\n error += deltaX * deltaX + deltaY * deltaY;\n return target;\n });\n return { angle, center, error, targets };\n }\n\n /**\n * Project one live helix onto the closest translated/rotated copy of its\n * straight template (2-D orthogonal Procrustes fit). Current and previous\n * coordinate clouds are projected separately, preserving the rigid body's\n * translational and rotational velocity instead of zeroing it each tick.\n */\n function projectLinearHelixRailTemplate(template) {\n if (!template || !Array.isArray(template.points)) return false;\n const live = template.points.map(point => point.node);\n if (live.some(node => !node || node.fixed)) return false;\n\n const reflection = template.reflection === -1 ? -1 : 1;\n const currentFit = fitLinearHelixRailTemplate(template, 'x', 'y', reflection);\n if (!currentFit) return false;\n const previousFit = fitLinearHelixRailTemplate(template, 'px', 'py', reflection) ||\n currentFit;\n\n template.points.forEach((point, index) => {\n point.node.x = currentFit.targets[index].x;\n point.node.y = currentFit.targets[index].y;\n point.node.px = previousFit.targets[index].x;\n point.node.py = previousFit.targets[index].y;\n point.node.varriLinearHelix = true;\n point.node.varriLinearHelixKind = template.kind;\n });\n template.angle = currentFit.angle;\n template.center = currentFit.center;\n template.previousAngle = previousFit.angle;\n template.previousCenter = previousFit.center;\n return true;\n }\n\n function normaliseRotationRadians(radians) {\n return Math.atan2(Math.sin(radians), Math.cos(radians));\n }\n\n function rotateCoordinateCloud(nodes, xField, yField, center, radians) {\n if (!center || ![center.x, center.y, radians].every(Number.isFinite)) return false;\n const cosine = Math.cos(radians);\n const sine = Math.sin(radians);\n nodes.forEach(node => {\n const offsetX = node[xField] - center.x;\n const offsetY = node[yField] - center.y;\n node[xField] = center.x + cosine * offsetX - sine * offsetY;\n node[yField] = center.y + sine * offsetX + cosine * offsetY;\n });\n return true;\n }\n\n function rotateTemplateFitState(template, centerField, angleField, pivot, radians) {\n const center = template?.[centerField];\n const angle = template?.[angleField];\n if (!center || ![center.x, center.y, angle].every(Number.isFinite)) return;\n const cosine = Math.cos(radians);\n const sine = Math.sin(radians);\n const offsetX = center.x - pivot.x;\n const offsetY = center.y - pivot.y;\n template[centerField] = {\n x: pivot.x + cosine * offsetX - sine * offsetY,\n y: pivot.y + sine * offsetX + cosine * offsetY,\n };\n template[angleField] = normaliseRotationRadians(angle + radians);\n }\n\n /**\n * Remove the global angular degree of freedom from an RRI layout by\n * rotating the complete graph until the paired-column centreline is\n * horizontal. Current and previous coordinate clouds are rotated\n * independently so translation is preserved without angular drift.\n */\n function orientLinearRriInteractionHorizontally(graph, rriTemplate, templates) {\n if (!graph || !Array.isArray(graph.nodes) || !rriTemplate) return false;\n const nodes = graph.nodes.filter(Boolean);\n if (nodes.length === 0 || nodes.some(node =>\n node.fixed || ![node.x, node.y, node.px, node.py].every(Number.isFinite)\n )) return false;\n if (![rriTemplate.angle, rriTemplate.previousAngle].every(Number.isFinite) ||\n !rriTemplate.center || !rriTemplate.previousCenter) return false;\n\n if (rriTemplate.horizontalDirection !== 1 &&\n rriTemplate.horizontalDirection !== -1) {\n rriTemplate.horizontalDirection = Math.cos(rriTemplate.angle) >= 0 ? 1 : -1;\n }\n const targetAngle = rriTemplate.horizontalDirection === 1 ? 0 : Math.PI;\n const currentRotation = normaliseRotationRadians(\n targetAngle - rriTemplate.angle\n );\n const previousRotation = normaliseRotationRadians(\n targetAngle - rriTemplate.previousAngle\n );\n const currentPivot = { ...rriTemplate.center };\n const previousPivot = { ...rriTemplate.previousCenter };\n\n rotateCoordinateCloud(nodes, 'x', 'y', currentPivot, currentRotation);\n rotateCoordinateCloud(nodes, 'px', 'py', previousPivot, previousRotation);\n templates.forEach(template => {\n rotateTemplateFitState(\n template,\n 'center',\n 'angle',\n currentPivot,\n currentRotation\n );\n rotateTemplateFitState(\n template,\n 'previousCenter',\n 'previousAngle',\n previousPivot,\n previousRotation\n );\n });\n rriTemplate.lastHorizontalRotation = currentRotation;\n return true;\n }\n\n function listVisibleIndexLabelPositions(v) {\n const positions = new Set(\n Object.entries(getIndexLabelValues(v))\n .filter(([, value]) => value !== 0)\n .map(([position]) => Number(position))\n );\n (Array.isArray(v.pointMutations) ? v.pointMutations : []).forEach(mutation => {\n const nodeId = Number(mutation.nodeId);\n if (Number.isInteger(nodeId)) positions.add(nodeId);\n });\n return positions;\n }\n\n function resolveGraphLinkNode(graph, endpoint) {\n if (endpoint && typeof endpoint === 'object') return endpoint;\n const index = Number(endpoint);\n return Number.isInteger(index) ? graph.nodes[index] || null : null;\n }\n\n /**\n * Match retained number labels to paired rail nodes and their mates.\n * Fornac's label link fixes distance but not which side of the rail wins,\n * so these records provide a transient, direction-only settling hint.\n */\n function collectLinearHelixIndexLabelBiases(container, v, templates) {\n const graph = container && container.graph;\n if (!graph || !Array.isArray(graph.nodes) || !Array.isArray(graph.links)) return [];\n\n const visiblePositions = listVisibleIndexLabelPositions(v);\n const partnerByNode = new Map();\n templates.forEach(template => {\n for (let index = 0; index + 1 < template.points.length; index += 2) {\n const first = template.points[index].node;\n const second = template.points[index + 1].node;\n partnerByNode.set(first, second);\n partnerByNode.set(second, first);\n }\n });\n\n const rawMultiplier = Number(container.options?.linkDistanceMultiplier);\n const multiplier = Number.isFinite(rawMultiplier) && rawMultiplier > 0\n ? rawMultiplier\n : 15;\n const seenLabels = new Set();\n const biases = [];\n\n graph.links.forEach(link => {\n if (link?.linkType !== 'label_link') return;\n const source = resolveGraphLinkNode(graph, link.source);\n const target = resolveGraphLinkNode(graph, link.target);\n const label = source?.nodeType === 'label'\n ? source\n : target?.nodeType === 'label' ? target : null;\n const anchor = source?.nodeType === 'nucleotide'\n ? source\n : target?.nodeType === 'nucleotide' ? target : null;\n const partner = partnerByNode.get(anchor);\n if (!label || !anchor || !partner || seenLabels.has(label) ||\n !visiblePositions.has(Number(anchor.num))) return;\n\n const rawValue = Number(link.value);\n const linkDistance = multiplier * (\n Number.isFinite(rawValue) && rawValue > 0 ? rawValue : 1\n );\n seenLabels.add(label);\n biases.push({ label, anchor, partner, linkDistance });\n });\n return biases;\n }\n\n /**\n * Gently move wrong-side number labels across the rail centreline.\n * Once a label reaches its exterior half-plane this becomes a no-op and\n * Fornac's native label link completes the ordinary spacing.\n */\n function nudgeLinearHelixIndexLabels(biases) {\n let moved = 0;\n biases.forEach(bias => {\n const { label, anchor, partner, linkDistance } = bias;\n if (!label || !anchor || !partner ||\n label.fixed || anchor.fixed || partner.fixed) return;\n if (![label.x, label.y, label.px, label.py,\n anchor.x, anchor.y, partner.x, partner.y,\n linkDistance].every(Number.isFinite)) return;\n\n const outwardX = anchor.x - partner.x;\n const outwardY = anchor.y - partner.y;\n const outwardLength = Math.hypot(outwardX, outwardY);\n if (!(outwardLength > 0)) return;\n const unitX = outwardX / outwardLength;\n const unitY = outwardY / outwardLength;\n const side = (label.x - anchor.x) * unitX +\n (label.y - anchor.y) * unitY;\n const target = LINEAR_HELIX_LABEL_BIAS_TARGET * linkDistance;\n if (!(side < target)) return;\n\n // Use the same bounded correction on every lifecycle event. The\n // end handler must never turn this settling hint into a late snap.\n const distance = Math.min(\n (target - side) * LINEAR_HELIX_LABEL_BIAS_GAIN,\n LINEAR_HELIX_LABEL_BIAS_MAX_STEP * linkDistance\n );\n const deltaX = distance * unitX;\n const deltaY = distance * unitY;\n label.x += deltaX;\n label.y += deltaY;\n label.px += deltaX;\n label.py += deltaY;\n moved += 1;\n });\n return moved;\n }\n\n /** Cache projected nodes, gently biased labels, and their visible links. */\n function createLinearHelixDomCache(\n graph,\n templates,\n labelBiases = [],\n syncWholeGraph = false\n ) {\n if (typeof document === 'undefined') return { nodes: [], links: [] };\n const projectedNodes = syncWholeGraph\n ? new Set(graph.nodes)\n : new Set([\n ...templates.flatMap(template => template.points.map(point => point.node)),\n ...labelBiases.map(bias => bias.label),\n ]);\n const graphLinks = Array.isArray(graph.links) ? graph.links : [];\n const incidentLinks = syncWholeGraph\n ? new Set(graphLinks)\n : new Set(graphLinks.filter(link =>\n projectedNodes.has(link?.source) || projectedNodes.has(link?.target)\n ));\n return {\n nodes: Array.from(document.querySelectorAll('g.gnode'))\n .filter(element => projectedNodes.has(element.__data__)),\n links: Array.from(document.querySelectorAll('line.link'))\n .filter(element => incidentLinks.has(element.__data__)),\n };\n }\n\n function syncFornacDirectionArrow(element, node) {\n const arrow = element.querySelector?.('path.fornac-directionArrow');\n const previous = node?.prevNode;\n if (!arrow || !previous || !node.linked ||\n ![node.x, node.y, previous.x, previous.y, node.radius].every(Number.isFinite)) {\n return;\n }\n let directionX = previous.x - node.x;\n let directionY = previous.y - node.y;\n const length = Math.hypot(directionX, directionY);\n if (!(length > 0)) return;\n directionX /= length;\n directionY /= length;\n const normalX = -directionY;\n const normalY = directionX;\n const tipX = (node.radius + 0.4) * directionX;\n const tipY = (node.radius + 0.4) * directionY;\n const size = 6;\n const width = 0.7;\n arrow.setAttribute('d',\n `M${tipX + size * (directionX / 2 + normalX * width / 2)},` +\n `${tipY + size * (directionY / 2 + normalY * width / 2)}` +\n `L${tipX},${tipY}` +\n `L${tipX + size * (directionX / 2 - normalX * width / 2)},` +\n `${tipY + size * (directionY / 2 - normalY * width / 2)}`\n );\n }\n\n /** Keep Fornac's already-created SVG in sync with post-tick projection. */\n function syncLinearHelixDom(cache) {\n cache.nodes.forEach(element => {\n const node = element.__data__;\n if (!node || ![node.x, node.y].every(Number.isFinite)) return;\n element.setAttribute('transform', `translate(${node.x},${node.y})`);\n syncFornacDirectionArrow(element, node);\n });\n cache.links.forEach(element => {\n const link = element.__data__;\n if (!link?.source || !link?.target) return;\n element.setAttribute('x1', String(link.source.x));\n element.setAttribute('y1', String(link.source.y));\n element.setAttribute('x2', String(link.target.x));\n element.setAttribute('y2', String(link.target.y));\n });\n }\n\n function clearLinearHelixConstraintState(container) {\n const hadConstraintState = !!container && (\n Object.prototype.hasOwnProperty.call(container, 'varriLinearHelixConstraints') ||\n Object.prototype.hasOwnProperty.call(container, 'varriLinearHelixTemplates') ||\n Object.prototype.hasOwnProperty.call(container, 'varriLinearHelixLabelBiases')\n );\n if (hadConstraintState && container.force && typeof container.force.on === 'function') {\n container.force.on('tick.varriLinearHelix', null);\n container.force.on('end.varriLinearHelix', null);\n }\n delete container?.varriLinearHelixConstraints;\n delete container?.varriLinearHelixTemplates;\n delete container?.varriLinearHelixLabelBiases;\n }\n\n /**\n * Apply rigid, invisible two-rail constraints for the requested RRI and/or\n * intramolecular helices, then restart the live D3 force once.\n *\n * @param {Object} container Live Fornac container.\n * @param {Object} v Validated parameter dictionary.\n * @param {{rri?:boolean,structure?:boolean}} [options]\n * @returns {number} Number of measured same-strand loop-span constraints.\n */\n function applyLinearHelixSprings(container, v, options = {}) {\n clearLinearHelixConstraintState(container);\n const graph = container && container.graph;\n if (!graph || !Array.isArray(graph.nodes) || !Array.isArray(graph.links)) return 0;\n\n const constraints = [];\n const groups = [];\n if (options.rri && v.molecules === '2') {\n constraints.push(...collectLinearHelixSpanConstraints(\n container,\n getLinearRriConstraintSpecs(v),\n LINEAR_RRI_LINK_TYPE\n ));\n groups.push(...listRriHelixPairGroups(v));\n }\n if (options.structure) {\n constraints.push(...collectLinearHelixSpanConstraints(\n container,\n getLinearStructureConstraintSpecs(v),\n LINEAR_STRUCTURE_LINK_TYPE\n ));\n groups.push(...listStructureHelixPairGroups(v));\n }\n\n const templates = groups\n .map(group => createLinearHelixRailTemplate(container, group))\n .filter(Boolean);\n if (templates.length === 0) return 0;\n\n const constrainedNodes = new Set(\n templates.flatMap(template => template.points.map(point => point.node))\n );\n const activeConstraints = constraints.filter(constraint =>\n constrainedNodes.has(constraint.source) &&\n constrainedNodes.has(constraint.target)\n );\n container.varriLinearHelixConstraints = activeConstraints;\n container.varriLinearHelixTemplates = templates;\n const labelBiases = collectLinearHelixIndexLabelBiases(container, v, templates);\n container.varriLinearHelixLabelBiases = labelBiases;\n const rriTemplate = options.rri\n ? templates.find(template => template.kind === 'rri') || null\n : null;\n const domCache = createLinearHelixDomCache(\n graph,\n templates,\n labelBiases,\n !!rriTemplate\n );\n const enforceAndSync = () => {\n templates.forEach(projectLinearHelixRailTemplate);\n if (rriTemplate) {\n orientLinearRriInteractionHorizontally(graph, rriTemplate, templates);\n }\n nudgeLinearHelixIndexLabels(labelBiases);\n syncLinearHelixDom(domCache);\n };\n let hasRefittedAtRest = false;\n const enforceSyncAndRefit = () => {\n enforceAndSync();\n if (!hasRefittedAtRest && typeof container.centerView === 'function') {\n hasRefittedAtRest = true;\n container.centerView();\n }\n };\n enforceAndSync();\n\n if (container.force) {\n if (typeof container.force.on === 'function') {\n container.force.on('tick.varriLinearHelix', () => enforceAndSync());\n // The final projection can extend beyond the bounds measured by\n // applyModifications while the force is still moving. Refit once\n // at rest so asymmetric bulges are not clipped at the viewport.\n container.force.on('end.varriLinearHelix', enforceSyncAndRefit);\n }\n if (typeof container.force.start === 'function') {\n container.force.start();\n }\n }\n return activeConstraints.length;\n }\n\n /**\n * Add background highlighting for intermolecular basepair stacks.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function backgroundhighlightBasepairs(v) {\n const intermolPairs = listIntermolPairs(v);\n if (intermolPairs.length === 0) {\n clearGeneratedRegionHighlights();\n return;\n }\n\n let stack = [intermolPairs.shift()];\n const highlightAreas = [];\n\n for (const [open, close] of intermolPairs) {\n const [stackOpen, stackClose] = stack[stack.length - 1];\n if (open - 1 === stackOpen && close + 1 === stackClose) {\n stack.push([open, close]);\n continue;\n }\n const area = stack.flatMap(([a, b]) => [a, b]).sort((a, b) => a - b);\n highlightAreas.push(area);\n stack = [[open, close]];\n }\n const area = stack.flatMap(([a, b]) => [a, b]).sort((a, b) => a - b);\n highlightAreas.push(area);\n\n clearGeneratedRegionHighlights();\n highlightAreas.forEach(region => {\n const seq1Range = getBackgroundRangeForPositions(v, region, '1');\n const seq2Range = getBackgroundRangeForPositions(v, region, '2');\n if (seq1Range && seq2Range) {\n registerGeneratedRegionHighlight(v, {\n sequence1Range: seq1Range,\n sequence2Range: seq2Range,\n color: COLORS.backgroundHighlight,\n });\n }\n });\n }\n\n /**\n * Add background highlighting for the entire intermolecular region.\n *\n * @param {Object} v Validated parameter dictionary.\n */\n function backgroundhighlightRegion(v) {\n const ranges = computeBackgroundRegionRanges(v);\n if (!ranges) {\n return;\n }\n\n registerGeneratedRegionHighlight(v, {\n sequence1Range: ranges.sequence1Range,\n sequence2Range: ranges.sequence2Range,\n color: COLORS.backgroundHighlight,\n });\n }\n\n /**\n * Add an accessibility-overlay circle on top of an existing node.\n *\n * @param {number} id Node ID.\n * @param {string} style CSS style for the overlay.\n * @param {string} tooltip Extra tooltip text to append.\n */\n function addAccessibilityOverlay(id, style, tooltip) {\n document.querySelectorAll(`circle[node_num=\"${id}\"]`).forEach(node => {\n const overlay = node.cloneNode(true);\n overlay.setAttribute('node_num', `o${id}`);\n overlay.setAttribute('style', style);\n if (overlay.firstChild) {\n overlay.firstChild.innerHTML += tooltip;\n }\n node.after(overlay);\n });\n }\n\n /**\n * Map a probability value to an opacity (higher probability \u2192 lower opacity).\n *\n * @param {number} prb Value in [0, 1].\n * @returns {number}\n */\n function mapProbabilityToOpacity(prb, representsOne) {\n return representsOne ? prb : (1 - prb);\n }\n\n /**\n * Visualise nucleotide accessibility data as overlaid coloured circles.\n *\n * @param {Object.<number, number>} accessData Map of node ID \u2192 accessibility probability.\n * @param {number} lenSeq Length of sequence 1 (used to distinguish colour by molecule).\n * @param {{sequence1?: string, sequence2?: string}|null} accessColors Optional colors for sequence 1/2 overlays.\n * @param {{sequence1RepresentsOne?: boolean, sequence2RepresentsOne?: boolean}|null} accessColorMode\n * Optional per-sequence mapping flags. If true, probability 1 maps to full color.\n */\n function visualiseAccessibility(accessData, lenSeq, accessColors = null, accessColorMode = null) {\n const seq1Color = accessColors?.sequence1 || COLORS.seq1profileColor;\n const seq2Color = accessColors?.sequence2 || COLORS.seq2profileColor;\n const seq1RepresentsOne = !!accessColorMode?.sequence1RepresentsOne;\n const seq2RepresentsOne = !!accessColorMode?.sequence2RepresentsOne;\n for (const [indexStr, prb] of Object.entries(accessData)) {\n const index = parseInt(indexStr, 10);\n const isSeq1 = index <= lenSeq;\n const color = isSeq1 ? seq1Color : seq2Color;\n const representsOne = isSeq1 ? seq1RepresentsOne : seq2RepresentsOne;\n const style = `fill: ${color};opacity: ${mapProbabilityToOpacity(prb, representsOne)}; stroke-width: 0;`;\n const prbTooltip = '\\n' + prb.toExponential(2);\n addAccessibilityOverlay(index, style, prbTooltip);\n }\n }\n\n /**\n * Resolve a force-graph link endpoint to a node object when possible.\n *\n * @param {Object} graph\n * @param {Object|number|string|null|undefined} endpoint\n * @returns {Object|null}\n */\n function resolveGraphNodeFromEndpoint(graph, endpoint) {\n if (endpoint && typeof endpoint === 'object') return endpoint;\n\n const idx = parseInt(String(endpoint), 10);\n if (!Number.isFinite(idx)) return null;\n\n if (Array.isArray(graph?.nodes) && graph.nodes[idx]) return graph.nodes[idx];\n if (Array.isArray(graph?.nodes)) {\n const byNumber = graph.nodes.find(node => node && node.num === idx);\n if (byNumber) return byNumber;\n }\n\n return null;\n }\n\n /**\n * Identify the force-graph nodes that implement Fornac's \"free-form\"\n * loop circularisation: the two synthetic closure nodes plus every hub\n * whose loop is exterior-flavoured, along with the full member set of\n * each such hub.\n *\n * Fornac's `reinforceLoops()` gives every loop of the structure (stems\n * excluded) its own fake \"middle\" hub node via `addFakeNode()`, which\n * pulls that loop's member nucleotides toward one shared point (keeping\n * the loop visually rounded). For the true top-level external loop\n * specifically \u2014 and only when Fornac's `circularizeExternal` option is\n * enabled, which is the default \u2014 two extra synthetic \"closure\" middle\n * nodes (`num: -2` and `num: -3`), positioned at the RNA's very first\n * and very last nucleotide, are additionally appended to that loop's\n * member list before its hub is created. This is exactly the\n * constraint that pulls the two sequence ends together.\n *\n * That hub cannot be found reliably via link adjacency: `addFakeNode()`\n * skips creating any link (hub spoke *and* the two \"chord\" links to\n * nearby members \u2014 see below) for member-list entries whose index\n * exceeds the sequence length, which is exactly what the closure nodes'\n * synthetic indices are. So the closure nodes are never linked to the\n * hub directly, only incidentally chord-linked to a couple of nearby\n * real nucleotide members. Instead, each hub's own `nucs` array \u2014 a\n * snapshot of the 1-based `graph.nodes` array indices of every member\n * of that loop, recorded when the hub was created \u2014 is used: for the\n * true external loop only, it includes the closure nodes' own array\n * indices, which identifies that hub precisely.\n *\n * vaRRI additionally inserts extra unpaired \"gap\" characters between two\n * molecules to work around a Fornac rendering bug. Fornac's own\n * `breakNodesToFakeNodes()` marks every member of *any* loop that\n * touches that gap as `elemType: \"e\"` (the same label used for the true\n * exterior loop), regardless of that loop's real type \u2014 this is exactly\n * the \"trailing ends around the & spacer\" that should also be freed.\n * Any hub whose resolved members include an `elemType: \"e\"` nucleotide\n * is therefore treated the same way as the true external-loop hub.\n *\n * Each qualifying hub's `nucs` array also lists every other member of\n * its loop (real nucleotides, and closure nodes for the true external\n * hub). Those member sets are returned too, because `addFakeNode()`\n * additionally links members directly to each other with two kinds of\n * \"chord\" links (skipping the hub entirely) to keep the loop's ring\n * shape from collapsing \u2014 e.g. a member is linked straight to the\n * member roughly opposite it in the loop. Those direct member-to-member\n * links must also be removed to fully free the loop's nucleotides;\n * removing only the hub and closure nodes leaves them in place, which\n * still visibly pulls opposite sides of the loop together. Loops that\n * don't qualify (i.e. every other stem/hairpin/interior/multi loop) are\n * left completely untouched.\n *\n * @param {Object} graph\n * @returns {{closureUids: Set<string>, hubUids: Set<string>, memberUids: Set<string>}|null}\n */\n function getFreeableLoopScaffoldUids(graph) {\n if (!graph || !Array.isArray(graph.nodes)) return null;\n\n const closureNodes = graph.nodes.filter(node =>\n node && node.nodeType === 'middle' && (node.num === -2 || node.num === -3)\n );\n const closureUids = new Set(closureNodes.map(node => node.uid).filter(Boolean));\n const closureIndices = new Set(closureNodes.map(node => graph.nodes.indexOf(node) + 1));\n\n const hubs = graph.nodes.filter(node =>\n node && node.nodeType === 'middle' && node.num === -1 && Array.isArray(node.nucs)\n );\n\n const hubUids = new Set();\n const memberUids = new Set(closureUids);\n\n hubs.forEach(hub => {\n const members = hub.nucs.map(idx => graph.nodes[idx - 1]).filter(Boolean);\n const touchesClosure = hub.nucs.some(idx => closureIndices.has(idx));\n const touchesExternalElemType = members.some(member => member.elemType === 'e');\n\n if (!touchesClosure && !touchesExternalElemType) return;\n\n hubUids.add(hub.uid);\n members.forEach(member => {\n if (member.uid) memberUids.add(member.uid);\n });\n });\n\n if (closureUids.size === 0 && hubUids.size === 0) return null;\n\n return { closureUids, hubUids, memberUids };\n }\n\n /**\n * Remove Fornac's exterior-flavoured loop circularisation scaffolds \u2014\n * the closure nodes and every hub whose loop is exterior-flavoured\n * (the true top-level external loop, plus any loop touching vaRRI's\n * inter-molecule gap) \u2014 from the force graph and rerun the layout.\n * Every other loop's own hub and circular constraint is left untouched.\n *\n * @param {Object} container\n * @param {Object} v\n * @returns {boolean}\n */\n function relaxForceGraphScaffold(container, v) {\n const graph = container && container.graph;\n const scaffold = getFreeableLoopScaffoldUids(graph);\n if (!scaffold) return false;\n\n const removableNodeUids = new Set(scaffold.closureUids);\n scaffold.hubUids.forEach(uid => removableNodeUids.add(uid));\n\n graph.links = graph.links.filter(link => {\n const linkType = String(link && link.linkType);\n if (linkType !== 'fake' && linkType !== 'fake_fake') return true;\n\n const sourceNode = resolveGraphNodeFromEndpoint(graph, link.source);\n const targetNode = resolveGraphNodeFromEndpoint(graph, link.target);\n const sourceUid = sourceNode && sourceNode.uid;\n const targetUid = targetNode && targetNode.uid;\n\n // Drop anything touching a freed hub or the closure nodes themselves.\n if ((sourceUid && removableNodeUids.has(sourceUid)) || (targetUid && removableNodeUids.has(targetUid))) {\n return false;\n }\n\n // Drop direct member-to-member \"chord\" links that bypass the hub\n // entirely but still connect two nucleotides of the external loop.\n if (sourceUid && targetUid && scaffold.memberUids.has(sourceUid) && scaffold.memberUids.has(targetUid)) {\n return false;\n }\n\n return true;\n });\n\n graph.nodes = graph.nodes.filter(node => !(node && node.uid && removableNodeUids.has(node.uid)));\n\n if (typeof container.update === 'function') {\n container.update();\n }\n\n if (container.force && typeof container.force.resume === 'function') {\n container.force.resume();\n } else if (container.force && typeof container.force.start === 'function') {\n container.force.start();\n }\n\n return true;\n }\n\n /**\n * Override Fornac's \"pseudoknot\" link force strength on a live container.\n *\n * Fornac's `FornaContainer` sets `container.linkStrengths.pseudoknot = 0`\n * by default, meaning pseudoknot basepair links exert no pull in the\n * force simulation. `container.linkStrengths` is read by the link-force\n * accessor function on every `force.start()` call (which rebuilds the\n * internal per-link strength array), but *not* by `force.resume()`\n * (which only restarts ticking without rebuilding that array). So the\n * new strength must be set before calling `force.start()` specifically.\n *\n * @param {Object} container\n * @param {boolean} enabled When true, sets pseudoknot strength to 10.\n */\n function applyPseudoknotLinkStrength(container, enabled) {\n if (!container || !container.linkStrengths) return;\n\n container.linkStrengths.pseudoknot = enabled ? 10 : 0;\n\n if (container.force && typeof container.force.start === 'function') {\n container.force.start();\n }\n }\n \n\n // -----------------------------------------------------------------------\n // Main render function\n // -----------------------------------------------------------------------\n\n /**\n * Stop the active Fornac force and cancel delayed/animation-frame work.\n * Pending render promises resolve as cancelled.\n */\n function cancelActiveRender() {\n if (_animFrameId !== null) {\n cancelAnimationFrame(_animFrameId);\n _animFrameId = null;\n }\n\n if (_renderTimeoutId !== null) {\n clearTimeout(_renderTimeoutId);\n _renderTimeoutId = null;\n if (_pendingRenderResolve) {\n const resolvePendingRender = _pendingRenderResolve;\n queueMicrotask(() => resolvePendingRender({ cancelled: true }));\n _pendingRenderResolve = null;\n }\n }\n\n // D3 v3 dispatches `end` synchronously from force.stop(). Remove the\n // helix lifecycle listeners first so a cancelled render cannot refit\n // a detached/cleared SVG container.\n if (_activeContainer) {\n clearLinearHelixConstraintState(_activeContainer);\n }\n if (_activeContainer?.force && typeof _activeContainer.force.stop === 'function') {\n _activeContainer.force.stop();\n }\n _activeContainer = null;\n }\n\n /**\n * Build the Fornac RNA visualisation inside `containerId` and apply all\n * vaRRI modifications.\n *\n * This is the main entry point. Call `validate()` first to produce `v`.\n *\n * @param {string} containerId CSS selector or element ID of the Fornac container.\n * @param {Object} v Validated parameter dictionary (from `validate()`).\n * @param {Object} [options]\n * @param {boolean} [options.forceLayout=false] Enable Fornac force-layout animation.\n * @param {boolean} [options.forceLayoutLinearRRI=false] Enforce a rigid two-rail RRI layout and orient the complete interaction horizontally.\n * @param {boolean} [options.forceLayoutLinearStructure=false] Enforce the same two-rail geometry within intramolecular helices.\n * @param {boolean} [options.freeTrailingEnds=false] Remove Fornac's external-loop circularisation constraint (the \"closure\" scaffold linking the sequence ends) from the force graph, leaving all other loop constraints intact.\n * @param {boolean} [options.pullPseudoknotBasepairs=false] Set Fornac's pseudoknot link force strength to 10 (default 0), pulling pseudoknot basepairs together in the force layout.\n * @param {Object.<number,number>|null} [options.accessData=null] Accessibility data map.\n * @param {{sequence1?: string, sequence2?: string}|null} [options.accessColors=null] Optional accessibility-overlay colors.\n * @param {{sequence1RepresentsOne?: boolean, sequence2RepresentsOne?: boolean}|null} [options.accessColorMode=null]\n * Optional per-sequence mapping flags; true means probability 1 maps to full color.\n */\n function render(containerId, v, options = {}) {\n cancelActiveRender();\n\n const {\n forceLayout = false,\n forceLayoutLinearRRI = false,\n forceLayoutLinearStructure = false,\n freeTrailingEnds = false,\n pullPseudoknotBasepairs = false,\n accessData = null,\n accessColors = null,\n accessColorMode = null,\n } = options;\n\n // Build molecules via Fornac\n const container = new fornac.FornaContainer(\n `#${containerId}`,\n { \n animation: forceLayout, \n labelInterval: 1\n }\n );\n _activeContainer = container;\n container.addRNA(v.structure, { structure: v.structure, sequence: v.sequence });\n\n if (forceLayout && freeTrailingEnds) {\n relaxForceGraphScaffold(container, v);\n }\n\n if (forceLayout && pullPseudoknotBasepairs) {\n applyPseudoknotLinkStrength(container, true);\n }\n\n if (forceLayout && (forceLayoutLinearRRI || forceLayoutLinearStructure)) {\n applyLinearHelixSprings(container, v, {\n rri: forceLayoutLinearRRI,\n structure: forceLayoutLinearStructure,\n });\n }\n\n function applyModifications() {\n // Set IDs for DOM querying\n setLinksId();\n setLabelsId();\n\n // Remove gap nodes\n removeDummyNodes(v.sequence);\n\n // Remove duplicate intermolecular links\n if (v.molecules === '2') {\n removeSecondLink();\n }\n\n // Strand coloring\n if (v.coloring === 'strand') {\n changeBackgroundColor(v);\n }\n\n // Tooltips and labels\n updateNodeToolTips(v);\n updateLinkTooltips(v);\n setIndexLabels(v);\n\n // Highlighting (only for 2-molecule input)\n clearGeneratedRegionHighlights();\n if (v.molecules === '2') {\n if (v.highlighting === 'region') highlightRegion(v);\n if (v.highlighting === 'basepairs') highlightBasepairs(v);\n if (v.backgroundhighlighting === 'region') backgroundhighlightRegion(v);\n if (v.backgroundhighlighting === 'basepairs') backgroundhighlightBasepairs(v);\n }\n\n // Basepair styling (colour + optional G-U dashing)\n styleBasepairs(v);\n\n // Region highlights\n applyRegionHighlights(v);\n\n // Subsequence highlights\n applySubsequenceHighlights(v);\n\n // Point mutations\n applyPointMutations(v);\n\n // Accessibility overlay\n if (accessData) {\n visualiseAccessibility(accessData, v.sequence1.length, accessColors, accessColorMode);\n }\n\n // Linear-helix constraints may extend the initial bounds. Refit\n // after the first force ticks and all hidden nodes are removed.\n if (forceLayout && (forceLayoutLinearRRI || forceLayoutLinearStructure) &&\n typeof container.centerView === 'function') {\n container.centerView();\n }\n\n // When animation is on, keep the background-highlight polygon in sync\n // with the force-layout by redrawing it on every animation frame.\n if (forceLayout) {\n function highlightSyncLoop() {\n document.querySelectorAll('[data-varri-region]').forEach(el => el.remove());\n document.querySelectorAll('[data-varri-subseq]').forEach(el => el.remove());\n\n applyRegionHighlights(v);\n applySubsequenceHighlights(v);\n\n _animFrameId = requestAnimationFrame(highlightSyncLoop);\n }\n _animFrameId = requestAnimationFrame(highlightSyncLoop);\n }\n }\n\n return new Promise((resolve, reject) => {\n _pendingRenderResolve = resolve;\n _renderTimeoutId = setTimeout(() => {\n _renderTimeoutId = null;\n _pendingRenderResolve = null;\n\n try {\n applyModifications();\n resolve({ cancelled: false });\n } catch (err) {\n reject(err);\n }\n }, 200);\n });\n }\n\n // -----------------------------------------------------------------------\n // Rotation helpers\n // -----------------------------------------------------------------------\n\n /**\n * Normalise a rotation angle to the range [-180, 180].\n *\n * @param {number} degrees\n * @returns {number}\n */\n function normaliseRotationDegrees(degrees) {\n if (!Number.isFinite(degrees)) {\n throw new Error('Rotation degrees must be a finite number');\n }\n let value = degrees % 360;\n if (value > 180) value -= 360;\n if (value < -180) value += 360;\n return value;\n }\n\n /**\n * Resolve the element that should host the rotation layer.\n *\n * If Fornac's plot group exists, rotate inside that group so that\n * pan/zoom transforms stay in screen-space and dragging keeps expected\n * directions after rotation.\n *\n * @param {SVGSVGElement} svgEl\n * @returns {SVGElement}\n */\n function getRotationHost(svgEl) {\n const fornacPlot = svgEl.querySelector('.fornac-plot');\n return fornacPlot || svgEl;\n }\n\n /**\n * Ensure a host element has a dedicated layer that can be rotated.\n *\n * @param {SVGElement} hostEl\n * @returns {SVGGElement}\n */\n function ensureRotationLayer(hostEl) {\n let layer = Array.from(hostEl.children).find(child =>\n child.tagName && child.tagName.toLowerCase() === 'g' &&\n child.getAttribute('data-varri-rotation-layer') === 'true'\n );\n\n if (!layer) {\n layer = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n layer.setAttribute('data-varri-rotation-layer', 'true');\n hostEl.appendChild(layer);\n }\n\n const nodesToMove = Array.from(hostEl.childNodes).filter(node => {\n if (node === layer) return false;\n if (hostEl.tagName && hostEl.tagName.toLowerCase() === 'svg' &&\n node.nodeType === Node.ELEMENT_NODE && node.tagName &&\n node.tagName.toLowerCase() === 'defs') {\n return false;\n }\n return true;\n });\n nodesToMove.forEach(node => layer.appendChild(node));\n\n return layer;\n }\n\n /**\n * Compute the centre of an SVG element's bounding box.\n *\n * @param {SVGGraphicsElement} el\n * @returns {{x:number, y:number}|null}\n */\n function getBBoxCenter(el) {\n try {\n const bbox = el.getBBox();\n if (!Number.isFinite(bbox.x) || !Number.isFinite(bbox.y) ||\n !Number.isFinite(bbox.width) || !Number.isFinite(bbox.height)) {\n return null;\n }\n return {\n x: bbox.x + (bbox.width / 2),\n y: bbox.y + (bbox.height / 2),\n };\n } catch (err) {\n return null;\n }\n }\n\n /**\n * Rotate the current visualisation around its bounding-box centre while\n * keeping text labels horizontally aligned.\n *\n * @param {string} containerId ID of the container element.\n * @param {number} degrees Rotation amount.\n * @param {Object} [options]\n * @param {'delta'|'absolute'} [options.mode='delta']\n * @returns {number} Applied absolute angle in degrees (normalised).\n */\n function rotateVisualization(containerId, degrees, options = {}) {\n const container = document.getElementById(containerId);\n const svgEl = container && container.querySelector('svg');\n if (!svgEl) throw new Error('No SVG found in container');\n\n const amount = Number(degrees);\n if (!Number.isFinite(amount)) {\n throw new Error('Rotation degrees must be a finite number');\n }\n\n const mode = options.mode === 'absolute' ? 'absolute' : 'delta';\n const current = Number(svgEl.getAttribute('data-varri-rotation') || 0);\n const target = normaliseRotationDegrees(mode === 'absolute' ? amount : current + amount);\n\n const hostEl = getRotationHost(svgEl);\n const layer = ensureRotationLayer(hostEl);\n const center = getBBoxCenter(layer);\n if (!center) return current;\n\n layer.setAttribute('transform', `rotate(${target} ${center.x} ${center.y})`);\n svgEl.setAttribute('data-varri-rotation', String(target));\n\n layer.querySelectorAll('text').forEach(textEl => {\n if (!textEl.hasAttribute('data-varri-base-transform')) {\n textEl.setAttribute('data-varri-base-transform', textEl.getAttribute('transform') || '');\n }\n const baseTransform = textEl.getAttribute('data-varri-base-transform') || '';\n if (target === 0) {\n if (baseTransform) {\n textEl.setAttribute('transform', baseTransform);\n } else {\n textEl.removeAttribute('transform');\n }\n return;\n }\n\n const textCenter = getBBoxCenter(textEl) || center;\n const transformParts = [];\n if (baseTransform) transformParts.push(baseTransform);\n transformParts.push(`rotate(${-target} ${textCenter.x} ${textCenter.y})`);\n textEl.setAttribute('transform', transformParts.join(' '));\n });\n\n return target;\n }\n\n // -----------------------------------------------------------------------\n // SVG / PNG export\n // -----------------------------------------------------------------------\n\n /**\n * SVG presentation properties to inline when exporting.\n *\n * These cover every visual property used by Fornac and vaRRI: fill/stroke\n * paint, font, text alignment, and element visibility. Using this fixed\n * list avoids dumping hundreds of irrelevant properties from\n * `getComputedStyle` (e.g. layout-only CSS that SVG viewers ignore).\n */\n const SVG_STYLE_PROPS = [\n 'fill', 'fill-opacity', 'fill-rule',\n 'stroke', 'stroke-width', 'stroke-opacity',\n 'stroke-dasharray', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit',\n 'font-family', 'font-size', 'font-weight', 'font-style',\n 'text-anchor', 'dominant-baseline', 'alignment-baseline',\n 'opacity', 'visibility', 'display',\n 'marker-start', 'marker-end', 'marker-mid',\n 'color',\n ];\n\n /**\n * Walk `originalEl` and `cloneEl` in parallel, reading computed styles\n * from `originalEl` (which has all browser CSS applied) and writing them\n * as an inline `style` attribute on `cloneEl`.\n *\n * This makes every element carry its own fully-resolved presentation\n * values so the exported SVG is self-contained \u2014 no external stylesheet\n * is required. In particular:\n * - class-based rules (`.fornac-node`, `.fornac-link`, etc.) are baked in\n * - relative units (`0.4em` font-size) are resolved to absolute pixels\n * - inline `style` overrides from vaRRI (strand colours, highlights) are\n * already included in the computed value, so nothing is lost\n *\n * @param {Element} originalEl Live DOM element (inside the visible SVG).\n * @param {Element} cloneEl Corresponding cloned element.\n */\n function inlineComputedStyles(originalEl, cloneEl) {\n if (!originalEl || originalEl.nodeType !== Node.ELEMENT_NODE) return;\n\n // Leave <style> and <defs> subtrees alone \u2014 they hold definitions, not\n // rendered shapes, and rewriting their style attributes would break them.\n const tag = (originalEl.tagName || '').toLowerCase();\n if (tag === 'style' || tag === 'defs') return;\n\n const computed = window.getComputedStyle(originalEl);\n let inlined = '';\n for (const prop of SVG_STYLE_PROPS) {\n const val = computed.getPropertyValue(prop);\n if (val) inlined += `${prop}:${val};`;\n }\n if (inlined) cloneEl.setAttribute('style', inlined);\n\n // Recurse into child elements in lock-step.\n const origKids = originalEl.children;\n const cloneKids = cloneEl.children;\n for (let i = 0; i < origKids.length; i++) {\n if (cloneKids[i]) inlineComputedStyles(origKids[i], cloneKids[i]);\n }\n }\n\n /**\n * Build a self-contained SVG string from the current Fornac visualisation.\n *\n * Strategy:\n * 1. Clone the live SVG element (preserves all D3 transforms and vaRRI\n * DOM modifications).\n * 2. Walk original + clone in parallel and inline every computed\n * presentation property so the file is fully self-contained.\n * 3. Set explicit pixel width/height on the root so viewers render at\n * the same size as the browser display.\n * 4. Prepend a white background rect to match the container's background.\n * 5. Serialise with XMLSerializer (namespace-aware).\n *\n * @param {string} containerId ID of the container element.\n * @returns {string} Full SVG markup.\n */\n function buildSVGString(containerId) {\n const container = document.getElementById(containerId);\n const svgEl = container && container.querySelector('svg');\n if (!svgEl) throw new Error('No SVG found in container');\n\n // Clone the live SVG so we can annotate it without touching the DOM.\n const clone = svgEl.cloneNode(true);\n\n // Inline all computed presentation styles before any other annotation\n // so that class-based CSS rules, relative units, and inherited values\n // are all baked into the clone as plain inline style attributes.\n inlineComputedStyles(svgEl, clone);\n\n // Required namespace declarations for a standalone SVG file.\n clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n clone.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');\n\n // Derive pixel dimensions from the rendered element so the exported\n // file renders at the same size as what the user sees in the browser.\n const w = svgEl.clientWidth || container.clientWidth || 800;\n const h = svgEl.clientHeight || container.clientHeight || 600;\n clone.setAttribute('width', w);\n clone.setAttribute('height', h);\n\n // Keep (or synthesise) the viewBox so the internal coordinate space\n // that Fornac uses maps 1:1 to the exported pixel dimensions.\n if (!clone.getAttribute('viewBox')) {\n clone.setAttribute('viewBox', `0 0 ${w} ${h}`);\n }\n\n // White background rect \u2014 matches the container's background: #fff\n // so the exported image looks identical to the on-screen visualisation.\n const bg = document.createElementNS('http://www.w3.org/2000/svg', 'rect');\n bg.setAttribute('width', '100%');\n bg.setAttribute('height', '100%');\n bg.setAttribute('fill', 'white');\n clone.insertBefore(bg, clone.firstChild);\n\n return new XMLSerializer().serializeToString(clone);\n }\n\n /**\n * Trigger a browser download of the current visualisation as an SVG file.\n *\n * @param {string} containerId ID of the container element.\n * @param {string} [filename=\"vaRRI_output.svg\"]\n */\n function downloadSVG(containerId, filename = 'vaRRI_output.svg') {\n const svgStr = buildSVGString(containerId);\n const blob = new Blob([svgStr], { type: 'image/svg+xml' });\n triggerDownload(URL.createObjectURL(blob), filename);\n }\n\n /**\n * Trigger a browser download of the current visualisation as a PNG image.\n *\n * Rasterises the SVG to a canvas at `scale` \u00D7 the rendered size and\n * converts it to a PNG data URL. A white background is painted on the\n * canvas before the image is drawn so the result matches the on-screen\n * appearance.\n *\n * @param {string} containerId ID of the container element.\n * @param {string} [filename=\"vaRRI_output.png\"]\n * @param {number} [scale=2] Resolution multiplier (2 = retina quality).\n */\n function downloadPNG(containerId, filename = 'vaRRI_output.png', scale = 2) {\n const svgStr = buildSVGString(containerId);\n const blob = new Blob([svgStr], { type: 'image/svg+xml' });\n const url = URL.createObjectURL(blob);\n\n // Determine the rendered pixel size from the live container so that\n // canvas dimensions are correct regardless of the SVG's naturalWidth.\n const container = document.getElementById(containerId);\n const svgEl = container && container.querySelector('svg');\n const w = (svgEl && svgEl.clientWidth) || (container && container.clientWidth) || 800;\n const h = (svgEl && svgEl.clientHeight) || (container && container.clientHeight) || 600;\n\n function rasterise(imgEl, canvasW, canvasH) {\n const canvas = document.createElement('canvas');\n canvas.width = canvasW;\n canvas.height = canvasH;\n const ctx = canvas.getContext('2d');\n // White background to match the container's CSS background colour.\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvasW, canvasH);\n ctx.drawImage(imgEl, 0, 0, canvasW, canvasH);\n return canvas.toDataURL('image/png');\n }\n\n const img = new Image();\n img.onload = () => {\n const dataUrl = rasterise(img, w * scale, h * scale);\n URL.revokeObjectURL(url);\n triggerDownload(dataUrl, filename);\n };\n img.onerror = () => {\n // Fallback: load the SVG via a data URI instead of a blob URL.\n URL.revokeObjectURL(url);\n const dataUri = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgStr);\n const imgFallback = new Image();\n imgFallback.onload = () => {\n triggerDownload(rasterise(imgFallback, w * scale, h * scale), filename);\n };\n imgFallback.src = dataUri;\n };\n img.src = url;\n }\n\n /**\n * Create a hidden `<a>` element and programmatically click it to download.\n *\n * @param {string} href URL or data URI.\n * @param {string} filename\n */\n function triggerDownload(href, filename) {\n const a = document.createElement('a');\n a.href = href;\n a.download = filename;\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n }\n\n // -----------------------------------------------------------------------\n // Public API\n // -----------------------------------------------------------------------\n\n const vaRRI = {\n // Core\n cancelActiveRender,\n normaliseRotationDegrees,\n render,\n rotateVisualization,\n validate,\n\n // Colors\n getColors,\n setColors,\n\n // Annotation registries\n clearPointMutations,\n clearRegionHighlights,\n clearSubsequenceHighlights,\n computeBackgroundRegionRanges,\n createPointMutation,\n createRegionHighlight,\n createSubsequenceHighlight,\n getPointMutations,\n getRegionHighlightNodePath,\n getRegionHighlights,\n getSubsequenceHighlights,\n registerGeneratedRegionHighlight,\n registerPointMutation,\n registerRegionHighlight,\n registerSubsequenceHighlight,\n removePointMutation,\n removeRegionHighlight,\n removeSubsequenceHighlight,\n updatePointMutation,\n updateRegionHighlight,\n updateSubsequenceHighlight,\n\n // Validation and formatting\n checkStructureInputSimple,\n findBasePairs,\n formatSequence,\n formatStructure,\n getIndexDictionary,\n getMolecules,\n getSequenceIndices,\n parseSubsequences,\n splitAtAmpersand,\n validateBackgroundhighlighting,\n validateCroppingInput,\n validateHighlighting,\n validateOffset,\n normaliseMutationPosition,\n validateSequenceInput,\n validateStructureInput,\n\n // Base-pair utilities\n getIntermolBasepairRegion,\n getLinearRriConstraintSpecs,\n getLinearStructureConstraintSpecs,\n listBasepairs,\n listIntermolNodes,\n listIntermolPairs,\n listRriLoopBoundaryPairs,\n listStructureLoopBoundaryPairs,\n sequenceColoring,\n\n // DOM modifications (advanced use)\n addElement,\n addStyleToNodes,\n applyLinearHelixSprings,\n applyPointMutations,\n applyRegionHighlights,\n applySubsequenceHighlights,\n backgroundhighlightBasepairs,\n backgroundhighlightRegion,\n changeBackgroundColor,\n closePolygonPoints,\n getPositionOfNode,\n highlightBasepairs,\n highlightRegion,\n highlightSubsequence,\n polyline,\n removeDummyNodes,\n removeSecondLink,\n setAttributeForElements,\n setIndexLabels,\n setLabelsId,\n setLinksId,\n styleBasepairs,\n updateLinkTooltips,\n updateNodeToolTips,\n visualiseAccessibility,\n\n // Export\n buildSVGString,\n downloadPNG,\n downloadSVG,\n };\n\n // Export\n global.vaRRI = vaRRI;\n if (typeof module !== 'undefined' && module.exports) {\n module.exports = vaRRI;\n }\n\n}(typeof window !== 'undefined' ? window : this));\n"],
5
+ "mappings": "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",
6
+ "names": ["global", "LINEAR_RRI_LINK_TYPE", "LINEAR_STRUCTURE_LINK_TYPE", "_animFrameId", "_renderTimeoutId", "_pendingRenderResolve", "_activeContainer", "COLORS", "SUBSEQUENCE_REGISTRY", "REGION_REGISTRY", "MUTATION_REGISTRY", "SUBSEQUENCE_HIGHLIGHTS", "REGION_HIGHLIGHTS", "POINT_MUTATIONS", "clearRegistry", "registry", "getRegistryItem", "id", "item", "candidate", "listRegistryItems", "cloneItem", "registerRegistryItem", "removeRegistryItem", "index", "cloneSubsequenceHighlight", "highlight", "start", "end", "normaliseHighlightSequence", "sequence", "seq", "normaliseHighlightRanges", "rangeInput", "context", "range", "parseSubsequences", "pair", "idx", "createSubsequenceHighlight", "input", "sequenceContext", "normalizedRange", "color", "alpha", "registerSubsequenceHighlight", "updateSubsequenceHighlight", "patch", "target", "normalized", "removeSubsequenceHighlight", "clearSubsequenceHighlights", "getSubsequenceHighlights", "cloneRegionHighlight", "normaliseRegionRange", "ranges", "createRegionHighlight", "context1", "context2", "seq1Range", "seq2Range", "registerRegionHighlight", "updateRegionHighlight", "removeRegionHighlight", "clearRegionHighlights", "getRegionHighlights", "clonePointMutation", "mutation", "normaliseMutationSequence", "buildSequencePositionMap", "map", "getSequenceIndices", "position", "normaliseMutationPosition", "positionInput", "validateOffset", "sequencePositionMap", "normaliseMutationReplacement", "replacement", "newLetter", "createPointMutation", "reference", "registerPointMutation", "updatePointMutation", "removePointMutation", "clearPointMutations", "getPointMutations", "getNodeIdForSequencePosition", "v", "nodeId", "seqName", "seqPosition", "getIndexDictionary", "setColors", "overrides", "getColors", "listIntermolNodes", "struc", "shift", "interBasepairs", "openBasepairs", "bracketPairs", "i", "char", "open", "close", "pairs", "a", "b", "splitAtAmpersand", "str", "checkStructureInputSimple", "structure", "basepairs", "closingBp", "bp", "count", "findBasePairs", "basepairList", "openIdx", "validateSequenceInput", "invalidChars", "validateCroppingInput", "cropping", "struc1", "struc2", "validateStructureInput", "seq1", "seq2", "offsetStr", "validateHighlighting", "highlighting", "validateBackgroundhighlighting", "bgHighlighting", "formatStructure", "first", "second", "fixedStructure", "bareStructure", "structureDict", "formatSequence", "fixedSequence", "bareSequence", "sequenceDict", "getMolecules", "validated", "seqId", "offset", "length", "indices", "zeroIdx", "n", "lastNum", "offset1", "offset2", "sequence1", "sequence2", "gapList", "dict", "num", "applyCropping", "rawSeq", "validStruc", "off", "unpairedLeading", "offOld", "trailing", "validate", "args", "rawStruc", "cropped", "seqFmt", "strucFmt", "h", "seenMutationPositions", "key", "startIndex", "sequenceLength", "sequenceId", "validIndices", "s", "r", "match", "setAttributeForElements", "targetAttr", "targetValue", "setAttr", "setValue", "el", "sequenceColoring", "changeBackgroundColor", "coloring", "node", "setLinksId", "line", "textContent", "parts", "ids", "x", "setLabelsId", "label", "updateNodeToolTips", "indexDict", "validateLabelPos", "pos", "indexing", "number", "neighbor", "highlightLabel", "targetIndex", "setLabelTooltip", "text", "parent", "existingTitleOnLabel", "title", "removeLabel", "removeLabelLink", "getIndexLabelValues", "structure1", "structure2", "labelInterval", "molecules", "sequence_dict", "length1", "lengthTotal", "indexLabels", "basepairRegion", "getIntermolBasepairRegion", "region", "posStr", "setIndexLabels", "mutationByNodeId", "labelValues", "value", "addStyleToNodes", "existingTitle", "updateLinkTooltips", "updatedIndices", "nodeIds", "style", "getPositionOfNode", "val", "getPlotInsertRoot", "plot", "child", "addElement", "elementType", "attr", "insertRoot", "getNodePointPairs", "points", "closePolygonPoints", "pointStrings", "y", "insertSvgShape", "tagName", "pointString", "extraAttrs", "shape", "name", "polyline", "polygon", "highlightRegion", "intermolNodes", "highlightBasepairs", "split", "type", "link", "nodes", "nodeNum", "removeSecondLink", "removeNode", "removeArrow", "removeLink", "startId", "endId", "targetIds", "removeDummyNodes", "highlightSubsequence", "keyOffset", "web", "mol", "webId", "distance1", "distance2", "startNode", "endNode", "clearGeneratedRegionHighlights", "registerGeneratedRegionHighlight", "spec", "getBackgroundRangeForPositions", "positions", "values", "entry", "computeBackgroundRegionRanges", "sequence1Range", "sequence2Range", "getRegionHighlightNodePath", "applyRegionHighlights", "registryHighlights", "nodePath", "applySubsequenceHighlights", "applyPointMutations", "styleBasepairs", "gapDots", "combined", "seqDict", "l1", "l2", "listBasepairs", "brackets", "indexStr", "listIntermolPairs", "intermol", "bracket", "normaliseBasepairList", "seen", "left", "right", "listDirectNestedPairChildren", "outer", "outerIndex", "children", "largestEarlierClose", "innerIndex", "inner", "pairEquals", "pairsCross", "createLoopBoundary", "extra", "firstGap", "secondGap", "listRriLoopBoundaryPairs", "boundaries", "firstInside", "secondInside", "listIntramolPairsBySequence", "sequence1End", "sequence2Start", "sequence2End", "grouped", "listStructureLoopBoundaryPairs", "loopBoundariesToConstraintSpecs", "kind", "constraints", "boundary", "loopId", "common", "getLinearRriConstraintSpecs", "getLinearStructureConstraintSpecs", "getGraphNucleotideByNumber", "graph", "nodeNumber", "getNodeDistance", "coordinates", "firstX", "firstY", "secondX", "secondY", "pairKey", "listRriHelixPairGroups", "listStructureHelixPairGroups", "groups", "crossingPairKeys", "other", "nextPair", "hasIncoming", "root", "path", "visited", "containsLoop", "collectLinearHelixSpanConstraints", "container", "specs", "linkType", "rawMultiplier", "multiplier", "byLoop", "loopSpecs", "resolved", "distances", "distance", "loopSpan", "source", "createLinearHelixRailTemplate", "group", "pairNodes", "column", "offsets", "intervals", "previous", "current", "isLoop", "measured", "span", "meanOffset", "sum", "along", "template", "ordinaryFit", "fitLinearHelixRailTemplate", "reflectedFit", "xField", "yField", "reflection", "live", "point", "center", "dot", "cross", "templateY", "liveX", "liveY", "angle", "cosine", "sine", "error", "targets", "deltaX", "deltaY", "projectLinearHelixRailTemplate", "currentFit", "previousFit", "normaliseRotationRadians", "radians", "rotateCoordinateCloud", "offsetX", "offsetY", "rotateTemplateFitState", "centerField", "angleField", "pivot", "orientLinearRriInteractionHorizontally", "rriTemplate", "templates", "targetAngle", "currentRotation", "previousRotation", "currentPivot", "previousPivot", "listVisibleIndexLabelPositions", "resolveGraphLinkNode", "endpoint", "collectLinearHelixIndexLabelBiases", "visiblePositions", "partnerByNode", "seenLabels", "biases", "anchor", "partner", "rawValue", "linkDistance", "nudgeLinearHelixIndexLabels", "moved", "bias", "outwardX", "outwardY", "outwardLength", "unitX", "unitY", "side", "createLinearHelixDomCache", "labelBiases", "syncWholeGraph", "projectedNodes", "graphLinks", "incidentLinks", "element", "syncFornacDirectionArrow", "arrow", "directionX", "directionY", "normalX", "normalY", "tipX", "tipY", "size", "width", "syncLinearHelixDom", "cache", "clearLinearHelixConstraintState", "applyLinearHelixSprings", "options", "constrainedNodes", "activeConstraints", "constraint", "domCache", "enforceAndSync", "hasRefittedAtRest", "enforceSyncAndRefit", "backgroundhighlightBasepairs", "intermolPairs", "stack", "highlightAreas", "stackOpen", "stackClose", "area", "backgroundhighlightRegion", "addAccessibilityOverlay", "tooltip", "overlay", "mapProbabilityToOpacity", "prb", "representsOne", "visualiseAccessibility", "accessData", "lenSeq", "accessColors", "accessColorMode", "seq1Color", "seq2Color", "seq1RepresentsOne", "seq2RepresentsOne", "isSeq1", "prbTooltip", "resolveGraphNodeFromEndpoint", "byNumber", "getFreeableLoopScaffoldUids", "closureNodes", "closureUids", "closureIndices", "hubs", "hubUids", "memberUids", "hub", "members", "touchesClosure", "touchesExternalElemType", "member", "relaxForceGraphScaffold", "scaffold", "removableNodeUids", "uid", "sourceNode", "targetNode", "sourceUid", "targetUid", "applyPseudoknotLinkStrength", "enabled", "cancelActiveRender", "resolvePendingRender", "render", "containerId", "forceLayout", "forceLayoutLinearRRI", "forceLayoutLinearStructure", "freeTrailingEnds", "pullPseudoknotBasepairs", "applyModifications", "highlightSyncLoop", "resolve", "reject", "err", "normaliseRotationDegrees", "degrees", "getRotationHost", "svgEl", "ensureRotationLayer", "hostEl", "layer", "getBBoxCenter", "bbox", "rotateVisualization", "amount", "mode", "textEl", "baseTransform", "textCenter", "transformParts", "SVG_STYLE_PROPS", "inlineComputedStyles", "originalEl", "cloneEl", "tag", "computed", "inlined", "prop", "origKids", "cloneKids", "buildSVGString", "clone", "w", "bg", "downloadSVG", "filename", "svgStr", "blob", "triggerDownload", "downloadPNG", "scale", "url", "rasterise", "imgEl", "canvasW", "canvasH", "canvas", "ctx", "img", "dataUrl", "dataUri", "imgFallback", "href", "vaRRI"]
7
+ }
@@ -0,0 +1,10 @@
1
+
2
+ ## `fornac` - A JavaScript library for visualizing RNA secondary structures in the browser.
3
+
4
+ - version v1.0.1 - download from [GitHub](https://github.com/ViennaRNA/fornac)
5
+ - license: Apache License v2.0 (see [LICENSE](fornac.LICENSE.txt) file)
6
+
7
+ ## `d3` - A JavaScript library for manipulating documents based on data.
8
+
9
+ - version 3.4.13 - download from [GitHub](https://github.com/d3/d3)
10
+ - license: ISC License (see [LICENSE](d3.LICENSE.txt) file)